diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..4b279e97 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +# GalaxyBenchmarker generated files +logs/ +results/ + +# Python stuff +venv/ +.venv/ \ No newline at end of file diff --git a/.gitignore b/.gitignore index f31affe7..ab55a5b4 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,8 @@ tools.yml *.html results/* tool_test_output.* +.vscode +evaluation/*.png ### Autogenerated: ### # Byte-compiled / optimized / DLL files diff --git a/Dockerfile b/Dockerfile index cc544d3e..d2d48373 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,28 @@ -FROM python:3.7 +FROM python:3.10 -COPY requirements.txt ./ -RUN pip install --no-cache-dir -r requirements.txt -RUN pip uninstall bioblend -y -RUN git clone https://github.com/galaxyproject/bioblend.git && cd bioblend && python setup.py install +ENV PIP_NO_CACHE_DIR=1 \ + PIP_DISABLE_PIP_VERSION_CHECK=1 -RUN cd / && git clone https://github.com/usegalaxy-eu/workflow-testing.git \ No newline at end of file +RUN \ + apt-get update \ + && apt-get install -y \ + rsync \ + tini \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* + +RUN pip install poetry + +WORKDIR /src +COPY poetry.lock pyproject.toml /src/ + +RUN poetry config virtualenvs.create false \ + && poetry install --no-dev --no-interaction --no-ansi + +RUN git clone https://github.com/usegalaxy-eu/workflow-testing.git /workflow-testing + +COPY . /src/ + +COPY scripts/entrypoint.sh /entrypoint.sh + +ENTRYPOINT ["/usr/bin/tini", "-v", "--", "/entrypoint.sh"] diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..9734302d --- /dev/null +++ b/Makefile @@ -0,0 +1,36 @@ +run: + docker-compose up -d + docker-compose logs -f benchmarker + +run_sudo: + sudo -E docker-compose up -d + sudo docker-compose logs -f benchmarker + +.PHONY: run_cron +run_cron: + /usr/local/bin/docker-compose up -d + +stop: + echo "Gracefull shutdown. Benchmarker has 30 seconds to save results" + docker-compose stop -t 30 benchmarker + +stop_sudo: + echo "Gracefull shutdown. Benchmarker has 30 seconds to save results" + sudo docker-compose stop -t 30 benchmarker + +build: + docker-compose build + +build_sudo: + sudo docker-compose build + +lint: lint_isort lint_black + +lint_isort: + poetry run isort galaxy_benchmarker + +lint_black: + poetry run black --target-version py310 galaxy_benchmarker + +lint_mypy: + poetry run mypy galaxy_benchmarker diff --git a/README.md b/README.md index fcf80bdf..6cac15b5 100644 --- a/README.md +++ b/README.md @@ -1,332 +1,103 @@ # Galaxy Benchmarker -A tool for benchmarking Galaxy job-destinations. -The goal is to easily benchmark different job-destinations. All you have to do is to configure the benchmarker itself, -define the destinations you want to benchmark, the workflows you want to run and the benchmarks you want to use. GalaxyBenchmarker -should handle the rest, like configuring Galaxy (to send the jobs to the right destination), submitting workflows and -collecting the metrics. +A tool for benchmarking different storage types individually or in more complex setups like Galaxy in combination with iRODS. -GalaxyBenchmarker is designed to be easily extendable in terms of destinations-types and benchmark-scenarios. +Link to the [original version](https://github.com/usegalaxy-eu/GalaxyBenchmarker/tree/668b9a5125541f686d00950a0e862a260ca4b787) of the GalaxyBenchmarker. -### Benchmark-Scenarios -Currently there are three types/scenarios of benchmarks available. Every benchmark allows to define its destination and workflows to test, how often workflows should -be run per destination and if some ansible-playbooks should be run before or after the benchmark ran. -#### Cold vs Warm -What is the difference between running a workflow for the first time or for it having been run multiple -times already? What is the overhead for staging time, installing tools, etc? +Link to the [results referenced](https://doi.org/10.5281/zenodo.7051186) in the `evaluation` section. -This benchmark cleans up Pulsar for every cold run, using a Ansible-Playbook ([coldwarm_pretask.yml]). +## Basic architecture -#### Destination Comparison -What are the differences between multiple destinations in terms of staging time (sending data -to a remote location might take some time), runtime, etc. +You can run GalaxyBenchmarker locally on your maschine or inside a container. It +mainly uses [Ansible](https://docs.ansible.com/) and [Docker](https://www.docker.com/) +to configure the target hosts. This ensures that the target hosts will be in a clean +state after benchmarking. -#### Burst -How does a destination handle a big burst of requests? +### Project structure - -## Requirements -* A Galaxy-Instance -* InfluxDB for saving the benchmark results -* Some job-destinations to benchmark -* Python 3.7 -* Ansible -* Docker (if you want to use the docker-compose setup) - -## Usage -A docker-compose setup is already provided that ships with InfluxDB and Grafana for easy -analysis of the metrics. - -### Install all dependencies (not needed if using docker-compose setup) -Some additional packages are needed in order for GalaxyBenchmarker to function properly. -To install those, run the following command: -```shell -pip3 install -r requirements.txt ``` - -### Configure the GalaxyBenchmarker -To use the benchmarker, you first need to create a yaml-configuration file. -You can find a basic example in `benchmark_config.yml.usegalaxyeu`. As a start, -rename it to `benchmark_config.yml` and fill in the credentials for your regular -UseGalaxy.eu user and, additionally, the user key for which the jobs are routed -to your wanted destination. - -Next, we need to define the workflows that should be run. The benchmarker -uses the test functionality of [Planemo](https://github.com/galaxyproject/planemo) to -submit workflows. The docker-compose setup already clones the examples from -https://github.com/usegalaxy-eu/workflow-testing. These are available at the -the path `/workflow-testing`. - -Workflows are defined in the configuration as following: -```yaml -workflows: - - name: ARDWorkflow - type: Galaxy - path: /workflow-testing/sklearn/ard/ard.ga -``` - -The benchmarker can submit jobs to multiple job destinations to compare the performance of each. For routing -the jobs to the right destination, users can be defined for each. The GalaxyBenchmarker can take -care of that -(see the [configuration examples](https://github.com/AndreasSko/Galaxy-Benchmarker/blob/master/benchmark_config.yml.example)) -if you are an administrator for the Galaxy instance. Otherwise, it is also possible to use different -users for each destination and link them in the configuration of the benchmarker: -```yaml -destinations: - - name: Destination1 - type: Galaxy - galaxy_user_key: USERKEY -``` - -Now, we just need to define the actual benchmark that we want to perform. To compare different job destinations, -you can do the following: -```yaml -benchmarks: - - name: DestinationComparisonBenchmark - type: DestinationComparison - destinations: - - Destination1 - workflows: - - ARDWorkflow - runs_per_workflow: 1 +. +├── ansible -- Ansible playbooks and inventory +├── evaluation -- Example plot code in jupiter notebooks +├── examples -- Example configurations +├── galaxy_benchmarker -- Source code +├── logs -- Generated logs +├── results -- Generated results +... ``` -### Run the benchmark using docker-compose -Now you should be ready to run the benchmark. Simply run: -````shell -docker-compose up -```` -This will spin up a InfluxDB and Grafana container, together with a container -running the benchmarker. After the benchmarking has been finished, you can -view the results using Grafana at http://localhost:3000 -(username: admin, password: admin). - -:warning: +## Usage -The data of InfluxDB is stored inside the container. Before running -`docker-compose down` remember to back up your data! +You can use GalaxyBenchmarker with the prepackaged and preconfigured container +environment or as a local project. For the container env it automatically maps +SSH (config, agent, ...) inside the container. With this you can use your local +SSH config and keys within the inventory. -### Run the benchmarks (without docker-compose) -The GalaxyBenchmarker can use different configuration files. If none is given, it will look for `benchmark_config.yml` -```shell -python3 galaxy_benchmarker --config benchmark_config.yml -``` +[Example usage described here](#example-usage) -## Benchmark Types -### Destination Comparison -Used to compare the performance of different destinations. +### Run with container -#### Requirements -* 1-n Galaxy destinations -* 1-n Galaxy workflows -* `runs_per_workflow` >= 1: How many times should a workflow be run on every destination? -#### Optional settings -* `warmup`: if set to true, a "warmup run" will be performed for every workflow -on every destination, while its results won't be counted -* ``pre_task``/`post_task`: a task that will be run before or after the benchmark has been completed +```bash +# Build and prepare the image(s) +docker-compose build -### Cold vs Warm -Used to compare the performance of a cold run (workflows hasn't been run before) to a warm run. To -simulate a cold run, you can define pre tasks that will be run before every cold workflow run, to -clean up caches etc. +# Change 'command' in docker-compose.yml to point to your config file +nano docker-compose.yml -Note: This benchmark type can only use one destination! +# Option 1: Run GalaxyBenchmarker and display progress +docker-compose up -d +docker-compose logs -f benchmarker -#### Requirements -* 1 Galaxy destination -* 1-n Galaxy workflows -* `runs_per_workflow` >= 1: How many times should a workflow be run on every destination? -* `cold_pre_task`: a task task that will be run in the cold phase before every workflow run +# Option 2: Run with make commands +make run +## If you require sudo for docker-compose (-E flag is required!) +make run_sudo -#### Optional settings -* ``pre_task``/`post_task`: a task that will be run before or after the benchmark has been completed -* `warm_pre_task`: a task task that will be run in the warm phase before every workflow run +# Gracefull shutdown (stores all results before exiting) +make stop +make stop_sudo +``` -### Burst -Used to start a burst of workflows at the same time. +### Run locally -#### Requirements -* 1-n Galaxy or Condor destinations -* 1-n Galaxy or Condor workflows -* `runs_per_workflow` >= 1: How many times should a workflow be run on every destination? -* `burst_rate` > 0: How many workflows should be submitted per second? -(for example: 0.5 results in a workflow submit every two seconds) +This project requires [Poetry](https://python-poetry.org/docs/). -#### Optional settings -* `warmup`: if set to true, a "warmup run" will be performed for every workflow -on every destination, while its results won't be counted -* ``pre_task``/`post_task`: a task that will be run before or after the benchmark has been completed +```bash +# Setup project +poetry env use python3.10 +poetry install -## Destination Types -Currently, the benchmarks can be run on two main types of destinations, while the first -is the best supported. -### Galaxy Destination -#### Regular -All you need to define is the username and api key to be used. This type expects, that -routing to destinations is handled by Galaxy and is user-based (e.g. one user per -destination). -```yaml -name: DestinationName -type: Galaxy -galaxy_user_name: me@andreas-sk.de -galaxy_user_key: f4284f9728e430a8140435861b17a454 +# Run GalaxyBenchmarker +poetry run python -m galaxy_benchmarker [... optional args] ``` -#### PulsarMQ -This type is used, if you want GalaxyBenchmarker to configure Galaxy to use a Pulsar -destination. -````yaml -name: PulsarDestinationName -type: PulsarMQ -amqp_url: "pyamqp://username:password@rabbitmq.example.com:5672//" -tool_dependency_dir: /data/share/tools -jobs_directory_dir: /data/share/staging -persistence_dir: /data/share/persisted_data -# To configure additional params in job_conf.xml -job_plugin_params: - manager: __default__ -job_destination_params: - dependency_resolution: remote - other_parameter: abc -```` - -### Condor Destination -This destination type was defined in order to benchmark HTCondor directly. You need -to set the credentials to your Condor submit node. GalaxyBenchmarker directly -connects to the host via SSH and runs ``condor_submit``. -````yaml -name: CondorDestinationName -type: Condor -host: submit.htcondor.com -host_user: ssh-user -ssh_key: /local/path/to/ssh/key.cert -jobs_directory_dir: /data/share/condor -```` +## Inventory / Hosts / Targets -## Workflow Types -### Galaxy Workflow -The benchmarker uses the test functionality of [Planemo](https://github.com/galaxyproject/planemo) to -submit workflows. Examples can be found at: https://github.com/usegalaxy-eu/workflow-testing. For a start, you -can clone this repository and use those workflows for benchmarking. Workflows are defined in the -configuration as following: -```yaml -name: GalaxyWorkflowName -type: Galaxy -path: path/to/galaxy/workflow/file.ga -timeout: 100 -``` +The targets/hosts are defined in [ansible/inventory/](./ansible/inventory/) as +[Ansible inventory](https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html). +Here, you can also set host specific variables, if necessary. -### Condor Workflow -A Condor Workflow is defined by the path to the folder containing its -files and the actual job file. GalaxyBenchmarker will upload the folder to -the Condor submit node using an Ansible Playbook and will trigger a -``condor_submit`` to start the workflow. -````yaml -name: CondorWorkflowName -type: Condor -path: path/to/condor/workflow/folder -job_file: job.job -```` +The defined hosts can then be used throughout the various benchmarker configs. -## Task Types -### Ansible Playbook -An Ansible Playbook can be run on every destination defined in a benchmark. -Note: You will need to add `host`, `host_user` and `ssh_key` to the definition of -every destination. - -Define a task as follows: -```yaml -type: ansible-playbook -playbook: /path/to/playbook.yml -``` +## Example usage -### Benchmarker Task -These are tasks defined in ``task.py``. Currently, there exist the following tasks: -* `delete_old_histories`: This will delete all histories of a user on Galaxy -* ``reboot_openstack_servers``: This will reboot all OpenStack instance which name correspond to -``name_contains`` -* ``reboot_random_openstack_server``: This will reboot a randomly chosen OpenStack instance -which name correspond to ``name_contains`` -* ``rebuild_random_openstack_server``: This will rebuild a randomly chosen OpenStack instance -which name correspond to ``name_contains`` +1. All examples use `irods_client` and `irods_server` as aliases for the hosts. These hosts have to be configured in [the inventory](./ansible/inventory/example.yml) +1. Run the GalaxyBenchmarker with `--cfg examples/01_verify_setup.yml` to verify the setup. -Define a task as follows: -````yaml -type: benchmarker-task -name: task-name -params: - param1: ab - param2: cd -```` -## Additional options -All possible options can be found in the [configuration examples](https://github.com/AndreasSko/Galaxy-Benchmarker/blob/master/benchmark_config.yml.example). +## Debugging -### InfluxDB -In normal cases, GalaxyBenchmarker will save the results in a json file under the `results` directory. However, job -metrics can also be submitted to InfluxDB for further analysis. -```yaml -influxdb: - host: influxdb.example.com - port: 8086 - username: glx_benchmarker_user - password: supersecret - db_name: glx_benchmarker -``` -Example Dashboards for Grafana can be found at [grafana_dashboards](https://github.com/AndreasSko/Galaxy-Benchmarker/tree/master/grafana_dashboards) +1. Change the configuration for more detailed logging + ``` + log_ansible_output: true + results_save_raw_results: true + ``` -### OpenStack -There exist some tasks that need access to OpenStack to work properly. For this, -you can define your credentials: -```yaml -openstack: - auth_url: https://auth.url.com:5000/v3/ - compute_endpoint_version: 2.1 - username: username - password: password - project_id: id - region_name: region - user_domain_name: Default -``` +1. Run GalaxyBenchmarker with the flags `--only-pre-task`, `--only-benchmark`, and `--only-post-task` to check the individual stages -### Let GalaxyBenchmarker handle the configuration -The GalaxyBenchmarker can configure Galaxy to use different job destinations and to install -tool dependencies. For that, you need have an admin user and SSH access to the instance. -```yaml -galaxy: - ... - # Install tool dependencies - shed_install: true - # Should Galaxy be configured to use the given Destinations or is everything already set? - configure_job_destinations: true - ssh_user: ubuntu - ssh_key: /local/path/to/ssh/key.cert - galaxy_root_path: /srv/galaxy - galaxy_config_dir: /srv/galaxy/server/config - galaxy_user: galaxy -``` -The settings for a new destination can then be defined as follows: -```yaml -destinations: - - name: PulsarDestination - type: PulsarMQ - amqp_url: "pyamqp://username:password@rabbitmq.example.com:5672//" - # If used for ColdWarmBenchmark, we need to have ssh-access to the Pulsar-Server - host: pulsar.example.com - host_user: centos - ssh_key: /local/path/to/ssh/key.cert - tool_dependency_dir: /data/share/tools - jobs_directory_dir: /data/share/staging - persistence_dir: /data/share/persisted_data - # To configure additional params in job_conf.xml - job_plugin_params: - manager: __default__ - job_destination_params: - dependency_resolution: remote - default_file_action: remote_transfer - remote_metadata: false - rewrite_parameters: true - amqp_acknowledge: true - amqp_ack_republish_time: 10 -``` \ No newline at end of file +1. Have a look into the logs of the container: + ``` + irods-fuse -> /tmp + irods -> /var/lib/irods/log/ + ``` diff --git a/ansible/cleanup_galaxy_server.yml b/ansible/cleanup_galaxy_server.yml new file mode 100644 index 00000000..d33c723b --- /dev/null +++ b/ansible/cleanup_galaxy_server.yml @@ -0,0 +1,38 @@ +- hosts: "{{ host }}" + become: true + tasks: + - import_tasks: tasks/remove_container.yml + vars: + path: "/galaxy" + + - name: Delete galaxy files + when: galaxy_host_volume is defined + become: yes + community.docker.docker_container: + name: fix_filepermissions + image: ubuntu + user: 1450 + detach: false + cleanup: true + volumes: + - "{{ galaxy_host_volume }}/galaxy_in_progress:/mnt/volume_under_test" + command: [ + "rm", + "-rf", + "/mnt/volume_under_test/files", + ] + + - name: Cleanup Host Volume + when: galaxy_host_volume is defined + file: + path: "{{ galaxy_host_volume }}/galaxy_in_progress" + state: absent + retries: 15 + delay: 5 + register: cleanup + until: not cleanup["failed"] + + - name: Cleanup Export Volume + file: + path: "{{ galaxy_export_volume }}/glx_export" + state: absent diff --git a/ansible/cleanup_irods_fuse_client.yml b/ansible/cleanup_irods_fuse_client.yml new file mode 100644 index 00000000..0aa2555b --- /dev/null +++ b/ansible/cleanup_irods_fuse_client.yml @@ -0,0 +1,15 @@ +- hosts: "{{ host }}" + become: true + tasks: + - import_tasks: tasks/remove_container.yml + vars: + path: "/irods" + + - name: Unmount fuse mount + ansible.posix.mount: + path: "{{ irods_host_volume }}/irods-fuse" + state: unmounted + - name: Remove irods volume + file: + path: "{{ irods_host_volume }}/irods-fuse" + state: absent diff --git a/ansible/cleanup_irods_server.yml b/ansible/cleanup_irods_server.yml new file mode 100644 index 00000000..f637d3c5 --- /dev/null +++ b/ansible/cleanup_irods_server.yml @@ -0,0 +1,17 @@ +- hosts: "{{ host }}" + become: true + tasks: + - import_tasks: tasks/remove_container.yml + when: (irods_use_davrods is defined) and (irods_use_davrods|bool) + vars: + path: "/davrods" + + - import_tasks: tasks/remove_container.yml + vars: + path: "/irods" + + - name: Remove irods volume + when: (irods_host_volume is defined) + file: + path: "{{ irods_host_volume }}/irods" + state: absent diff --git a/ansible/cleanup_irods_webdav_client.yml b/ansible/cleanup_irods_webdav_client.yml new file mode 100644 index 00000000..107b3ed5 --- /dev/null +++ b/ansible/cleanup_irods_webdav_client.yml @@ -0,0 +1,15 @@ +- hosts: "{{ host }}" + become: true + tasks: + - import_tasks: tasks/remove_container.yml + vars: + path: "/irods" + + - name: Unmount webdav mount + ansible.posix.mount: + path: "{{ irods_host_volume }}/irods-webdav" + state: unmounted + - name: Remove irods volume + file: + path: "{{ irods_host_volume }}/irods-webdav" + state: absent diff --git a/ansible/connection_test.yml b/ansible/connection_test.yml new file mode 100644 index 00000000..ad587563 --- /dev/null +++ b/ansible/connection_test.yml @@ -0,0 +1,4 @@ +- hosts: "{{ host }}" + tasks: + - name: "Get the hostname to see if host is reachable" + shell: hostname diff --git a/ansible/files/galaxy-server/.gitignore b/ansible/files/galaxy-server/.gitignore new file mode 100644 index 00000000..1f5d6409 --- /dev/null +++ b/ansible/files/galaxy-server/.gitignore @@ -0,0 +1 @@ +export \ No newline at end of file diff --git a/ansible/files/galaxy-server/docker-compose.yml b/ansible/files/galaxy-server/docker-compose.yml new file mode 100644 index 00000000..37513219 --- /dev/null +++ b/ansible/files/galaxy-server/docker-compose.yml @@ -0,0 +1,22 @@ +version: "3" +services: + galaxy: + build: galaxy + hostname: galaxy + domainname: local + ports: + # Webserver + - "8080:80" + # supvisord web app + - "9002:9002" + volumes: + - ${GALAXY_EXPORT_VOLUME}:/export + - ./galaxy/custom_tools:/custom_tools + - ./galaxy/custom_config:/custom_config + # PLACEHOLDER_VOLUMES + environment: + - GALAXY_CONFIG_TOOL_CONFIG_FILE=/custom_tools/custom_tool_conf.xml + # PLACEHOLDER_ENV + + galaxy-job-starter: + build: job-starter diff --git a/ansible/files/galaxy-server/galaxy/Dockerfile b/ansible/files/galaxy-server/galaxy/Dockerfile new file mode 100644 index 00000000..12092840 --- /dev/null +++ b/ansible/files/galaxy-server/galaxy/Dockerfile @@ -0,0 +1,7 @@ +FROM bgruening/galaxy-stable + +# Install additional requirements +COPY requirements.txt /requirements.txt +RUN . $GALAXY_VIRTUAL_ENV/bin/activate \ + && pip install -r /requirements.txt \ + && deactivate diff --git a/ansible/files/galaxy-server/galaxy/custom_config/object_store_conf.irods.xml.j2 b/ansible/files/galaxy-server/galaxy/custom_config/object_store_conf.irods.xml.j2 new file mode 100644 index 00000000..58af82ad --- /dev/null +++ b/ansible/files/galaxy-server/galaxy/custom_config/object_store_conf.irods.xml.j2 @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/ansible/files/galaxy-server/galaxy/custom_config/object_store_conf.local_mount.xml b/ansible/files/galaxy-server/galaxy/custom_config/object_store_conf.local_mount.xml new file mode 100644 index 00000000..b5137a9f --- /dev/null +++ b/ansible/files/galaxy-server/galaxy/custom_config/object_store_conf.local_mount.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/ansible/files/galaxy-server/galaxy/custom_config/object_store_conf.s3.xml.j2 b/ansible/files/galaxy-server/galaxy/custom_config/object_store_conf.s3.xml.j2 new file mode 100644 index 00000000..fb9f8efb --- /dev/null +++ b/ansible/files/galaxy-server/galaxy/custom_config/object_store_conf.s3.xml.j2 @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/ansible/files/galaxy-server/galaxy/custom_tools/custom_tool_conf.xml b/ansible/files/galaxy-server/galaxy/custom_tools/custom_tool_conf.xml new file mode 100644 index 00000000..e8f56ebb --- /dev/null +++ b/ansible/files/galaxy-server/galaxy/custom_tools/custom_tool_conf.xml @@ -0,0 +1,6 @@ + + +
+ +
+
diff --git a/ansible/files/galaxy-server/galaxy/custom_tools/file_gen.py b/ansible/files/galaxy-server/galaxy/custom_tools/file_gen.py new file mode 100644 index 00000000..1050c918 --- /dev/null +++ b/ansible/files/galaxy-server/galaxy/custom_tools/file_gen.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python +# Generate files with random data + +import sys +import io +import os +from pathlib import Path +import random + +BUFFER_BLOCK_SIZE = 1024*1024 # 1MiB +BUFFER_NUM_BLOCKS = 1024 # Total size: 1 GiB + +def fill_file_from_buffer(file: Path, buffer: io.BytesIO, file_size: int): + """Fill file with random blocks from buffer until file_size is reached""" + + num_blocks, rest = divmod(file_size, BUFFER_BLOCK_SIZE) + with file.open("wb") as out: + # Copy num_blocks random blocks + for _ in range(num_blocks): + rand_block = random.randint(0,BUFFER_NUM_BLOCKS-1) + buffer.seek(rand_block) + out.write(buffer.read(BUFFER_BLOCK_SIZE)) + + # Write the last block + rand_block = random.randint(0,BUFFER_NUM_BLOCKS-1) + buffer.seek(rand_block) + out.write(buffer.read(rest)) + +def main(): + + num_files = int(sys.argv[1]) + file_size_in_bytes = int(sys.argv[2]) + output_dir = Path(sys.argv[3]) + + output_dir.mkdir(parents=True, exist_ok=True) + + with io.BytesIO() as buffer: + if file_size_in_bytes > 0: + # Create a buffer with random data of size + # BUFFER_BLOCK_SIZE*BUFFER_NUM_BLOCKS + for _ in range (BUFFER_NUM_BLOCKS): + buffer.write(os.urandom(BUFFER_BLOCK_SIZE)) + buffer.seek(0) + + for i in range(num_files): + file = output_dir / f"data_{i}.data" + fill_file_from_buffer(file, buffer, file_size_in_bytes) + + +if __name__ == "__main__": + main() diff --git a/ansible/files/galaxy-server/galaxy/custom_tools/file_gen.xml b/ansible/files/galaxy-server/galaxy/custom_tools/file_gen.xml new file mode 100644 index 00000000..ef37a827 --- /dev/null +++ b/ansible/files/galaxy-server/galaxy/custom_tools/file_gen.xml @@ -0,0 +1,20 @@ + + Create random files + +python '$__tool_directory__/file_gen.py' '${num_files}' '${file_size_in_bytes}' './output' + + + + + + + + + + + + + +Create **num_files** files with **file_size_in_bytes** bytes. + + diff --git a/ansible/files/galaxy-server/galaxy/requirements.txt b/ansible/files/galaxy-server/galaxy/requirements.txt new file mode 100644 index 00000000..927fd105 --- /dev/null +++ b/ansible/files/galaxy-server/galaxy/requirements.txt @@ -0,0 +1,2 @@ +git+https://github.com/galaxyproject/bioblend.git@main#egg=bioblend +python-irodsclient>1,<2 \ No newline at end of file diff --git a/ansible/files/galaxy-server/job-starter/Dockerfile b/ansible/files/galaxy-server/job-starter/Dockerfile new file mode 100644 index 00000000..ad4c2a77 --- /dev/null +++ b/ansible/files/galaxy-server/job-starter/Dockerfile @@ -0,0 +1,19 @@ +FROM python:3.10-slim + +# Runtime for galaxy scripts + +RUN \ + apt-get update \ + && DEBIAN_FRONTEND=noninteractive apt-get install -y \ + git \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* + +WORKDIR /scripts +COPY scripts/requirements.txt /scripts + +RUN python -m pip install -r requirements.txt + +COPY scripts/ /scripts/ + +ENTRYPOINT ["/scripts/entrypoint.sh"] diff --git a/ansible/files/galaxy-server/job-starter/scripts/entrypoint.sh b/ansible/files/galaxy-server/job-starter/scripts/entrypoint.sh new file mode 100755 index 00000000..ee9e034c --- /dev/null +++ b/ansible/files/galaxy-server/job-starter/scripts/entrypoint.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +sleep infinity diff --git a/ansible/files/galaxy-server/job-starter/scripts/requirements.txt b/ansible/files/galaxy-server/job-starter/scripts/requirements.txt new file mode 100644 index 00000000..61fabcae --- /dev/null +++ b/ansible/files/galaxy-server/job-starter/scripts/requirements.txt @@ -0,0 +1 @@ +git+https://github.com/galaxyproject/bioblend.git@main#egg=bioblend diff --git a/ansible/files/galaxy-server/job-starter/scripts/run_job.py b/ansible/files/galaxy-server/job-starter/scripts/run_job.py new file mode 100644 index 00000000..5e263287 --- /dev/null +++ b/ansible/files/galaxy-server/job-starter/scripts/run_job.py @@ -0,0 +1,46 @@ +import os +import json +import logging +import shlex +from bioblend.galaxy import GalaxyInstance + +log = logging.getLogger(__name__) +logging.basicConfig(level=logging.INFO) + +GLX_HISTORY_NAME = os.getenv("GLX_HISTORY_NAME", "galaxy_benchmarker") +GLX_TOOL_ID = os.getenv("GLX_TOOL_ID", "") +GLX_TOOL_INPUT = os.getenv("GLX_TOOL_INPUT", "") + +def main(): + """Connect to the local galaxy and trigger a job""" + if not GLX_TOOL_ID: + raise ValueError("Env var 'GLX_TOOL_ID' is required. Expected: Tool id which should be executed") + if not GLX_TOOL_INPUT: + raise ValueError("Env var 'GLX_TOOL_INPUT' is required. Expected: String encoded json input") + tool_input = json.loads(shlex.split(GLX_TOOL_INPUT)[0]) + + glx = GalaxyInstance(url="http://galaxy", key="fakekey") + + history_id = get_history_id(glx, GLX_HISTORY_NAME) + + log.info("Initiate tool run") + result = glx.tools.run_tool( + tool_id=GLX_TOOL_ID, + tool_inputs=tool_input, + history_id=history_id + ) + log.info(result) + +def get_history_id(glx: GalaxyInstance, history_name: str) -> str: + """Get history id by history name""" + histories = glx.histories.get_histories() + for history in histories: + if history["name"] == history_name: + return history["id"] + + result = glx.histories.create_history(history_name) + return result["id"] + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/ansible/files/irods-davrods-server/Dockerfile b/ansible/files/irods-davrods-server/Dockerfile new file mode 100644 index 00000000..9ca9e8db --- /dev/null +++ b/ansible/files/irods-davrods-server/Dockerfile @@ -0,0 +1,28 @@ +FROM ubuntu:18.04 +RUN apt-get update \ + && apt-get install -y \ + apache2 \ + apache2-utils \ + gnupg \ + lsb-release \ + wget \ + && wget -qO - https://packages.irods.org/irods-signing-key.asc | apt-key add - \ + && echo "deb [arch=amd64] https://packages.irods.org/apt/ $(lsb_release -sc) main" | tee /etc/apt/sources.list.d/renci-irods.list \ + && apt-get update \ + && apt-get install -y \ + irods-runtime=4.2.10 \ + && wget -qO - https://github.com/UtrechtUniversity/davrods/releases/download/4.2.10_1.5.0/davrods-4.2.10-1.5.0.deb > /tmp/davrods.deb \ + && apt install /tmp/davrods.deb \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* + +RUN a2enmod dav \ + && a2enmod davrods \ + && a2ensite davrods-vhost + +ARG host_owner_uid +RUN adduser --disabled-password --gecos "" --uid "${host_owner_uid}" irods + +EXPOSE 80 +ENTRYPOINT ["apache2ctl"] +CMD ["-DFOREGROUND"] diff --git a/ansible/files/irods-davrods-server/docker-compose.yml b/ansible/files/irods-davrods-server/docker-compose.yml new file mode 100644 index 00000000..2b82783a --- /dev/null +++ b/ansible/files/irods-davrods-server/docker-compose.yml @@ -0,0 +1,19 @@ +version: "3" +services: + davrods: + build: + context: . + args: + host_owner_uid: ${DAVRODS_VOLUME_OWNER} + + networks: + - irods_default + ports: + - 80:80 + volumes: + - ./volumes/davrods-vhost.conf:/etc/apache2/sites-available/davrods-vhost.conf + - ./volumes/irods_environment.json:/etc/apache2/irods/irods_environment.json + +networks: + irods_default: + external: true diff --git a/ansible/files/irods-davrods-server/volumes/davrods-vhost.conf b/ansible/files/irods-davrods-server/volumes/davrods-vhost.conf new file mode 100644 index 00000000..b70193a3 --- /dev/null +++ b/ansible/files/irods-davrods-server/volumes/davrods-vhost.conf @@ -0,0 +1,213 @@ +# -*- mode: apache -*- +# vim: ft=apache ts=4 sw=4 et +# +# davrods-vhost.conf +# +# Davrods is a mod_dav WebDAV provider. Configuration directives for +# Davrods should be placed in a block. +# +# Below we provide an example vhost configuration that enables Davrods +# using its default options. +# + + + # Enter your server name here. + ServerName localhost + ServerAlias SET_TO_HOST_IP + + # NB: Some webdav clients expect the server to implement webdav at the root + # location (they execute an OPTIONS request to verify existence of webdav + # protocol support). + + + + # Options needed to enable Davrods. {{{ + # ================================= + + # Disable built-in Apache directory listings - Davrods will + # provide this instead. + DirectoryIndex disabled + + # Restrict access to authenticated users. + AuthType Basic + Require valid-user + + # The realm name that will be shown to clients upon authentication + AuthName DAV + + # Use the 'irods' HTTP basic authentication provider, implemented by Davrods. + AuthBasicProvider irods + + # The DAV provider for this location. + # + # Davrods implements multiple dav providers, use either: + # - davrods-nolocks: WebDAV class 1 provider, no support for locking + # - davrods-locallock (recommended): WebDAV class 2 provider, uses a DBM lock database local to this webserver + # + # Note that the davrods-locallock provider requires an apache-writable lockdb directory + # (/var/lib/davrods, or a path specified using the DavrodsLockDB directive - see further down this file). + # The RPM/DEB distribution creates this directory for you. + # + Dav davrods-locallock + + # }}} + + # Davrods configuration directives. {{{ + # ================================= + + # Location of the iRODS environment file that specifies the iRODS + # client configuration used by Davrods. + # + # Note: When options in the iRODS environment file overlap with Davrods + # configuration directives, as with the host, port, and zone of the + # iRODS server, the values specified in the iRODS environment file are + # NOT used. + # + DavrodsEnvFile /etc/apache2/irods/irods_environment.json + + # The following options can be used to customize Davrods for your environment. + # These options and their default values are provided below. + # Having these directives commented out has the effect of enabling + # the listed default option. + + # Hostname and port of the iRODS server to connect to. + # + DavrodsServer irods 1247 + + # Data grid zone id of the iRODS server. + # + DavrodsZone zone_1 + + # Authentication type to use when connecting to iRODS. + # + # Supported authentication types are 'Native' and 'Pam'. + # ('Native' corresponds to what was formerly called 'Standard' auth in iRODS). + # + #DavrodsAuthScheme Native + + # Anonymous mode switch. + # + # (default: Off) + # When 'Off', basic authentication is required to log into + # Davrods. AuthType must be set to 'Basic' and AuthBasicProvider + # must be set to 'irods'. There must also be a 'Require valid-user' + # line. + # + # When 'On', Davrods will log into iRODS with a preset + # username and password (See options DavrodsAnonymousLogin and + # DavrodsAuthScheme). AuthType must be unset, or set to None, + # and there should be no 'Require valid-user' line + # (instead: Require all granted). + # + # This allows users to access Davrods without being prompted + # for a login, making public access and embedding in web pages + # easier. + #DavrodsAnonymousMode Off + + # iRODS authentication options for Davrods anonymous mode. + # + # This option is used only when DavrodsAnonymousMode is set to + # 'On'. + # + # Specifies the username and password to use for anonymous login. + # The default value is 'anonymous', with an empty password. + # (this user, if created, is treated specially within iRODS) + # + # The special 'anonymous' iRODS user normally requires the + # DavrodsAuthScheme to be set to Native. + # + #DavrodsAnonymousLogin "anonymous" "" + + # iRODS default resource to use for file uploads. + # + # Leave this empty to let the server decide. + # + #DavrodsDefaultResource "" + + # Exposed top collection of iRODS. + # + # Note that the collection chosen MUST be readable for all users, + # otherwise they will experience problems when mounting the drive. + # For example, if you set it to "Home", then as a rodsadmin user + # execute the icommand: ichmod read public /zone-name/home + # + # Davrods accepts the following values for exposed-root: + # - 'Zone' (collection /zone-name) + # - 'Home' (collection /zone-name/home) + # - 'User' (collection /zone-name/home/logged-in-username) + # - full-path (named collection, must be absolute path, starts with /) + # + DavrodsExposedRoot User + + # Size of the buffers used for file transfer to/from the iRODS server. + # + # The default values optimize performance for regular configurations. + # The Tx buffer is used for transfer to iRODS (PUT), while the Rx + # buffer is used for transfer from iRODS (GET). + # Buffer sizes lower than 1024K will lead to decreased file transfer performance. + # + # The buffer sizes are specified as a number of kibibytes ('1' means 1024 bytes). + # We use 4 MiB transfer buffers by default. + # + #DavrodsTxBufferKbs 4096 + #DavrodsRxBufferKbs 4096 + + # Optionally Davrods can support rollback for aborted uploads. In this scenario + # a temporary file is created during upload and upon succesful transfer this + # temporary file is renamed to the destination filename. + # NB: Please note that the use of temporary files may conflict with your iRODS + # data policies (e.g. a acPostProcForPut would act upon the temporary filename). + # Valid values for this option are 'On'/'Yes' and 'Off'/'No'. + # + #DavrodsTmpfileRollback Off + + # When using the davrods-locallock DAV provider (see the 'Dav' + # directive above), this option can be used to set the location of the + # lock database. + # + #DavrodsLockDB /var/lib/davrods/lockdb_locallock + + # Davrods provides read-only HTML directory listings for web browser access. + # The UI is basic and unstyled by default, but can be modified with three + # theming directives. + # + # Each of these directives points to a local HTML file that must be readable + # by the apache user. + # + # The default value for each of these is "", which disables the option. + # + # - DavrodsHtmlHead is inserted in the HEAD tag of the listing. + # - DavrodsHtmlHeader is inserted at the top of the listing's BODY tag. + # - DavrodsHtmlFooter is inserted at the bottom of the listing's BODY tag. + # + # Example HTML files are provided in /etc/apache2/irods. You should edit these + # before enabling them. + # + # To see an example, uncomment the following three lines: + # + #DavrodsHtmlHead "/etc/apache2/irods/head.html" + #DavrodsHtmlHeader "/etc/apache2/irods/header.html" + #DavrodsHtmlFooter "/etc/apache2/irods/footer.html" + + # Depending on file type, web browser clients will either display + # files directly or offer a download to the user. + # This behavior can be influenced with the 'Content-Disposition' header. + # + # By default (value 'Off'), no such header is sent by Davrods. + # When DavrodsForceDownload is 'On', Davrods will send + # 'Content-Disposition: attachment' for all data objects, signalling that + # web browsers should not display files inline, but offer a download + # instead. + # + #DavrodsForceDownload Off + + # }}} + + + + # To avoid cleartext password communication we strongly recommend to + # enable Davrods only over SSL. + # For HTTPS-only access, change the port at the start of the vhost block + # from 80 to 443 and add your SSL options below. + + diff --git a/ansible/files/irods-davrods-server/volumes/irods_environment.json b/ansible/files/irods-davrods-server/volumes/irods_environment.json new file mode 100644 index 00000000..258aa66d --- /dev/null +++ b/ansible/files/irods-davrods-server/volumes/irods_environment.json @@ -0,0 +1,9 @@ +{ + "irods_client_server_negotiation": "request_server_negotiation", + "irods_client_server_policy": "CS_NEG_REFUSE", + "irods_encryption_key_size": 32, + "irods_encryption_salt_size": 8, + "irods_encryption_num_hash_rounds": 16, + "irods_encryption_algorithm": "AES-256-CBC", + "irods_ssl_verify_server": "hostname" +} diff --git a/ansible/files/irods-fuse-client/Dockerfile b/ansible/files/irods-fuse-client/Dockerfile new file mode 100644 index 00000000..0c8f68a8 --- /dev/null +++ b/ansible/files/irods-fuse-client/Dockerfile @@ -0,0 +1,46 @@ +FROM ubuntu:20.04 + +## Install official release version + +# ENV IRODSFS_VERSION=v0.7.3 +# ENV IRODSFS_TAR=irodsfs_amd64_linux_${IRODSFS_VERSION}.tar + +# RUN \ +# apt-get update \ +# && apt-get install -y \ +# fuse \ +# wget \ +# && wget https://github.com/cyverse/irodsfs/releases/download/${IRODSFS_VERSION}/${IRODSFS_TAR} \ +# && mkdir /irodsfs \ +# && tar -xf ${IRODSFS_TAR} -C /irodsfs \ +# && rm ${IRODSFS_TAR} \ +# && apt-get clean \ +# && rm -rf /var/lib/apt/lists/* /tmp/* + +## Install from specific commit +ENV IRODSFS_COMMIT_SHA=f60d7e16a1ca4f1dcdddc725847ad3095bee92c2 + +RUN \ + apt-get update \ + && DEBIAN_FRONTEND=noninteractive apt-get install -y \ + fuse \ + wget \ + make \ + git \ + golang-go \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* \ + && mkdir /irodsfs.git \ + && git clone https://github.com/cyverse/irodsfs /irodsfs.git \ + && cd /irodsfs.git \ + && git checkout ${IRODSFS_COMMIT_SHA} \ + && make build \ + && mkdir /irodsfs \ + && mv /irodsfs.git/bin/* /irodsfs + +COPY scripts/ /scripts + +ARG host_owner_uid +RUN adduser --disabled-password --gecos "" --uid "${host_owner_uid}" irods + +ENTRYPOINT ["/scripts/entrypoint.sh"] diff --git a/ansible/files/irods-fuse-client/docker-compose.yml b/ansible/files/irods-fuse-client/docker-compose.yml new file mode 100644 index 00000000..b86e90f9 --- /dev/null +++ b/ansible/files/irods-fuse-client/docker-compose.yml @@ -0,0 +1,13 @@ +version: "3" +services: + irods-fuse: + build: + context: . + args: + host_owner_uid: ${IRODS_HOST_VOLUME_OWNER} + privileged: true + user: "${IRODS_HOST_VOLUME_OWNER}:${IRODS_HOST_VOLUME_OWNER}" + volumes: + # Volume under test + - "${IRODS_HOST_VOLUME}/irods-fuse:/mnt/volume_under_test:rshared" + - "./volumes/fuse.conf:/etc/fuse.conf" diff --git a/ansible/files/irods-fuse-client/scripts/config.yml b/ansible/files/irods-fuse-client/scripts/config.yml new file mode 100644 index 00000000..ca8a3516 --- /dev/null +++ b/ansible/files/irods-fuse-client/scripts/config.yml @@ -0,0 +1,14 @@ +host: # Set by playbook +port: 1247 +proxy_user: rods +client_user: rods +zone: zone_1 +password: adminpass + +path_mappings: + - irods_path: /zone_1/home/rods + mapping_path: / + resource_type: dir + + +allow_other: true \ No newline at end of file diff --git a/ansible/files/irods-fuse-client/scripts/entrypoint.sh b/ansible/files/irods-fuse-client/scripts/entrypoint.sh new file mode 100755 index 00000000..3d367d84 --- /dev/null +++ b/ansible/files/irods-fuse-client/scripts/entrypoint.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +echo Starting fuse-client... + +/irodsfs/irodsfs -config /scripts/config.yml /mnt/volume_under_test + +sleep infinity diff --git a/ansible/files/irods-fuse-client/volumes/fuse.conf b/ansible/files/irods-fuse-client/volumes/fuse.conf new file mode 100644 index 00000000..926d936f --- /dev/null +++ b/ansible/files/irods-fuse-client/volumes/fuse.conf @@ -0,0 +1,8 @@ +# /etc/fuse.conf - Configuration file for Filesystem in Userspace (FUSE) + +# Set the maximum number of FUSE mounts allowed to non-root users. +# The default is 1000. +#mount_max = 1000 + +# Allow non-root users to specify the allow_other or allow_root mount options. +user_allow_other diff --git a/ansible/files/irods-server/Dockerfile b/ansible/files/irods-server/Dockerfile new file mode 100644 index 00000000..691e1e03 --- /dev/null +++ b/ansible/files/irods-server/Dockerfile @@ -0,0 +1,29 @@ +FROM ubuntu:18.04 + +ENV IRODS_VERSION=4.2.10 + +RUN \ + apt-get update \ + && apt-get install -y \ + expect \ + gnupg \ + lsb-release \ + wget \ + && wget -qO - https://packages.irods.org/irods-signing-key.asc | apt-key add - \ + && echo "deb [arch=amd64] https://packages.irods.org/apt/ $(lsb_release -sc) main" | tee /etc/apt/sources.list.d/renci-irods.list \ + && apt-get update \ + && apt-get install -y \ + irods-runtime=${IRODS_VERSION}* \ + irods-icommands=${IRODS_VERSION}* \ + irods-server=${IRODS_VERSION}* \ + irods-database-plugin-postgres=${IRODS_VERSION}* \ + irods-resource-plugin-s3=${IRODS_VERSION}* \ + libcurl4-gnutls-dev \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* + +COPY scripts/ /scripts + +RUN adduser --disabled-password --gecos "" --uid 1000 irods + +ENTRYPOINT ["/scripts/entrypoint.sh"] \ No newline at end of file diff --git a/ansible/files/irods-server/docker-compose.yml b/ansible/files/irods-server/docker-compose.yml new file mode 100644 index 00000000..fab5a938 --- /dev/null +++ b/ansible/files/irods-server/docker-compose.yml @@ -0,0 +1,30 @@ +version: "3" +services: + irods: + build: . + hostname: irods + domainname: local + depends_on: + - db + ports: + # Zone Endpoint + - 1247:1247 + # Control plane + - 1248:1248 + # Server port range + - "20000-20199:20000-20199" + volumes: + # Irods client config + - ./volumes/config_client/:/root/.irods/ + # Irods server config + - ./volumes/config_server/:/etc/irods/ + # Volume under test + - ${IRODS_HOST_VOLUME}/irods:/mnt/mountedVolumeResc + db: + image: postgres:11-alpine + # volumes: + # - ./db:/var/lib/postgresql/data + environment: + POSTGRES_USER: irods + POSTGRES_PASSWORD: password + POSTGRES_DB: ICAT diff --git a/ansible/files/irods-server/scripts/entrypoint.sh b/ansible/files/irods-server/scripts/entrypoint.sh new file mode 100755 index 00000000..af835961 --- /dev/null +++ b/ansible/files/irods-server/scripts/entrypoint.sh @@ -0,0 +1,23 @@ +#!/bin/bash + + +if [ -f "/.irods_installed" ]; then + echo iRODS already installed. Skipping Installation +else + echo Installing iRODS... + + python /var/lib/irods/scripts/setup_irods.py --json_configuration_file /scripts/setup_config.json + + if [ $? -eq 0 ]; then + echo Installation done! + touch /.irods_installed + else + echo "setup_irods failed" + exit 1 + fi +fi + +echo Starting server as user 'irods'... +su -c "python /var/lib/irods/scripts/irods_control.py start" irods + +sleep infinity diff --git a/ansible/files/irods-server/scripts/setup_config.json b/ansible/files/irods-server/scripts/setup_config.json new file mode 100644 index 00000000..6e6c10d8 --- /dev/null +++ b/ansible/files/irods-server/scripts/setup_config.json @@ -0,0 +1,124 @@ +{ + "admin_password": "adminpass", + "host_system_information": { + "service_account_user_name": "irods", + "service_account_group_name": "irods" + }, + "server_config": { + "catalog_provider_hosts": [ + "irods.local" + ], + "catalog_service_role": "provider", + "default_hash_scheme": "SHA256", + "default_resource_name": "demoResc", + "federation": [], + "match_hash_policy": "compatible", + "negotiation_key": "uP0aes0pien1eeg8euch9eegah1Aighi", + "rule_engine_namespaces": [ + "" + ], + "schema_validation_base_uri": "file:///var/lib/irods/configuration_schemas", + "server_control_plane_encryption_algorithm": "AES-256-CBC", + "server_control_plane_encryption_num_hash_rounds": 16, + "server_control_plane_key": "phiSai4vee9yoojai4dae6Aep8EiTh9m", + "server_control_plane_port": 1248, + "server_control_plane_timeout_milliseconds": 10000, + "server_port_range_end": 20199, + "server_port_range_start": 20000, + "zone_auth_scheme": "native", + "zone_key": "foepeiFiecaothai0ie3Oow2PooreV5o", + "zone_name": "zone_1", + "zone_port": 1247, + "zone_user": "rods", + "advanced_settings": { + "default_log_rotation_in_days": 5, + "default_number_of_transfer_threads": 4, + "default_temporary_password_lifetime_in_seconds": 120, + "dns_cache": { + "eviction_age_in_seconds": 3600, + "shared_memory_size_in_bytes": 5000000 + }, + "hostname_cache": { + "eviction_age_in_seconds": 3600, + "shared_memory_size_in_bytes": 2500000 + }, + "maximum_number_of_concurrent_rule_engine_server_processes": 4, + "maximum_size_for_single_buffer_in_megabytes": 32, + "maximum_size_of_delay_queue_in_bytes": 0, + "maximum_temporary_password_lifetime_in_seconds": 1000, + "rule_engine_server_execution_time_in_seconds": 120, + "rule_engine_server_sleep_time_in_seconds": 30, + "transfer_buffer_size_for_parallel_transfer_in_megabytes": 4, + "transfer_chunk_size_for_parallel_transfer_in_megabytes": 40 + }, + "environment_variables": { + "IRODS_DATABASE_USER_PASSWORD_SALT": "eux9cooseiJee1IePheiSh6jeichae8j" + }, + "plugin_configuration": { + "authentication": {}, + "database": { + "postgres": { + "db_host": "db", + "db_name": "ICAT", + "db_odbc_driver": "PostgreSQL Unicode", + "db_password": "password", + "db_port": 5432, + "db_username": "irods" + } + }, + "network": {}, + "resource": {}, + "rule_engines": [ + { + "instance_name": "irods_rule_engine_plugin-irods_rule_language-instance", + "plugin_name": "irods_rule_engine_plugin-irods_rule_language", + "plugin_specific_configuration": { + "re_data_variable_mapping_set": [ + "core" + ], + "re_function_name_mapping_set": [ + "core" + ], + "re_rulebase_set": [ + "core" + ], + "regexes_for_supported_peps": [ + "ac[^ ]*", + "msi[^ ]*", + "[^ ]*pep_[^ ]*_(pre|post|except|finally)" + ] + }, + "shared_memory_instance": "irods_rule_language_rule_engine" + }, + { + "instance_name": "irods_rule_engine_plugin-cpp_default_policy-instance", + "plugin_name": "irods_rule_engine_plugin-cpp_default_policy", + "plugin_specific_configuration": {} + } + ] + } + }, + "hosts_config": { + "host_entries": [] + }, + "host_access_control_config": { + "access_entries": [] + }, + "service_account_environment": { + "irods_client_server_negotiation": "request_server_negotiation", + "irods_client_server_policy": "CS_NEG_REFUSE", + "irods_cwd": "/zone_1/home/rods", + "irods_default_hash_scheme": "SHA256", + "irods_default_resource": "rootResc", + "irods_encryption_algorithm": "AES-256-CBC", + "irods_encryption_key_size": 32, + "irods_encryption_num_hash_rounds": 16, + "irods_encryption_salt_size": 8, + "irods_home": "/zone_1/home/rods", + "irods_host": "irods.local", + "irods_match_hash_policy": "compatible", + "irods_port": 1247, + "irods_user_name": "rods", + "irods_zone_name": "zone_1" + } +} \ No newline at end of file diff --git a/ansible/files/irods-server/volumes/config_client/irods_environment.json b/ansible/files/irods-server/volumes/config_client/irods_environment.json new file mode 100644 index 00000000..e3234ea3 --- /dev/null +++ b/ansible/files/irods-server/volumes/config_client/irods_environment.json @@ -0,0 +1,6 @@ +{ + "irods_host": "localhost", + "irods_port": 1247, + "irods_user_name": "rods", + "irods_zone_name": "zone_1" +} diff --git a/ansible/files/irods-server/volumes/config_server/.gitignore b/ansible/files/irods-server/volumes/config_server/.gitignore new file mode 100644 index 00000000..e69de29b diff --git a/ansible/files/irods-webdav-client/Dockerfile b/ansible/files/irods-webdav-client/Dockerfile new file mode 100644 index 00000000..65a8f756 --- /dev/null +++ b/ansible/files/irods-webdav-client/Dockerfile @@ -0,0 +1,17 @@ +FROM ubuntu:20.04 + +RUN \ + apt-get update \ + && apt-get install -y \ + davfs2 \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* + +COPY scripts/ /scripts + +RUN echo "/mnt/volume_under_test rods adminpass" >> /etc/davfs2/secrets + +ARG host_owner_uid +RUN adduser --disabled-password --gecos "" --uid "${host_owner_uid}" irods + +ENTRYPOINT ["/scripts/entrypoint.sh"] diff --git a/ansible/files/irods-webdav-client/docker-compose.yml b/ansible/files/irods-webdav-client/docker-compose.yml new file mode 100644 index 00000000..65598b2b --- /dev/null +++ b/ansible/files/irods-webdav-client/docker-compose.yml @@ -0,0 +1,14 @@ +version: "3" +services: + irods-webdav: + build: + context: . + args: + host_owner_uid: ${IRODS_HOST_VOLUME_OWNER} + privileged: true + volumes: + # Configure mount for non-root users + - ./volumes/fstab:/etc/fstab + - ./volumes/davfs2.conf:/etc/davfs2/davfs2.conf + # Volume under test + - "${IRODS_HOST_VOLUME}/irods-webdav:/mnt/volume_under_test:rshared" diff --git a/ansible/files/irods-webdav-client/scripts/entrypoint.sh b/ansible/files/irods-webdav-client/scripts/entrypoint.sh new file mode 100755 index 00000000..3e7246e3 --- /dev/null +++ b/ansible/files/irods-webdav-client/scripts/entrypoint.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +echo Starting webdav-client... + +mount /mnt/volume_under_test + +sleep infinity diff --git a/ansible/files/irods-webdav-client/volumes/davfs2.conf b/ansible/files/irods-webdav-client/volumes/davfs2.conf new file mode 100644 index 00000000..87ae1627 --- /dev/null +++ b/ansible/files/irods-webdav-client/volumes/davfs2.conf @@ -0,0 +1,77 @@ +# davfs2 configuration file 2014-08-10 +# version 12 +# ------------------------------------ + +# Copyright (C) 2006, 2007, 2008, 2009, 2012, 2013, 2014 Werner Baumann + +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. + + +# Please read the davfs2.conf (5) man page for a description of the +# configuration options and syntax rules. + + +# Available options and default values +# ==================================== + +# General Options +# --------------- + +# dav_user davfs2 # system wide config file only +# dav_group davfs2 # system wide config file only +# kernel_fs fuse +# buf_size 16 # KiByte + +# WebDAV Related Options +# ---------------------- + +# use_proxy 1 # system wide config file only +# proxy # system wide config file only +# trust_ca_cert +# servercert # deprecated: use trust_ca_cert +# trust_server_cert +# clientcert +# secrets ~/.davfs2/secrets # user config file only +# ask_auth 1 +# use_locks 1 +# lock_owner +# lock_timeout 1800 # seconds +# lock_refresh 60 # seconds +# use_expect100 0 +# if_match_bug 0 +# drop_weak_etags 0 +# n_cookies 0 +# precheck 1 +# ignore_dav_header 0 +# use_compression 0 +# min_propset 0 +# follow_redirect 0 +# server_charset +# connect_timeout 10 # seconds +# read_timeout 30 # seconds +# retry 30 # seconds +# max_retry 300 # seconds +# add_header + +# Cache Related Options +# --------------------- + +# backup_dir lost+found +# cache_dir /var/cache/davfs2 # system wide cache +# ~/.davfs2/cache # per user cache +# cache_size 50 # MiByte +# table_size 1024 +# dir_refresh 60 # seconds +# file_refresh 1 # second +delay_upload 0 +# gui_optimize 0 +# minimize_mem 0 + +# Debugging Options +# ----------------- + +# debug # possible values: config, kernel, cache, http, xml, + # httpauth, locks, ssl, httpbody, secrets, most + diff --git a/ansible/files/irods-webdav-client/volumes/fstab.j2 b/ansible/files/irods-webdav-client/volumes/fstab.j2 new file mode 100644 index 00000000..994ddf8d --- /dev/null +++ b/ansible/files/irods-webdav-client/volumes/fstab.j2 @@ -0,0 +1 @@ +http://{{ irods_server_host }} /mnt/volume_under_test davfs rw,user,uid={{davrods_owner_uid}},gid={{davrods_owner_uid}},noauto,_netdev 0 0 diff --git a/ansible/files/tool-dd/Dockerfile b/ansible/files/tool-dd/Dockerfile new file mode 100644 index 00000000..2c045c36 --- /dev/null +++ b/ansible/files/tool-dd/Dockerfile @@ -0,0 +1,6 @@ +FROM ubuntu:22.04 + +COPY entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +ENTRYPOINT [ "/entrypoint.sh" ] diff --git a/ansible/files/tool-dd/entrypoint.sh b/ansible/files/tool-dd/entrypoint.sh new file mode 100755 index 00000000..aec59136 --- /dev/null +++ b/ansible/files/tool-dd/entrypoint.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +dd bs=$DD_BS if=$DD_IF of=$DD_OF conv=fsync count=$DD_COUNT & +dd bs=$DD_BS if=$DD_IF of=$DD_OF conv=fsync count=$DD_COUNT seek=$DD_SEEK_1 skip=$DD_SKIP_1 & +dd bs=$DD_BS if=$DD_IF of=$DD_OF conv=fsync count=$DD_COUNT seek=$DD_SEEK_2 skip=$DD_SKIP_2 & +dd bs=$DD_BS if=$DD_IF of=$DD_OF conv=fsync count=$DD_COUNT seek=$DD_SEEK_3 skip=$DD_SKIP_3 & +wait diff --git a/ansible/files/tool-fio/Dockerfile b/ansible/files/tool-fio/Dockerfile new file mode 100644 index 00000000..d100e1b9 --- /dev/null +++ b/ansible/files/tool-fio/Dockerfile @@ -0,0 +1,22 @@ +FROM ubuntu:22.04 + +ENV FIO_SRC_REF=fio-3.30 + +RUN \ + apt-get update \ + && apt-get install -y \ + build-essential \ + libaio1 \ + libaio-dev \ + git \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* \ + && mkdir /fio.git \ + && git clone https://github.com/axboe/fio /fio.git \ + && cd /fio.git \ + && git checkout ${FIO_SRC_REF} \ + && ./configure \ + && make \ + && make install + +ENTRYPOINT [ "fio" ] \ No newline at end of file diff --git a/ansible/files/tool-mdtest/Dockerfile b/ansible/files/tool-mdtest/Dockerfile new file mode 100644 index 00000000..1a1cfc2f --- /dev/null +++ b/ansible/files/tool-mdtest/Dockerfile @@ -0,0 +1,24 @@ +FROM ubuntu:20.04 + +ENV MDTEST_VERSION_TAG=3.3.0 + +RUN \ + apt-get update \ + && DEBIAN_FRONTEND=noninteractive apt-get install -y \ + git automake autoconf make gcc mpich \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* \ + && git clone https://github.com/hpc/ior /mdtest \ + && cd /mdtest \ + && git checkout ${MDTEST_VERSION_TAG} \ + && ./bootstrap \ + && mkdir build \ + && cd build \ + && ../configure \ + && make \ + && make install \ + && mkdir /mnt/volume_under_test + +WORKDIR /mnt/volume_under_test + +ENTRYPOINT [ "mdtest" ] \ No newline at end of file diff --git a/ansible/files/tool-s3-benchmark/Dockerfile b/ansible/files/tool-s3-benchmark/Dockerfile new file mode 100644 index 00000000..2e1b2086 --- /dev/null +++ b/ansible/files/tool-s3-benchmark/Dockerfile @@ -0,0 +1,14 @@ +FROM golang:1.15-alpine + +RUN apk add --update --no-cache \ + git \ + && git clone https://github.com/chinglinwen/s3-benchmark.git /s3-benchmark \ + && cd /s3-benchmark \ + && git checkout tags/v1.0-beta -b branch-v1.0-beta \ + && go get code.cloudfoundry.org/bytefmt \ + && go get github.com/aws/aws-sdk-go/aws \ + && go get github.com/jmespath/go-jmespath \ + && go build s3-benchmark.go + +WORKDIR /s3-benchmark +ENTRYPOINT [ "./s3-benchmark" ] \ No newline at end of file diff --git a/ansible/files/tool-warp/Dockerfile b/ansible/files/tool-warp/Dockerfile new file mode 100644 index 00000000..4db17788 --- /dev/null +++ b/ansible/files/tool-warp/Dockerfile @@ -0,0 +1,21 @@ +FROM golang:1.14.7-alpine + +WORKDIR /warp + +ENV CGO_ENABLED=0 + +RUN apk add --update --no-cache \ + git \ + && git clone https://github.com/minio/warp /warp \ + && cd /warp \ + && git checkout tags/v0.5.5 -b branch-v0.5.5 \ + && go mod download \ + && go build -ldflags '-w -s' -a -o warp . + + +FROM scratch + +COPY --from=0 /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ +COPY --from=0 /warp/warp /warp + +ENTRYPOINT ["/warp"] \ No newline at end of file diff --git a/ansible/install_docker.yml b/ansible/install_docker.yml new file mode 100644 index 00000000..e880f20b --- /dev/null +++ b/ansible/install_docker.yml @@ -0,0 +1,59 @@ +- hosts: "{{ host }}" + become: true + tasks: + + - name: Check if Docker is installed + command: docker --version + register: docker_valid + ignore_errors: yes + + # Install docker if not present + - name: Install docker dependencies + when: docker_valid.failed + apt: name={{ item }} state=latest update_cache=yes + loop: + - 'apt-transport-https' + - 'ca-certificates' + - 'curl' + - 'software-properties-common' + - 'python3-pip' + - 'virtualenv' + - 'python3-setuptools' + - name: Add Docker GPG key + when: docker_valid.failed + apt_key: + url: https://download.docker.com/linux/ubuntu/gpg + state: present + - name: Add Docker repository + when: docker_valid.failed + apt_repository: + repo: deb https://download.docker.com/linux/ubuntu bionic stable + state: present + - name: Install Docker + when: docker_valid.failed + apt: update_cache=yes name=docker-ce state=latest + + - name: Check if Docker-compose is installed + command: docker-compose --version + register: docker_compose_valid + ignore_errors: yes + + # Install docker-compose if not present + - name: Install docker-compose dependencies + when: docker_compose_valid.failed + apt: name={{ item }} state=latest update_cache=yes + loop: + - python3-pip + - python3-dev + - libffi-dev + - librust-openssl-dev + - gcc + - libc-dev + - rustc + - cargo + - make + - name: Install docker-compose + when: docker_compose_valid.failed + pip: + name: + - docker-compose diff --git a/ansible/inventory/.gitignore b/ansible/inventory/.gitignore new file mode 100644 index 00000000..8f4119ff --- /dev/null +++ b/ansible/inventory/.gitignore @@ -0,0 +1,3 @@ +* +!.gitignore +!example.yml \ No newline at end of file diff --git a/ansible/inventory/example.yml b/ansible/inventory/example.yml new file mode 100644 index 00000000..d790a4c4 --- /dev/null +++ b/ansible/inventory/example.yml @@ -0,0 +1,7 @@ +all: + # For more details see https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html + hosts: + irods_server: + ansible_host: #TODO + irods_client: + ansible_host: #TODO diff --git a/ansible/run_dd_benchmark.yml b/ansible/run_dd_benchmark.yml new file mode 100644 index 00000000..4682ad17 --- /dev/null +++ b/ansible/run_dd_benchmark.yml @@ -0,0 +1,101 @@ +- hosts: "{{ host }}" + tasks: + - name: Create test directory + file: + path: "{{ dd_dir }}/dd_in_progress" + state: directory + + - name: Copy Dockerfile + ansible.posix.synchronize: + src: tool-dd/ + dest: "{{ dd_dir }}/dd_build" + recursive: yes + + - name: Build latest dd image + become: yes + community.docker.docker_image: + build: + path: "{{ dd_dir }}/dd_build" + name: galaxybenchmarker/tool-dd + tag: latest + force_source: yes + force_tag: yes + source: build + + - name: Clear in-ram caches + become: yes + copy: + content: 3 + dest: /proc/sys/vm/drop_caches + unsafe_writes: yes + + - name: Run dd container (single dd) + become: yes + when: not dd_parallel|bool + community.docker.docker_container: + name: dd + image: galaxybenchmarker/tool-dd + detach: false + cleanup: true + volumes: + - "{{ dd_dir }}/dd_in_progress:/mnt/volume_under_test:rw" + working_dir: /mnt/volume_under_test + entrypoint: "dd" + command: [ + "bs={{ dd_blocksize }}", + "count={{ dd_blockcount }}", + "if={{ dd_input }}", + "of={{ dd_output }}", + "{% if dd_flush|bool %}conv=fsync{% endif %}", + ] + register: dd_single_results + + - name: Save results (single dd) + when: not dd_parallel|bool + local_action: + module: copy + content: "{{ dd_single_results.container.Output }}" + dest: "{{ controller_dir }}/{{ dd_result_file }}" + + - name: Run dd container (parallel dd) + become: yes + when: dd_parallel|bool + community.docker.docker_container: + name: dd + image: galaxybenchmarker/tool-dd + detach: false + cleanup: true + volumes: + - "{{ dd_dir }}/dd_in_progress:/mnt/volume_under_test:rw" + working_dir: /mnt/volume_under_test + env: + DD_BS: "{{ dd_blocksize }}" + DD_IF: "{{ dd_input }}" + DD_OF: "{{ dd_output }}" + DD_COUNT: "{{ dd_blockcount|int // 4 }}" + DD_SEEK_1: "{{ dd_blockcount|int // 4 * 1 }}" + DD_SKIP_1: "{{ dd_blockcount|int // 4 * 1 }}" + DD_SEEK_2: "{{ dd_blockcount|int // 4 * 2 }}" + DD_SKIP_2: "{{ dd_blockcount|int // 4 * 2 }}" + DD_SEEK_3: "{{ dd_blockcount|int // 4 * 3 }}" + DD_SKIP_3: "{{ dd_blockcount|int // 4 * 3 }}" + register: dd_parallel_results + + # - name: debug + # debug: var=dd_results + + - name: Save results (parallel dd) + when: dd_parallel|bool + local_action: + module: copy + content: "{{ dd_parallel_results.container.Output }}" + dest: "{{ controller_dir }}/{{ dd_result_file }}" + + - name: Cleanup + file: + path: "{{ dd_dir }}/{{ item }}" + state: absent + when: dd_cleanup|bool + loop: + - dd_in_progress + - dd_build diff --git a/ansible/run_fio_benchmark.yml b/ansible/run_fio_benchmark.yml new file mode 100644 index 00000000..dbec1b66 --- /dev/null +++ b/ansible/run_fio_benchmark.yml @@ -0,0 +1,187 @@ +- hosts: "{{ host }}" + tasks: + - name: Run fio benchmark + block: + - name: Create test directory + file: + path: "{{ fio_dir }}/fio_in_progress" + state: directory + mode: '0777' + + - name: Create temporary build directory + tempfile: + state: directory + suffix: .fio.build + register: tempdir + + - name: Copy Dockerfile + ansible.posix.synchronize: + src: tool-fio/ + dest: "{{ tempdir.path }}" + recursive: yes + + - name: Build latest fio image + become: yes + community.docker.docker_image: + build: + path: "{{ tempdir.path }}" + name: galaxybenchmarker/tool-fio + tag: latest + force_source: yes + force_tag: yes + source: build + + - name: Prepare read benchmark + when: fio_mode in ["read", "randread"] + block: + - name: Prepare in /tmp and move to destination + when: fio_prepare_read_benchmark_in_tmp|bool + block: + - name: Run fio container (create files) + become: yes + community.docker.docker_container: + name: fio + image: galaxybenchmarker/tool-fio + detach: false + cleanup: true + volumes: + - "{{ tempdir.path }}:/results:rw" + - "/tmp/fio_in_progress:/mnt/volume_under_test:rw" + working_dir: /mnt/volume_under_test + command: [ + "--bs={{ fio_blocksize }}", + "--direct=1", + "--end_fsync=1", + "--filename=testfile.fio", + "--group_reporting", + "--iodepth={{ fio_iodepth }}", + "--ioengine=libaio", + "--name={{ fio_jobname }}", + "--numjobs={{ fio_numjobs }}", + "--output", "/results/{{ fio_result_file }}", + "--output-format=json", + "{% if fio_refill_buffers|bool %}--refill_buffers{% endif %}", + "{% if fio_time_based|bool %}--runtime={{ fio_runtime_in_s }}{% endif %}", + "--rw={{ fio_mode }}", + "--size={{ fio_filesize }}", + "{% if fio_time_based|bool %}--time_based{% endif %}", + "--create_only=1", + ] + register: fio_results + ignore_errors: yes + + - name: debug + debug: var=fio_results + + - name: Move testfile to {{ fio_dir }}/fio_in_progress + become: yes + command: mv /tmp/fio_in_progress/testfile.fio {{ fio_dir }}/fio_in_progress + + - name: Change owner of testfile + become: yes + command: chown 1000:1000 {{ fio_dir }}/fio_in_progress/testfile.fio + + - name: Prepare in destination + when: not fio_prepare_read_benchmark_in_tmp|bool + block: + - name: Run fio container (create files) + become: yes + community.docker.docker_container: + name: fio + image: galaxybenchmarker/tool-fio + detach: false + cleanup: true + volumes: + - "{{ tempdir.path }}:/results:rw" + - "{{ fio_dir }}/fio_in_progress:/mnt/volume_under_test:rw" + working_dir: /mnt/volume_under_test + command: [ + "--bs={{ fio_blocksize }}", + "--direct=1", + "--end_fsync=1", + "--filename=testfile.fio", + "--group_reporting", + "--iodepth={{ fio_iodepth }}", + "--ioengine=libaio", + "--name={{ fio_jobname }}", + "--numjobs={{ fio_numjobs }}", + "--output", "/results/{{ fio_result_file }}", + "--output-format=json", + "{% if fio_refill_buffers|bool %}--refill_buffers{% endif %}", + "{% if fio_time_based|bool %}--runtime={{ fio_runtime_in_s }}{% endif %}", + "--rw={{ fio_mode }}", + "--size={{ fio_filesize }}", + "{% if fio_time_based|bool %}--time_based{% endif %}", + "--create_only=1", + ] + register: fio_results + ignore_errors: yes + + - name: debug + debug: var=fio_results + + # Stat breaks with OSError + # - name: Check if file exists + # stat: path={{ fio_dir }}/fio_in_progress/testfile.fio + # when: fio_mode in ["read", "randread"] + # register: stat_result + - name: Check if file exists (1) + shell: ls -la {{ fio_dir }}/fio_in_progress/testfile.fio + register: ls_result + + - name: Wait for file + when: not ("{{ fio_filesize_in_bytes }}" in ls_result.stdout) + block: + - name: Pause for 10 seconds to wait for file + pause: + seconds: 10 + + - name: Check if file exists (2) + shell: ls -la {{ fio_dir }}/fio_in_progress/testfile.fio + register: ls_result2 + + - name: Wait for file looonger + when: not ("{{ fio_filesize_in_bytes }}" in ls_result2.stdout) + block: + + - name: Pause for 1 minute to wait for file + pause: + minutes: 1 + + - name: Check if file exists (3) + shell: ls -la {{ fio_dir }}/fio_in_progress/testfile.fio + register: ls_result3 + + - name: Wait for file loooooonger + when: not ("{{ fio_filesize_in_bytes }}" in ls_result3.stdout) + block: + + - name: Pause for 1 more minute to wait for file + pause: + minutes: 1 + + - name: Check if file exists (4) + shell: ls -la {{ fio_dir }}/fio_in_progress/testfile.fio + register: ls_result4 + failed_when: not ("{{ fio_filesize_in_bytes }}" in ls_result4.stdout) + + - include_tasks: tasks/run_fio_and_check_result.yml + + - name: Collect result + ansible.builtin.fetch: + src: "{{ tempdir.path }}/{{ fio_result_file }}" + dest: "{{ controller_dir }}/{{ fio_result_file }}" + flat: yes + + always: + - name: Cleanup + file: + path: "{{ item }}" + state: absent + loop: + - "{{ fio_dir }}/fio_in_progress" + - "{{ tempdir.path }}" + retries: 15 + delay: 5 + register: cleanup + until: not cleanup["failed"] diff --git a/ansible/run_fio_benchmark_not_containerized.yml b/ansible/run_fio_benchmark_not_containerized.yml new file mode 100644 index 00000000..4ad00895 --- /dev/null +++ b/ansible/run_fio_benchmark_not_containerized.yml @@ -0,0 +1,45 @@ +- hosts: "{{ host }}" + tasks: + - name: Cleanup possible left over files + file: + path: "{{ fio_dir }}/fio_in_progress" + state: absent + - name: Create test directory + file: + path: "{{ fio_dir }}/fio_in_progress" + state: directory + + - name: Execute the fio test + ansible.builtin.command: | + fio + --rw={{ fio_mode }} + --name={{ fio_jobname }} + --bs={{ fio_blocksize }} + --direct=1 + --numjobs={{ fio_numjobs }} + --ioengine=libaio + --iodepth={{ fio_iodepth }} + --group_reporting + --runtime={{ fio_runtime_in_s }} + --size={{ fio_filesize }} + --output-format=json + --filename=testfile.fio + --output {{ fio_result_file }} + {% if fio_time_based|bool %} --time_based {% endif %} + {% if fio_refill_buffers|bool %} --refill_buffers {% endif %} + --exec_prerun="./scripts/barrier.py localhost 42042" + args: + chdir: "{{ fio_dir }}/fio_in_progress" + become: yes + register: fio_status + + - name: Collect result + ansible.builtin.fetch: + src: "{{ fio_dir }}/fio_in_progress/{{ fio_result_file }}" + dest: "{{ controller_dir }}/{{ fio_result_file }}" + flat: yes + + - name: Cleanup + file: + path: "{{ fio_dir }}/fio_in_progress" + state: absent \ No newline at end of file diff --git a/ansible/run_fio_benchmark_not_containerized_check.yml b/ansible/run_fio_benchmark_not_containerized_check.yml new file mode 100644 index 00000000..797561b4 --- /dev/null +++ b/ansible/run_fio_benchmark_not_containerized_check.yml @@ -0,0 +1,10 @@ +- hosts: "{{ host }}" + tasks: + - name: "Check if fio is installed" + shell: fio --help + register: package_check + ignore_errors: true + - name: "Error handling" + fail: + msg: "fio has to be installed" + when: package_check is failed \ No newline at end of file diff --git a/ansible/run_galaxy_job.yml b/ansible/run_galaxy_job.yml new file mode 100644 index 00000000..25af5a49 --- /dev/null +++ b/ansible/run_galaxy_job.yml @@ -0,0 +1,3 @@ +- hosts: "{{ host }}" + tasks: + - include_tasks: tasks/run_galaxy_job.yml diff --git a/ansible/run_galaxy_mount_benchmark.yml b/ansible/run_galaxy_mount_benchmark.yml new file mode 100644 index 00000000..f9b742d8 --- /dev/null +++ b/ansible/run_galaxy_mount_benchmark.yml @@ -0,0 +1,10 @@ +- hosts: "{{ host }}" + tasks: + - include_tasks: tasks/run_galaxy_job.yml + + - name: Check number of files + shell: find {{glx_path_to_files}} -type f -size {{ glx_expected_size_in_bytes }}c | wc -l + register: result + until: result.stdout.find("{{glx_expected_num_files}}") != -1 + retries: "{{ glx_verification_timeout_in_s|int // 5 }}" + delay: 5 \ No newline at end of file diff --git a/ansible/run_mdtest_benchmark.yml b/ansible/run_mdtest_benchmark.yml new file mode 100644 index 00000000..aab8bcfa --- /dev/null +++ b/ansible/run_mdtest_benchmark.yml @@ -0,0 +1,70 @@ +- hosts: "{{ host }}" + tasks: + - name: Create test directory + file: + path: "{{ mdtest_dir }}/mdtest_in_progress" + state: directory + mode: '0777' + + - name: Create temporary build directory + tempfile: + state: directory + suffix: .mdtest.build + register: tempdir + + - name: Copy Dockerfile + ansible.posix.synchronize: + src: tool-mdtest/ + dest: "{{ tempdir.path }}" + recursive: yes + + - name: Build latest mdtest image + become: yes + community.docker.docker_image: + build: + path: "{{ tempdir.path }}" + name: galaxybenchmarker/tool-mdtest + tag: latest + force_source: yes + force_tag: yes + source: build + + - name: Run mdtest container + become: yes + community.docker.docker_container: + name: mdtest + image: galaxybenchmarker/tool-mdtest + detach: false + cleanup: true + volumes: + - "{{ mdtest_dir }}/mdtest_in_progress:/mnt/volume_under_test:rw" + working_dir: /mnt/volume_under_test + command: [ + "-y", + "-Y", + "-n", + "{{ mdtest_num_files }}", + ] + register: mdtest_results + ignore_errors: yes + + - name: debug + debug: var=mdtest_results + + - name: Save results + local_action: + module: copy + content: "{{ mdtest_results.container.Output }}" + dest: "{{ controller_dir }}/{{ mdtest_result_file }}" + + - name: Cleanup + file: + path: "{{ item }}" + state: absent + loop: + - "{{ mdtest_dir }}/mdtest_in_progress" + - "{{ tempdir.path }}" + retries: 15 + delay: 5 + register: cleanup + until: not cleanup["failed"] diff --git a/ansible/run_s3-benchmark_benchmark.yml b/ansible/run_s3-benchmark_benchmark.yml new file mode 100644 index 00000000..ded65e96 --- /dev/null +++ b/ansible/run_s3-benchmark_benchmark.yml @@ -0,0 +1,59 @@ +- hosts: "{{ host }}" + tasks: + - name: Create temporary build directory + tempfile: + state: directory + suffix: .s3-benchmark.build + register: tempdir + + - name: Copy Dockerfile + ansible.posix.synchronize: + src: tool-s3-benchmark/ + dest: "{{ tempdir.path }}" + recursive: yes + + - name: Build latest s3-benchmark image + become: yes + community.docker.docker_image: + build: + path: "{{ tempdir.path }}" + name: galaxybenchmarker/tool-s3-benchmark + tag: latest + force_source: yes + force_tag: yes + source: build + + - name: Run s3-benchmark container + become: yes + community.docker.docker_container: + name: s3-benchmark + image: galaxybenchmarker/tool-s3-benchmark + detach: false + cleanup: true + command: [ + "-a", "{{ s3b_access_key_id }}", + "-b", "{{ s3b_bucket_name }}", + "-d", "{{ s3b_runtime_in_s }}", + "-s", "{{ s3b_secret_access_key }}", + "-t", "{{ s3b_threads }}", + "-u", "{{ s3b_base_url }}", + "-r", "{{ s3b_region }}", + "-z", "{{ s3b_filesize}}", + ] + register: s3b_results + + - name: debug + debug: var=s3b_results + + - name: Save results + local_action: + module: copy + content: "{{ s3b_results.container.Output }}" + dest: "{{ controller_dir }}/{{ s3b_result_file }}" + + - name: Cleanup + file: + path: "{{ item }}" + state: absent + loop: + - tempdir.path diff --git a/ansible/run_warp_benchmark.yml b/ansible/run_warp_benchmark.yml new file mode 100644 index 00000000..64328dc8 --- /dev/null +++ b/ansible/run_warp_benchmark.yml @@ -0,0 +1,61 @@ +- hosts: "{{ host }}" + tasks: + - name: Create temporary build directory + tempfile: + state: directory + suffix: .warp.build + register: tempdir + + - name: Copy Dockerfile + ansible.posix.synchronize: + src: tool-warp/ + dest: "{{ tempdir.path }}" + recursive: yes + + - name: Build latest warp image + become: yes + community.docker.docker_image: + build: + path: "{{ tempdir.path }}" + name: galaxybenchmarker/tool-warp + tag: latest + force_source: yes + force_tag: yes + source: build + + - name: Run warp container + become: yes + community.docker.docker_container: + name: warp + image: galaxybenchmarker/tool-warp + detach: false + cleanup: true + command: [ + "{{ warp_mode }}", + "--access-key", "{{ warp_access_key_id }}", + "--bucket", "{{ warp_bucket_name }}", + "--concurrent", "{{ warp_concurrent_ops }}", + "--duration", "{{ warp_runtime }}", + "--host", "{{ warp_base_url }}", + "--obj.size", "{{ warp_filesize}}", + "--region", "{{ warp_region }}", + "--secret-key", "{{ warp_secret_access_key }}", + "--tls", + ] + register: warp_results + + - name: debug + debug: var=warp_results + + - name: Save results + local_action: + module: copy + content: "{{ warp_results.container.Output }}" + dest: "{{ controller_dir }}/{{ warp_result_file }}" + + - name: Cleanup + file: + path: "{{ item }}" + state: absent + loop: + - tempdir.path diff --git a/ansible/setup_galaxy_server.yml b/ansible/setup_galaxy_server.yml new file mode 100644 index 00000000..9aa896a3 --- /dev/null +++ b/ansible/setup_galaxy_server.yml @@ -0,0 +1,133 @@ +- hosts: "{{ host }}" + become: true + tasks: + - name: Copy container setup onto host (sychronize preserves file permissions) + ansible.posix.synchronize: + src: ../files/galaxy-server/ + dest: /galaxy + recursive: yes + + - include_tasks: tasks/initial_start_galaxy.yml + + - name: Setup mount backend + when: (galaxy_use_mount is defined) and (galaxy_use_mount|bool) + block: + - name: Create galaxy files directory + file: + path: "{{ galaxy_host_volume }}/galaxy_in_progress" + state: directory + mode: '0777' + + - name: Update docker-compose with object store config + lineinfile: + path: /galaxy/docker-compose.yml + backrefs: yes + regexp: '^(.*)# PLACEHOLDER_ENV' + line: '\1- GALAXY_CONFIG_OBJECT_STORE_CONFIG_FILE=/custom_config/object_store_conf.local_mount.xml' + + - name: Update docker-compose volume mounts + lineinfile: + path: /galaxy/docker-compose.yml + backrefs: yes + regexp: '^(.*)# PLACEHOLDER_VOLUMES' + line: '\1- ${GALAXY_HOST_VOLUME}/galaxy_in_progress:/mnt/volume_under_test' + + - name: Restart galaxy + community.docker.docker_compose: + project_src: /galaxy + restarted: yes + environment: + GALAXY_HOST_VOLUME: "{{ galaxy_host_volume }}" + GALAXY_EXPORT_VOLUME: "{{ galaxy_export_volume }}/glx_export" + + - name: Setup s3 backend + when: (galaxy_use_s3 is defined) and (galaxy_use_s3|bool) + block: + - name: Update object store config with credentials + template: + src: ./files/galaxy-server/galaxy/custom_config/object_store_conf.s3.xml.j2 + dest: /galaxy/galaxy/custom_config/object_store_conf.s3.xml + owner: 1450 + group: 1450 + mode: '0664' + + - name: Update docker-compose with object store config + lineinfile: + path: /galaxy/docker-compose.yml + backrefs: yes + regexp: '^(.*)# PLACEHOLDER_ENV' + line: '\1- GALAXY_CONFIG_OBJECT_STORE_CONFIG_FILE=/custom_config/object_store_conf.s3.xml' + + - name: Restart galaxy + community.docker.docker_compose: + project_src: /galaxy + restarted: yes + environment: + GALAXY_EXPORT_VOLUME: "{{ galaxy_export_volume }}/glx_export" + + - name: Setup irods backend + when: (galaxy_use_irods is defined) and (galaxy_use_irods|bool) + block: + - name: Update object store config with connection details + template: + src: ./files/galaxy-server/galaxy/custom_config/object_store_conf.irods.xml.j2 + dest: /galaxy/galaxy/custom_config/object_store_conf.irods.xml + owner: 1450 + group: 1450 + mode: '0664' + + - name: Update docker-compose with object store config + lineinfile: + path: /galaxy/docker-compose.yml + backrefs: yes + regexp: '^(.*)# PLACEHOLDER_ENV' + line: '\1- GALAXY_CONFIG_OBJECT_STORE_CONFIG_FILE=/custom_config/object_store_conf.irods.xml' + + - name: Restart galaxy + community.docker.docker_compose: + project_src: /galaxy + restarted: yes + environment: + GALAXY_EXPORT_VOLUME: "{{ galaxy_export_volume }}/glx_export" + + - name: Wait for galaxy to become available + uri: + url: "http://localhost:8080/api/histories" + status_code: 200 + headers: + x-api-key: fakekey + register: result + until: result.status == 200 + retries: 4 + delay: 5 + ignore_errors: yes + + - name: Restart handler0 + when: result.status != 200 + uri: + url: "http://localhost:9002/index.html?processname=galaxy%3Ahandler0&action=restart" + timeout: 60 + + - name: Restart handler1 + when: result.status != 200 + uri: + url: "http://localhost:9002/index.html?processname=galaxy%3Ahandler1&action=restart" + timeout: 60 + + - name: Restart handler0 + when: result.status != 200 + uri: + url: "http://localhost:9002/index.html?processname=galaxy%3Agalaxy_web&action=restart" + timeout: 60 + + - name: Wait for galaxy to become available (2) + uri: + url: "http://localhost:8080/api/histories" + status_code: 200 + headers: + x-api-key: fakekey + register: result + until: result.status == 200 + retries: 4 + delay: 5 + ignore_errors: yes \ No newline at end of file diff --git a/ansible/setup_irods_fuse_client.yml b/ansible/setup_irods_fuse_client.yml new file mode 100644 index 00000000..3690ad6a --- /dev/null +++ b/ansible/setup_irods_fuse_client.yml @@ -0,0 +1,44 @@ +- hosts: "{{ host }}" + become: true + tasks: + - name: Copy container setup onto host (sychronize preserves file permissions) + synchronize: + src: ./files/irods-fuse-client/ + dest: /irods + recursive: yes + + - name: Set irods host ip for client + lineinfile: + path: /irods/scripts/config.yml + regexp: '^host: ' + line: "host: {{ irods_server_host }}" + + - name: Create irods volume + file: + path: "{{ irods_host_volume }}/irods-fuse" + state: directory + owner: "{{ irods_host_volume_owner_uid }}" + group: "{{ irods_host_volume_owner_uid }}" + mode: '0755' + + - name: Build and start container + community.docker.docker_compose: + project_src: /irods + state: present + build: yes + environment: + IRODS_HOST_VOLUME: "{{ irods_host_volume }}" + IRODS_HOST_VOLUME_OWNER: "{{ irods_host_volume_owner_uid }}" + register: output + + # - name: Debug startup logs + # command: docker logs irods_irods-fuse_1 + # register: logoutput + + # - name: debug + # debug: var=output + + - name: "Check 'Build and start container'" + ansible.builtin.assert: + that: + - "output.services['irods-fuse']['irods_irods-fuse_1'].state.running" diff --git a/ansible/setup_irods_server.yml b/ansible/setup_irods_server.yml new file mode 100644 index 00000000..b0f3de36 --- /dev/null +++ b/ansible/setup_irods_server.yml @@ -0,0 +1,21 @@ +- hosts: "{{ host }}" + become: true + tasks: + - include: tasks/install_irods.yml + vars: + irods_host_volume_internal: "{{ irods_host_volume | default('/tmp') }}" + + - include: tasks/configure_irods_local_volume.yml + when: (irods_host_volume is defined) + + - include: tasks/configure_irods_s3_volume.yml + when: (irods_use_s3 is defined) and (irods_use_s3|bool) + vars: + IRODS_S3_BUCKET: frct-smoe-bench-ec61-01 + IRODS_S3_DOMAIN: s3.bwsfs.uni-freiburg.de + IRODS_S3_REGION: fr-repl + IRODS_S3_ACCESS_ID: ... + IRODS_S3_SECRET_KEY: ... + + - include: tasks/install_davrods.yml + when: (irods_use_davrods is defined) and (irods_use_davrods|bool) diff --git a/ansible/setup_irods_webdav_client.yml b/ansible/setup_irods_webdav_client.yml new file mode 100644 index 00000000..869d720a --- /dev/null +++ b/ansible/setup_irods_webdav_client.yml @@ -0,0 +1,43 @@ +- hosts: "{{ host }}" + become: true + tasks: + - name: Copy container setup onto host (sychronize preserves file permissions) + synchronize: + src: ./files/irods-webdav-client/ + dest: /irods + recursive: yes + + - name: Setup fstab + template: + src: ./files/irods-webdav-client/volumes/fstab.j2 + dest: /irods/volumes/fstab + + - name: Create irods volume + file: + path: "{{ irods_host_volume }}/irods-webdav" + state: directory + owner: "{{ davrods_owner_uid }}" + group: "{{ davrods_owner_uid }}" + mode: '0755' + + - name: Build and start container + community.docker.docker_compose: + project_src: /irods + state: present + build: yes + environment: + IRODS_HOST_VOLUME: "{{ irods_host_volume }}" + IRODS_HOST_VOLUME_OWNER: "{{ davrods_owner_uid }}" + register: output + + # - name: Debug startup logs + # command: docker logs irods_irods-fuse_1 + # register: logoutput + + # - name: debug + # debug: var=output + + - name: "Check 'Build and start container'" + ansible.builtin.assert: + that: + - "output.services['irods-webdav']['irods_irods-webdav_1'].state.running" diff --git a/ansible/tasks/configure_irods_local_volume.yml b/ansible/tasks/configure_irods_local_volume.yml new file mode 100644 index 00000000..8b4a16f8 --- /dev/null +++ b/ansible/tasks/configure_irods_local_volume.yml @@ -0,0 +1,30 @@ +--- +- name: Add rootResc resource + community.docker.docker_container_exec: + container: irods_irods_1 + argv: + - /bin/bash + - "-c" + - iadmin mkresc rootResc passthru + +- name: Add mountedVolumeResc resource + community.docker.docker_container_exec: + container: irods_irods_1 + argv: + - /bin/bash + - "-c" + - iadmin mkresc mountedVolumeResc unixfilesystem irods.local:/mnt/mountedVolumeResc + +- name: Connect rootResc with mountedVolumeResc + community.docker.docker_container_exec: + container: irods_irods_1 + argv: + - /bin/bash + - "-c" + - iadmin addchildtoresc rootResc mountedVolumeResc + +- name: Set rootResc as default resource + ansible.builtin.lineinfile: + path: /irods/volumes/config_server/core.re + regexp: '^acSetRescSchemeForCreate' + line: acSetRescSchemeForCreate {msiSetDefaultResc("rootResc","null"); } diff --git a/ansible/tasks/configure_irods_s3_volume.yml b/ansible/tasks/configure_irods_s3_volume.yml new file mode 100644 index 00000000..f5305d9c --- /dev/null +++ b/ansible/tasks/configure_irods_s3_volume.yml @@ -0,0 +1,43 @@ +--- +- name: Add rootResc resource + community.docker.docker_container_exec: + container: irods_irods_1 + argv: + - /bin/bash + - "-c" + - iadmin mkresc rootResc passthru + +- name: Create S3 credential file + copy: + content: "{{ IRODS_S3_ACCESS_ID }}\n{{ IRODS_S3_SECRET_KEY }}" + dest: /s3_credentials + +- name: Copy S3 credential file into irods container + shell: docker cp /s3_credentials irods_irods_1:/s3_credentials + +- name: Remove S3 credential file from host + file: + path: "/s3_credentials" + state: absent + +- name: Add s3Resc resource + community.docker.docker_container_exec: + container: irods_irods_1 + argv: + - /bin/bash + - "-c" + - iadmin mkresc s3Resc s3 irods.local:/{{IRODS_S3_BUCKET}}/irods "S3_DEFAULT_HOSTNAME={{IRODS_S3_DOMAIN}};S3_AUTH_FILE=/s3_credentials;S3_REGIONNAME={{IRODS_S3_REGION}};S3_RETRY_COUNT=1;S3_WAIT_TIME_SEC=3;S3_PROTO=HTTPS;ARCHIVE_NAMING_POLICY=consistent;HOST_MODE=cacheless_attached" + +- name: Connect rootResc with s3Resc + community.docker.docker_container_exec: + container: irods_irods_1 + argv: + - /bin/bash + - "-c" + - iadmin addchildtoresc rootResc s3Resc + +- name: Set rootResc as default resource + ansible.builtin.lineinfile: + path: /irods/volumes/config_server/core.re + regexp: '^acSetRescSchemeForCreate' + line: acSetRescSchemeForCreate {msiSetDefaultResc("rootResc","null"); } diff --git a/ansible/tasks/initial_start_galaxy.yml b/ansible/tasks/initial_start_galaxy.yml new file mode 100644 index 00000000..f06120ab --- /dev/null +++ b/ansible/tasks/initial_start_galaxy.yml @@ -0,0 +1,52 @@ +--- +- name: Initial start of galaxy (Dockerfile has a racecondition, maybe retry necessary) + block: + - name: Increment the retry count + set_fact: + retry_count: "{{ 0 if retry_count is undefined else retry_count | int + 1 }}" + + - name: Build and start galaxy + community.docker.docker_compose: + project_src: /galaxy + state: present + build: yes + + environment: + GALAXY_EXPORT_VOLUME: "{{ galaxy_export_volume }}/glx_export" + register: output + + - name: "Check 'Build and start galaxy'" + assert: + that: + - "output.services.galaxy.galaxy_galaxy_1.state.running" + + - name: Wait for galaxy to become available + uri: + url: "http://localhost:8080/api/histories" + status_code: 200 + headers: + x-api-key: fakekey + register: result + until: result.status == 200 + retries: 18 + delay: 5 + + rescue: + - fail: + msg: Maximum retries of grouped tasks reached + when: retry_count | int == 3 + + - debug: + msg: "Initial start of galaxy failed, let's give it another shot" + + - name: Stop galaxy + community.docker.docker_compose: + project_src: "/galaxy" + state: absent + + - name: Cleanup galaxy volume + file: + path: "{{ galaxy_export_volume }}/glx_export" + state: absent + + - include_tasks: initial_start_galaxy.yml \ No newline at end of file diff --git a/ansible/tasks/install_davrods.yml b/ansible/tasks/install_davrods.yml new file mode 100644 index 00000000..c9b2717c --- /dev/null +++ b/ansible/tasks/install_davrods.yml @@ -0,0 +1,33 @@ +--- +- name: Copy container setup onto host (sychronize preserves file permissions) + ansible.posix.synchronize: + src: ../files/irods-davrods-server/ + dest: /davrods + recursive: yes + +- name: Set host ip as server alias + lineinfile: + path: /davrods/volumes/davrods-vhost.conf + regexp: 'ServerAlias SET_TO_HOST_IP' + line: " ServerAlias {{ hostvars[inventory_hostname]['ansible_default_ipv4']['address'] }}" + +- name: Build and start container + community.docker.docker_compose: + project_src: /davrods + state: present + build: yes + environment: + DAVRODS_VOLUME_OWNER: "{{ davrods_owner_uid }}" + register: output + +# - name: Debug startup logs +# command: docker logs irods_irods_1 +# register: logoutput + +# - name: debug +# debug: var=logoutput + +- name: "Check 'Build and start container'" + ansible.builtin.assert: + that: + - "output.services.davrods.davrods_davrods_1.state.running" diff --git a/ansible/tasks/install_irods.yml b/ansible/tasks/install_irods.yml new file mode 100644 index 00000000..d6068836 --- /dev/null +++ b/ansible/tasks/install_irods.yml @@ -0,0 +1,54 @@ +--- +- name: Create irods volume + file: + path: "{{ irods_host_volume_internal }}/irods" + state: directory + owner: "1000" + group: "1000" + mode: '0755' + +- name: Copy container setup onto host (sychronize preserves file permissions) + ansible.posix.synchronize: + src: ../files/irods-server/ + dest: /irods + recursive: yes + +- name: Build and start container + community.docker.docker_compose: + project_src: /irods + state: present + build: yes + environment: + IRODS_HOST_VOLUME: "{{ irods_host_volume_internal }}" + register: output + +# - name: Debug startup logs +# command: docker logs irods_irods_1 +# register: logoutput + +# - name: debug +# debug: var=logoutput + +- name: "Check 'Build and start container'" + ansible.builtin.assert: + that: + - "output.services.irods.irods_irods_1.state.running" + - "output.services.db.irods_db_1.state.running" + +- name: Wait for port 1247 to become open on the host + wait_for: + port: 1247 + delay: 5 + timeout: 30 + +- name: Configure rods user for i-Commands + community.docker.docker_container_exec: + container: irods_irods_1 + argv: + - /bin/bash + - "-c" + - iinit adminpass + retries: 3 + delay: 3 + register: result + until: result.rc == 0 diff --git a/ansible/tasks/remove_container.yml b/ansible/tasks/remove_container.yml new file mode 100644 index 00000000..72ef2e3f --- /dev/null +++ b/ansible/tasks/remove_container.yml @@ -0,0 +1,16 @@ +--- +- name: "Check if {{path}} exists" + stat: + path: "{{path}}" + register: stat_result + +- name: "Stop container for project in {{path}}" + when: stat_result.stat.exists + community.docker.docker_compose: + project_src: "{{path}}" + state: absent + +- name: "Remove {{path}} from server" + file: + path: "{{path}}" + state: absent diff --git a/ansible/tasks/run_fio_and_check_result.yml b/ansible/tasks/run_fio_and_check_result.yml new file mode 100644 index 00000000..b92d9c5c --- /dev/null +++ b/ansible/tasks/run_fio_and_check_result.yml @@ -0,0 +1,57 @@ +--- +- name: "Taskgroup: run fio and check result, potential retry" + block: + - name: Increment the retry count + set_fact: + retry_count: "{{ 0 if retry_count is undefined else retry_count | int + 1 }}" + + - name: Run fio container + become: true + community.docker.docker_container: + name: fio + image: galaxybenchmarker/tool-fio + detach: false + cleanup: true + volumes: + - "{{ tempdir.path }}:/results:rw" + - "{{ fio_dir }}/fio_in_progress:/mnt/volume_under_test:rw" + working_dir: /mnt/volume_under_test + command: [ + "--bs={{ fio_blocksize }}", + "--direct=1", + "--end_fsync=1", + "--filename=testfile.fio", + "--group_reporting", + "--iodepth={{ fio_iodepth }}", + "--ioengine={{ fio_ioengine }}", + "--name={{ fio_jobname }}", + "--numjobs={{ fio_numjobs }}", + "--output", "/results/{{ fio_result_file }}", + "--output-format=json", + "{% if fio_refill_buffers|bool %}--refill_buffers{% endif %}", + "{% if fio_time_based|bool %}--runtime={{ fio_runtime_in_s }}{% endif %}", + "--rw={{ fio_mode }}", + "--size={{ fio_filesize }}", + "{% if fio_time_based|bool %}--time_based{% endif %}", + "{% if fio_mode in ['read', 'randread'] %}--create_on_open=1{% endif %}", + "{% if fio_ramptime_in_s %}--ramp_time={{ fio_ramptime_in_s }}{% endif%}", + ] + register: fio_results_2 + # IRODSFS generates errors during cleanup + ignore_errors: true + + - name: debug + debug: var=fio_results_2 + + - name: Check fio result, fail when bandwidth=0 + command: 'grep -E "bw_mean\" : [1-9]" {{ tempdir.path }}/{{ fio_result_file }}' + + rescue: + - fail: + msg: Maximum retries of grouped tasks reached + when: retry_count | int == 5 + + - debug: + msg: "Fio failed, let's give it another shot" + + - include_tasks: run_fio_and_check_result.yml \ No newline at end of file diff --git a/ansible/tasks/run_galaxy_job.yml b/ansible/tasks/run_galaxy_job.yml new file mode 100644 index 00000000..c4378757 --- /dev/null +++ b/ansible/tasks/run_galaxy_job.yml @@ -0,0 +1,18 @@ +--- +- name: Check if galaxy directory exist + stat: + path: "/galaxy" + register: stat_output + failed_when: not stat_output.stat.isdir + +- name: Run script inside galaxy-job-starter container + community.docker.docker_container_exec: + container: galaxy_galaxy-job-starter_1 + command: python run_job.py + env: + GLX_TOOL_ID: "{{ glx_tool_id }}" + GLX_TOOL_INPUT: "'{{ glx_tool_input }}'" + register: job_result + +- name: debug + debug: var=job_result diff --git a/benchmark_config.yml.example b/benchmark_config.yml.example deleted file mode 100644 index 14abe70c..00000000 --- a/benchmark_config.yml.example +++ /dev/null @@ -1,130 +0,0 @@ -# You need have access to a GalaxyInstance with admin-rights -galaxy: - url: "http://galaxy.example.com" - user_key: "blablabla" - # Check, if tools need to be installed when running GalaxyWorkflow (can be turned off after first benchmark-run) - shed_install: true - # Should Galaxy be configured to use the given Destinations or is everything already set? - configure_job_destinations: true - # Used to deploy DynamicDestinations via Ansible - ssh_user: ubuntu - ssh_key: /local/path/to/ssh/key.cert - galaxy_root_path: /srv/galaxy - galaxy_config_dir: /srv/galaxy/server/config - galaxy_user: galaxy - -# Used for analyzing results -influxdb: - host: influxdb.example.com - port: 8086 - username: glx_benchmarker_user - password: supersecret - db_name: glx_benchmarker - -# Configure all the Destinations that should be benchmarked -destinations: - - name: LocalPulsar - type: PulsarMQ - amqp_url: "pyamqp://username:password@rabbitmq.example.com:5672//" - # If used for ColdWarmBenchmark, we need to have ssh-access to the Pulsar-Server - host: pulsar.example.com - host_user: centos - ssh_key: /local/path/to/ssh/key.cert - tool_dependency_dir: /data/share/tools - jobs_directory_dir: /data/share/staging - persistence_dir: /data/share/persisted_data - # To configure additional params in job_conf.xml - job_plugin_params: - manager: __default__ - job_destination_params: - dependency_resolution: remote - default_file_action: remote_transfer - remote_metadata: false - rewrite_parameters: true - amqp_acknowledge: true - amqp_ack_republish_time: 10 - - name: RemotePulsar1 - type: PulsarMQ - # If credentials are set, Destination won't create a user on Galaxy and will just use these for submitting jobs - galaxy_user_name: user2 - galaxy_user_key: blablablakey - - name: RemotePulsar1 - type: PulsarMQ - amqp_url: "pyamqp://username:password@rabbit.example3.com:5672//" - job_plugin_params: - manager: __default__ - job_destination_params: - dependency_resolution: remote - default_file_action: remote_transfer - remote_metadata: false - rewrite_parameters: true - amqp_acknowledge: true - amqp_ack_republish_time: 10 - - name: CondorManager - type: Condor - host: condor-manager.uni.andreas-sk.de - host_user: centos - ssh_key: /local/path/to/ssh/key.cert - jobs_directory_dir: /data/share/condor - -workflows: - - name: GalaxyWorkflow1 - type: Galaxy - path: path/to/galaxy/workflow/file.ga - timeout: 100 # Optional. Workflow will be canceled after timeout. - - name: CondorWorkflow1 - type: Condor - path: path/to/condor/workflow/folder - job_file: job.job # needs to be in directory at "path" - -benchmarks: - - name: ColdvsWarm - type: ColdvsWarm - destinations: - - LocalPulsar - runs_per_workflow: 5 - workflows: - - GalaxyWorkflow1 - # Pre task to clean up Pulsar, so the workflow-run is actually cold (i.e. run for the "first time") - cold_pre_task: - type: "ansible-playbook" - playbook: "coldwarm_pretask.yml" - warm_pre_task: - type: "ansible-playbook" - playbook: "cleanup-pulsar.yml" - # If you want to clean up Pulsar after Benchmark ran - post_task: - type: "ansible-playbook" - playbook: "cleanup_pulsar.yml" - - name: DestinationComparison - type: DestinationComparison - pre_task: - type: "ansible-playbook" - playbook: "cleanup-pulsar.yml" - post_task: - type: "ansible-playbook" - playbook: "cleanup-pulsar.yml" # Call some script after ending benchmark (like for cleaning up) - destinations: - - LocalPulsar - - RemotePulsar1 - - RemotePulsar2 - runs_per_workflow: 5 - warmup: true # Should a warmup-run happen before the actual benchmarking? - workflows: - - GalaxyWorkflow1 - - name: PulsarBurstBenchmark - type: Burst - runs_per_workflow: 100 - burst_rate: 1 # How many workflows should be submitted per second - destinations: - - RemotePulsar1 # Only one destination allowed! - workflows: - - GalaxyWorkflow1 - - name: CondorBurstBenchmark - type: Burst - runs_per_workflow: 1000 - burst_rate: 20 - destinations: - - CondorManager - workflows: - - CondorWorkflow1 \ No newline at end of file diff --git a/benchmark_config.yml.usegalaxyeu b/benchmark_config.yml.usegalaxyeu deleted file mode 100644 index 9246fdb7..00000000 --- a/benchmark_config.yml.usegalaxyeu +++ /dev/null @@ -1,56 +0,0 @@ -galaxy: - url: "https://usegalaxy.eu" - # Check, if tools need to be installed when running GalaxyWorkflow (can be turned off after first benchmark-run) - shed_install: false - # Should Galaxy be configured to use the given Destinations or is everything already set? - configure_job_destinations: false - user_key: YOUR-REGULAR-USER-KEY - -# Configure all the Destinations that should be benchmarked -destinations: - - name: YourDestinationName - type: Galaxy - galaxy_user_key: YOUR-USER-KEY-FOR-DESTINATION-USER - -workflows: - - name: ard - type: Galaxy - path: /workflow-testing/sklearn/ard/ard.ga - timeout: 5000 # Optional. Workflow will be canceled after timeout. - - name: gromacs - type: Galaxy - path: /workflow-testing/training/computational-chemistry/gromacs/gromacs.ga - timeout: 5400 - - name: mapping_by_sequencing - type: Galaxy - path: /workflow-testing/training/variant-analysis/mapping-by-sequencing/mapping_by_sequencing.ga - timeout: 5400 - - name: adaboost - type: Galaxy - path: /workflow-testing/sklearn/adaboost/adaboost.ga - timeout: 1200 - - name: ard - type: Galaxy - path: /workflow-testing/sklearn/ard/ard.ga - timeout: 1200 - -benchmarks: - - name: BenchmarkName - type: DestinationComparison - destinations: - - YourDestinationName - workflows: - - ard - - adaboost - - gromacs - - mapping_by_sequencing - runs_per_workflow: 5 - warmup: true - -# Please do not change! -influxdb: - host: influxdb - port: 8086 - username: glx_benchmarker - password: glx_benchmarker - db_name: glx_benchmarker \ No newline at end of file diff --git a/cleanup_pulsar.yml b/cleanup_pulsar.yml deleted file mode 100644 index 42166022..00000000 --- a/cleanup_pulsar.yml +++ /dev/null @@ -1,6 +0,0 @@ -- hosts: all - tasks: - - name: Delete old-folder in tool_dependency_dir - file: - path: "{{tool_dependency_dir}}/old" - state: absent \ No newline at end of file diff --git a/coldwarm_pretask.yml b/coldwarm_pretask.yml deleted file mode 100644 index a5b9dbac..00000000 --- a/coldwarm_pretask.yml +++ /dev/null @@ -1,50 +0,0 @@ -- hosts: all - tasks: - - name: Stop Pulsar - service: - name: pulsar - state: stopped - become: yes - - name: Create old-folder for old _conda folders - file: - path: "{{tool_dependency_dir}}/old" - state: directory - - name: Check if base dependencies folder already exists - stat: - path: "{{tool_dependency_dir}}/_conda-base" - register: conda_base_directory - - name: Check if _conda folder already exists - stat: - path: "{{tool_dependency_dir}}/_conda" - register: conda_directory - - name: Move _conda to old-folder, so it can be removed at the end of benchmark (to speedup benchmark) - command: "mv {{tool_dependency_dir}}/_conda {{tool_dependency_dir}}/old/_conda-{{ansible_date_time.iso8601_basic_short}}" - when: conda_directory.stat.exists and conda_directory.stat.isdir - - name: Delete conda.lock in tool_dependency_dir - file: - path: "{{tool_dependency_dir}}/conda.lock" - state: absent - - name: Copy _conda-base to _conda (to speed up Pulsar-Startup) - command: "cp {{tool_dependency_dir}}/_conda-base/ {{tool_dependency_dir}}/_conda/ -R" - when: conda_base_directory.stat.exists and conda_base_directory.stat.isdir - - name: Delete persisted_data-folder - file: - path: "{{persistence_dir}}" - state: absent - - name: Delete staging-folder - file: - path: "{{jobs_directory_dir}}" - state: absent - ignore_errors: yes - - name: Start Pulsar - service: - name: pulsar - state: started - become: yes - - name: Pause for 10 minutes, so Pulsar has time to install all base dependencies - pause: - minutes: 10 - when: conda_base_directory.stat.exists == False - - name: Copy _conda to _conda-base to speedup future Pulsar-Startups - command: "cp {{tool_dependency_dir}}/_conda/ {{tool_dependency_dir}}/_conda-base/ -R" - when: conda_base_directory.stat.exists == False diff --git a/coldwarm_pretask_only_persistence_dir.yml b/coldwarm_pretask_only_persistence_dir.yml deleted file mode 100644 index 1e4e65d1..00000000 --- a/coldwarm_pretask_only_persistence_dir.yml +++ /dev/null @@ -1,21 +0,0 @@ -- hosts: all - tasks: - - name: Stop Pulsar - service: - name: pulsar - state: stopped - become: yes - - name: Delete persisted_data-folder - file: - path: "{{persistence_dir}}" - state: absent - - name: Delete staging-folder - file: - path: "{{jobs_directory_dir}}" - state: absent - ignore_errors: yes - - name: Start Pulsar - service: - name: pulsar - state: started - become: yes diff --git a/coldwarm_pretask_with_cvmfs-clear.yml b/coldwarm_pretask_with_cvmfs-clear.yml deleted file mode 100644 index c19a21cc..00000000 --- a/coldwarm_pretask_with_cvmfs-clear.yml +++ /dev/null @@ -1,53 +0,0 @@ -- hosts: all - tasks: - - name: Stop Pulsar - service: - name: pulsar - state: stopped - become: yes - - name: Create old-folder for old _conda folders - file: - path: "{{tool_dependency_dir}}/old" - state: directory - - name: Check if base dependencies folder already exists - stat: - path: "{{tool_dependency_dir}}/_conda-base" - register: conda_base_directory - - name: Check if _conda folder already exists - stat: - path: "{{tool_dependency_dir}}/_conda" - register: conda_directory - - name: Move _conda to old-folder, so it can be removed at the end of benchmark (to speedup benchmark) - command: "mv {{tool_dependency_dir}}/_conda {{tool_dependency_dir}}/old/_conda-{{ansible_date_time.iso8601_basic_short}}" - when: conda_directory.stat.exists and conda_directory.stat.isdir - - name: Delete conda.lock in tool_dependency_dir - file: - path: "{{tool_dependency_dir}}/conda.lock" - state: absent - - name: Copy _conda-base to _conda (to speed up Pulsar-Startup) - command: "cp {{tool_dependency_dir}}/_conda-base/ {{tool_dependency_dir}}/_conda/ -R" - when: conda_base_directory.stat.exists and conda_base_directory.stat.isdir - - name: Delete persisted_data-folder - file: - path: "{{persistence_dir}}" - state: absent - - name: Delete staging-folder - file: - path: "{{jobs_directory_dir}}" - state: absent - ignore_errors: yes - - name: Clear CVMFS (cvmfs_config wipecache) - command: "cvmfs_config wipecache" - become: yes - - name: Start Pulsar - service: - name: pulsar - state: started - become: yes - - name: Pause for 10 minutes, so Pulsar has time to install all base dependencies - pause: - minutes: 10 - when: conda_base_directory.stat.exists == False - - name: Copy _conda to _conda-base to speedup future Pulsar-Startups - command: "cp {{tool_dependency_dir}}/_conda/ {{tool_dependency_dir}}/_conda-base/ -R" - when: conda_base_directory.stat.exists == False diff --git a/condor-workflows/exit/exit.job b/condor-workflows/exit/exit.job deleted file mode 100644 index 19dc2a6d..00000000 --- a/condor-workflows/exit/exit.job +++ /dev/null @@ -1,7 +0,0 @@ -Universe = vanilla -Executable = exit.sh -Log = exit.$(process).log -Output = exit.$(process).out -Error = exit.$(process).err -request_cpus = 1 -Queue 100 \ No newline at end of file diff --git a/condor-workflows/exit/exit.sh b/condor-workflows/exit/exit.sh deleted file mode 100644 index c3c3f3f5..00000000 --- a/condor-workflows/exit/exit.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -exit 0 \ No newline at end of file diff --git a/condor-workflows/fio/fio.job b/condor-workflows/fio/fio.job deleted file mode 100644 index c506e947..00000000 --- a/condor-workflows/fio/fio.job +++ /dev/null @@ -1,7 +0,0 @@ -Universe = vanilla -Executable = fio.sh -Log = fio.$(process).log -Output = fio.$(process).out -Error = fio.$(process).err -request_cpus = 2 -Queue 1 diff --git a/condor-workflows/fio/fio.sh b/condor-workflows/fio/fio.sh deleted file mode 100644 index 8b3bf3aa..00000000 --- a/condor-workflows/fio/fio.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -echo "Max-Write-Speed:" -fio --rw=write --name=IOPS-write --bs=1024k --direct=1 --filename=test-file --numjobs=4 --ioengine=libaio --iodepth=32 --refill_buffers --group_reporting --runtime=60 --time_based --size=5G -rm test-file - -echo "Max-Read-Speed:" -fio --rw=read --name=IOPS-read --bs=1024k --direct=1 --filename=test-file --numjobs=4 --ioengine=libaio --iodepth=32 --refill_buffers --group_reporting --runtime=60 --time_based --size=5G -rm test-file - -echo "IOPS-Write:" -fio --rw=randwrite --name=IOPS-write --bs=4k --direct=1 --filename=test-file --numjobs=4 --ioengine=libaio --iodepth=32 --refill_buffers --group_reporting --runtime=60 --time_based --size=5G -rm test-file - -echo "IOPS-Read:" -fio --rw=randread --name=IOPS-read --bs=4k --direct=1 --filename=test-file --numjobs=4 --ioengine=libaio --iodepth=32 --refill_buffers --group_reporting --runtime=60 --time_based --size=5G -rm test-file diff --git a/condor-workflows/iperf/iperf.job b/condor-workflows/iperf/iperf.job deleted file mode 100644 index 781b2561..00000000 --- a/condor-workflows/iperf/iperf.job +++ /dev/null @@ -1,7 +0,0 @@ -Universe = vanilla -Executable = iperf.sh -Log = iperf.$(process).log -Output = iperf.$(process).out -Error = iperf.$(process).err -request_cpus = 1 -Queue 1 diff --git a/condor-workflows/iperf/iperf.sh b/condor-workflows/iperf/iperf.sh deleted file mode 100644 index 008770f6..00000000 --- a/condor-workflows/iperf/iperf.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -iperf -c iperf.uni.andreas-sk.de -t 30 diff --git a/condor-workflows/ping/ping.job b/condor-workflows/ping/ping.job deleted file mode 100644 index 548bfb8e..00000000 --- a/condor-workflows/ping/ping.job +++ /dev/null @@ -1,7 +0,0 @@ -Universe = vanilla -Executable = ping.sh -Log = ping.$(process).log -Output = ping.$(process).out -Error = ping.$(process).err -request_cpus = 1 -Queue 1 diff --git a/condor-workflows/ping/ping.sh b/condor-workflows/ping/ping.sh deleted file mode 100644 index 5f9af03e..00000000 --- a/condor-workflows/ping/ping.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -ping iperf.uni.andreas-sk.de -c 30 \ No newline at end of file diff --git a/condor-workflows/sleep/test.job b/condor-workflows/sleep/test.job deleted file mode 100644 index ead1e0f6..00000000 --- a/condor-workflows/sleep/test.job +++ /dev/null @@ -1,7 +0,0 @@ -Universe = vanilla -Executable = test.sh -Log = test.$(process).log -Output = test.$(process).out -Error = test.$(process).err -request_cpus = 1 -Queue 1 \ No newline at end of file diff --git a/condor-workflows/sleep/test.sh b/condor-workflows/sleep/test.sh deleted file mode 100644 index ec964988..00000000 --- a/condor-workflows/sleep/test.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -sleep 1 -echo "$(hostname)" \ No newline at end of file diff --git a/custom_galaxy_workflows/big-file-transfer/big-file-transfer-test.yml b/custom_galaxy_workflows/big-file-transfer/big-file-transfer-test.yml deleted file mode 100644 index 16041cfd..00000000 --- a/custom_galaxy_workflows/big-file-transfer/big-file-transfer-test.yml +++ /dev/null @@ -1,6 +0,0 @@ -- doc: Just transfer a big file - job: - big-file: - class: File - location: http://speedtest.belwue.net/random-1G - filetype: txt diff --git a/custom_galaxy_workflows/big-file-transfer/big-file-transfer.ga b/custom_galaxy_workflows/big-file-transfer/big-file-transfer.ga deleted file mode 100644 index 4597de78..00000000 --- a/custom_galaxy_workflows/big-file-transfer/big-file-transfer.ga +++ /dev/null @@ -1,78 +0,0 @@ -{ - "uuid": "cec65567-724c-40f6-9079-b79a241be7a4", - "tags": [], - "format-version": "0.1", - "name": "Big-File", - "version": 1, - "steps": { - "0": { - "tool_id": null, - "tool_version": null, - "outputs": [], - "workflow_outputs": [{ - "output_name": "output", - "uuid": "c1221d53-4d6b-4735-a39d-c8e9a4cd3492", - "label": null - }], - "input_connections": {}, - "tool_state": "{}", - "id": 0, - "uuid": "2d85b49e-97c0-4200-be9f-7b20d7546b7e", - "errors": null, - "name": "Input dataset", - "label": "big-file", - "inputs": [ - { - "description": "", - "name": "big-file" - } - ], - "position": { - "top": 133, - "left": 155.5 - }, - "annotation": "", - "content_id": null, - "type": "data_input" - }, - "1": { - "tool_id": "Show beginning1", - "tool_version": "1.0.0", - "outputs": [{ - "type": "input", - "name": "out_file1" - }], - "workflow_outputs": [{ - "output_name": "out_file1", - "uuid": "af594591-07e2-412e-840a-f6460edc1531", - "label": null - }], - "input_connections": { - "input": { - "output_name": "output", - "id": 0 - } - }, - "tool_state": "{\"__page__\": null, \"input\": \"{\\\"__class__\\\": \\\"RuntimeValue\\\"}\", \"__rerun_remap_job_id__\": null, \"lineNum\": \"\\\"1\\\"\"}", - "id": 1, - "uuid": "010ee8b9-bd5d-4f99-87b9-a7dad0e0f985", - "errors": null, - "name": "Select first", - "post_job_actions": {}, - "label": "Select first line ", - "inputs": [{ - "name": "input", - "description": "runtime parameter for tool Select first" - }], - "position": { - "top": 446, - "left": 481.5 - }, - "annotation": "", - "content_id": "Show beginning1", - "type": "tool" - } - }, - "annotation": "", - "a_galaxy_workflow": "true" -} diff --git a/custom_galaxy_workflows/big-file-transfer/very-big-file-transfer-test.yml b/custom_galaxy_workflows/big-file-transfer/very-big-file-transfer-test.yml deleted file mode 100644 index 49345ae8..00000000 --- a/custom_galaxy_workflows/big-file-transfer/very-big-file-transfer-test.yml +++ /dev/null @@ -1,6 +0,0 @@ -- doc: Just transfer a big file - job: - big-file: - class: File - location: http://speedtest.belwue.net/10G - filetype: txt diff --git a/custom_galaxy_workflows/big-file-transfer/very-big-file-transfer.ga b/custom_galaxy_workflows/big-file-transfer/very-big-file-transfer.ga deleted file mode 100644 index 4597de78..00000000 --- a/custom_galaxy_workflows/big-file-transfer/very-big-file-transfer.ga +++ /dev/null @@ -1,78 +0,0 @@ -{ - "uuid": "cec65567-724c-40f6-9079-b79a241be7a4", - "tags": [], - "format-version": "0.1", - "name": "Big-File", - "version": 1, - "steps": { - "0": { - "tool_id": null, - "tool_version": null, - "outputs": [], - "workflow_outputs": [{ - "output_name": "output", - "uuid": "c1221d53-4d6b-4735-a39d-c8e9a4cd3492", - "label": null - }], - "input_connections": {}, - "tool_state": "{}", - "id": 0, - "uuid": "2d85b49e-97c0-4200-be9f-7b20d7546b7e", - "errors": null, - "name": "Input dataset", - "label": "big-file", - "inputs": [ - { - "description": "", - "name": "big-file" - } - ], - "position": { - "top": 133, - "left": 155.5 - }, - "annotation": "", - "content_id": null, - "type": "data_input" - }, - "1": { - "tool_id": "Show beginning1", - "tool_version": "1.0.0", - "outputs": [{ - "type": "input", - "name": "out_file1" - }], - "workflow_outputs": [{ - "output_name": "out_file1", - "uuid": "af594591-07e2-412e-840a-f6460edc1531", - "label": null - }], - "input_connections": { - "input": { - "output_name": "output", - "id": 0 - } - }, - "tool_state": "{\"__page__\": null, \"input\": \"{\\\"__class__\\\": \\\"RuntimeValue\\\"}\", \"__rerun_remap_job_id__\": null, \"lineNum\": \"\\\"1\\\"\"}", - "id": 1, - "uuid": "010ee8b9-bd5d-4f99-87b9-a7dad0e0f985", - "errors": null, - "name": "Select first", - "post_job_actions": {}, - "label": "Select first line ", - "inputs": [{ - "name": "input", - "description": "runtime parameter for tool Select first" - }], - "position": { - "top": 446, - "left": 481.5 - }, - "annotation": "", - "content_id": "Show beginning1", - "type": "tool" - } - }, - "annotation": "", - "a_galaxy_workflow": "true" -} diff --git a/deploy_condor_workflow.yml b/deploy_condor_workflow.yml deleted file mode 100644 index 0784904b..00000000 --- a/deploy_condor_workflow.yml +++ /dev/null @@ -1,11 +0,0 @@ -- hosts: all - tasks: - - name: Create folder for workflow - file: - path: "{{ jobs_directory_dir }}/{{ workflow_name }}" - state: directory - - name: Upload workflow-files - copy: - src: "{{ workflow_directory_path }}/" - dest: "{{ jobs_directory_dir }}/{{ workflow_name }}" - mode: 0700 \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 7fbb6577..b3a03aa5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,31 +2,30 @@ version: "3" services: benchmarker: build: . + environment: + - SSH_AUTH_SOCK=/ssh-agent volumes: - - .:/src + - type: bind + source: . + target: /src + - type: bind + source: ${SSH_AUTH_SOCK} + target: /ssh-agent + - type: bind + source: ${HOME}/.ssh + target: /root/.ssh-host + read_only: true working_dir: /src - entrypoint: python3 galaxy_benchmarker - depends_on: - - influxdb - influxdb: - image: influxdb:1.5 - ports: - - 8086:8086 - environment: - INFLUXDB_DATA_QUERY_LOG_ENABLED: "false" - INFLUXDB_HTTP_LOG_ENABLED: "false" - INFLUXDB_ADMIN_USER: admin - INFLUXDB_ADMIN_PASSWORD: admin - INFLUXDB_DB: glx_benchmarker - INFLUXDB_USER: glx_benchmarker - INFLUXDB_USER_PASSWORD: glx_benchmarker - volumes: - - .:/src - grafana: - image: grafana/grafana:6.5.0 - ports: - - 3000:3000 - volumes: - - ./grafana/config.ini:/etc/grafana/config.ini - - ./grafana/provisioning:/etc/grafana/provisioning - - ./grafana/dashboards:/var/lib/grafana/dashboards \ No newline at end of file + + command: [ + "--cfg", + "examples/06_benchmark_galaxy_1x0.yml", + "examples/06_benchmark_galaxy_1x1K.yml", + "examples/06_benchmark_galaxy_10x1K.yml", + "examples/06_benchmark_galaxy_100x1K.yml", + "examples/06_benchmark_galaxy_1000x1K.yml", + "examples/06_benchmark_galaxy_2000x1K.yml", + "examples/06_benchmark_galaxy_1x1M.yml", + "examples/06_benchmark_galaxy_1x1G.yml", + "examples/06_benchmark_galaxy_10x1G.yml", + ] diff --git a/evaluation/plots.ipynb b/evaluation/plots.ipynb new file mode 100644 index 00000000..0e745447 --- /dev/null +++ b/evaluation/plots.ipynb @@ -0,0 +1,22051 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "ebc0884d", + "metadata": {}, + "source": [ + "# Evaluation and plotting for different experiments\n", + "\n", + "- Write throughput (fio vs dd, isilon vs netapp)\n", + "- Containerized fio vs non-containerized fio" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "c777afc1", + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import annotations\n", + "import json\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import itertools\n", + "from pathlib import Path\n", + "import scipy.stats as st\n", + "from datetime import datetime, timedelta" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "3c021dc8", + "metadata": {}, + "outputs": [], + "source": [ + "METRIC = {\n", + " \"throughput_write\": \"write_bw_mean_in_MiB\",\n", + " \"iops_write\": \"write_iops_mean\",\n", + " \"latency_write\": \"write_lat_mean_in_ms\",\n", + " \"throughput_read\": \"read_bw_mean_in_MiB\",\n", + " \"iops_read\": \"read_iops_mean\",\n", + " \"latency_read\": \"read_lat_mean_in_ms\",\n", + "}\n", + "\n", + "def load_data(filename: Path | str, metric: str, suffix: str=\"\", x_labels=None) -> pd.DataFrame:\n", + " \"\"\"Load data either from file or from multiple files placed in a directory\"\"\"\n", + " path = Path(filename)\n", + " if path.is_file():\n", + " data = load_json(path)\n", + " elif path.is_dir():\n", + " data = load_dir(path, suffix)\n", + " else:\n", + " data = load_json(str(path)+suffix)\n", + " return extract_results(data, metric, x_labels)\n", + "\n", + "def load_json(filename: Path | str) -> dict:\n", + " \"\"\"Load file as json\"\"\"\n", + " file = Path(filename)\n", + " assert file.is_file(), f\"{file} is not a file\"\n", + " return json.loads(file.read_text())\n", + "\n", + "def load_dir(path: Path, suffix: str) -> dict:\n", + " \"\"\"Combine results of multiple files in directory. Specify files by suffix\"\"\"\n", + " res = {\"results\":{}}\n", + "\n", + " for file in path.glob(f\"*{suffix}\"):\n", + " data = load_json(file)\n", + "\n", + " for key, value in data[\"results\"].items():\n", + " if key not in res[\"results\"]:\n", + " res[\"results\"][key] = []\n", + " res[\"results\"][key].extend(value)\n", + "\n", + " return res\n", + "\n", + "def extract_results(data, metric, x_labels=None):\n", + " x_labels = x_labels if x_labels else list(data[\"results\"].keys())\n", + " pd_data = []\n", + " for label in x_labels:\n", + " results = data[\"results\"][label]\n", + " values = [float(r[metric]) for r in results]\n", + " values = [v for v in values if v > 0]\n", + " mean = np.mean(values)\n", + " conf = confidence_interval(values)\n", + " std = np.std(values)\n", + " num = len(values)\n", + " pd_data.append([mean, std, num])\n", + "\n", + "\n", + " df = pd.DataFrame(data=pd_data, index=x_labels, columns=[\"mean\", \"std\", \"num\"])\n", + " df.rename(index={\"1024\": 1, \"2048\": 2, \"4096\": 4, \"8192\": 8, \"16384\": 16}, inplace=True)\n", + " df.rename(index={\"256M\": 0.25, \"1G\": 1, \"2G\": 2, \"4G\": 4, \"8G\": 8, \"16G\": 16}, inplace=True)\n", + " df.rename(index={\"1k\": 1, \"4k\": 4, \"16k\": 16, \"64k\": 64, \"256k\": 256, \"1024k\": 1024, \"4096k\": 4096, \"16384k\": 16384}, inplace=True)\n", + " df.rename(index={\"1\": 1, \"2\": 2, \"4\": 4, \"8\": 8, \"16\": 16, \"32\": 32}, inplace=True)\n", + " df.rename(index={\"10\": 10, \"20\": 20, \"60\": 60, \"120\": 120, \"300\": 300}, inplace=True)\n", + "\n", + " return df\n", + "\n", + "def confidence_interval(values):\n", + " confidence = 0.95\n", + " dof = len(values)-1\n", + " m = np.mean(values)\n", + " sem = st.sem(values)\n", + " interval = st.t.interval(alpha=confidence, df=dof, loc=m, scale=sem)\n", + " if np.isnan(interval[0]) or np.isnan(interval[1]):\n", + " return m * 10**-6\n", + " return (interval[1]-interval[0])/2\n", + " \n", + "def plot(filename, metric=\"write_bw_mean_in_mb\"):\n", + " data = load_json(filename)\n", + " df = extract_results(data, metric=metric)\n", + " print(df)\n", + " ax = df[\"mean\"].plot(kind='bar', rot=0, xlabel='Date', ylabel='Value', title='My Plot', figsize=(6, 4), yerr=df[\"conf\"])\n", + " plt.show()\n", + "\n", + "def tex_table(lst):\n", + " for item in lst:\n", + " print(*item, sep=\" & \", end=\"\\\\\\\\\\n\")" + ] + }, + { + "cell_type": "markdown", + "id": "846f7df3", + "metadata": {}, + "source": [ + "# Experiment: Parameter-evaluation: dd" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "83992cb3", + "metadata": {}, + "outputs": [ + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "/* global mpl */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function () {\n", + " if (typeof WebSocket !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof MozWebSocket !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert(\n", + " 'Your browser does not have WebSocket support. ' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.'\n", + " );\n", + " }\n", + "};\n", + "\n", + "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = this.ws.binaryType !== undefined;\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById('mpl-warnings');\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent =\n", + " 'This browser does not support binary websocket messages. ' +\n", + " 'Performance may be slow.';\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = document.createElement('div');\n", + " this.root.setAttribute('style', 'display: inline-block');\n", + " this._root_extra_style(this.root);\n", + "\n", + " parent_element.appendChild(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message('supports_binary', { value: fig.supports_binary });\n", + " fig.send_message('send_image_mode', {});\n", + " if (fig.ratio !== 1) {\n", + " fig.send_message('set_device_pixel_ratio', {\n", + " device_pixel_ratio: fig.ratio,\n", + " });\n", + " }\n", + " fig.send_message('refresh', {});\n", + " };\n", + "\n", + " this.imageObj.onload = function () {\n", + " if (fig.image_mode === 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function () {\n", + " fig.ws.close();\n", + " };\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "};\n", + "\n", + "mpl.figure.prototype._init_header = function () {\n", + " var titlebar = document.createElement('div');\n", + " titlebar.classList =\n", + " 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n", + " var titletext = document.createElement('div');\n", + " titletext.classList = 'ui-dialog-title';\n", + " titletext.setAttribute(\n", + " 'style',\n", + " 'width: 100%; text-align: center; padding: 3px;'\n", + " );\n", + " titlebar.appendChild(titletext);\n", + " this.root.appendChild(titlebar);\n", + " this.header = titletext;\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._init_canvas = function () {\n", + " var fig = this;\n", + "\n", + " var canvas_div = (this.canvas_div = document.createElement('div'));\n", + " canvas_div.setAttribute(\n", + " 'style',\n", + " 'border: 1px solid #ddd;' +\n", + " 'box-sizing: content-box;' +\n", + " 'clear: both;' +\n", + " 'min-height: 1px;' +\n", + " 'min-width: 1px;' +\n", + " 'outline: 0;' +\n", + " 'overflow: hidden;' +\n", + " 'position: relative;' +\n", + " 'resize: both;'\n", + " );\n", + "\n", + " function on_keyboard_event_closure(name) {\n", + " return function (event) {\n", + " return fig.key_event(event, name);\n", + " };\n", + " }\n", + "\n", + " canvas_div.addEventListener(\n", + " 'keydown',\n", + " on_keyboard_event_closure('key_press')\n", + " );\n", + " canvas_div.addEventListener(\n", + " 'keyup',\n", + " on_keyboard_event_closure('key_release')\n", + " );\n", + "\n", + " this._canvas_extra_style(canvas_div);\n", + " this.root.appendChild(canvas_div);\n", + "\n", + " var canvas = (this.canvas = document.createElement('canvas'));\n", + " canvas.classList.add('mpl-canvas');\n", + " canvas.setAttribute('style', 'box-sizing: content-box;');\n", + "\n", + " this.context = canvas.getContext('2d');\n", + "\n", + " var backingStore =\n", + " this.context.backingStorePixelRatio ||\n", + " this.context.webkitBackingStorePixelRatio ||\n", + " this.context.mozBackingStorePixelRatio ||\n", + " this.context.msBackingStorePixelRatio ||\n", + " this.context.oBackingStorePixelRatio ||\n", + " this.context.backingStorePixelRatio ||\n", + " 1;\n", + "\n", + " this.ratio = (window.devicePixelRatio || 1) / backingStore;\n", + "\n", + " var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n", + " 'canvas'\n", + " ));\n", + " rubberband_canvas.setAttribute(\n", + " 'style',\n", + " 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n", + " );\n", + "\n", + " // Apply a ponyfill if ResizeObserver is not implemented by browser.\n", + " if (this.ResizeObserver === undefined) {\n", + " if (window.ResizeObserver !== undefined) {\n", + " this.ResizeObserver = window.ResizeObserver;\n", + " } else {\n", + " var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n", + " this.ResizeObserver = obs.ResizeObserver;\n", + " }\n", + " }\n", + "\n", + " this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n", + " var nentries = entries.length;\n", + " for (var i = 0; i < nentries; i++) {\n", + " var entry = entries[i];\n", + " var width, height;\n", + " if (entry.contentBoxSize) {\n", + " if (entry.contentBoxSize instanceof Array) {\n", + " // Chrome 84 implements new version of spec.\n", + " width = entry.contentBoxSize[0].inlineSize;\n", + " height = entry.contentBoxSize[0].blockSize;\n", + " } else {\n", + " // Firefox implements old version of spec.\n", + " width = entry.contentBoxSize.inlineSize;\n", + " height = entry.contentBoxSize.blockSize;\n", + " }\n", + " } else {\n", + " // Chrome <84 implements even older version of spec.\n", + " width = entry.contentRect.width;\n", + " height = entry.contentRect.height;\n", + " }\n", + "\n", + " // Keep the size of the canvas and rubber band canvas in sync with\n", + " // the canvas container.\n", + " if (entry.devicePixelContentBoxSize) {\n", + " // Chrome 84 implements new version of spec.\n", + " canvas.setAttribute(\n", + " 'width',\n", + " entry.devicePixelContentBoxSize[0].inlineSize\n", + " );\n", + " canvas.setAttribute(\n", + " 'height',\n", + " entry.devicePixelContentBoxSize[0].blockSize\n", + " );\n", + " } else {\n", + " canvas.setAttribute('width', width * fig.ratio);\n", + " canvas.setAttribute('height', height * fig.ratio);\n", + " }\n", + " canvas.setAttribute(\n", + " 'style',\n", + " 'width: ' + width + 'px; height: ' + height + 'px;'\n", + " );\n", + "\n", + " rubberband_canvas.setAttribute('width', width);\n", + " rubberband_canvas.setAttribute('height', height);\n", + "\n", + " // And update the size in Python. We ignore the initial 0/0 size\n", + " // that occurs as the element is placed into the DOM, which should\n", + " // otherwise not happen due to the minimum size styling.\n", + " if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n", + " fig.request_resize(width, height);\n", + " }\n", + " }\n", + " });\n", + " this.resizeObserverInstance.observe(canvas_div);\n", + "\n", + " function on_mouse_event_closure(name) {\n", + " return function (event) {\n", + " return fig.mouse_event(event, name);\n", + " };\n", + " }\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mousedown',\n", + " on_mouse_event_closure('button_press')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseup',\n", + " on_mouse_event_closure('button_release')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'dblclick',\n", + " on_mouse_event_closure('dblclick')\n", + " );\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband_canvas.addEventListener(\n", + " 'mousemove',\n", + " on_mouse_event_closure('motion_notify')\n", + " );\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseenter',\n", + " on_mouse_event_closure('figure_enter')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseleave',\n", + " on_mouse_event_closure('figure_leave')\n", + " );\n", + "\n", + " canvas_div.addEventListener('wheel', function (event) {\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " on_mouse_event_closure('scroll')(event);\n", + " });\n", + "\n", + " canvas_div.appendChild(canvas);\n", + " canvas_div.appendChild(rubberband_canvas);\n", + "\n", + " this.rubberband_context = rubberband_canvas.getContext('2d');\n", + " this.rubberband_context.strokeStyle = '#000000';\n", + "\n", + " this._resize_canvas = function (width, height, forward) {\n", + " if (forward) {\n", + " canvas_div.style.width = width + 'px';\n", + " canvas_div.style.height = height + 'px';\n", + " }\n", + " };\n", + "\n", + " // Disable right mouse context menu.\n", + " this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n", + " event.preventDefault();\n", + " return false;\n", + " });\n", + "\n", + " function set_focus() {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'mpl-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " continue;\n", + " }\n", + "\n", + " var button = (fig.buttons[name] = document.createElement('button'));\n", + " button.classList = 'mpl-widget';\n", + " button.setAttribute('role', 'button');\n", + " button.setAttribute('aria-disabled', 'false');\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + "\n", + " var icon_img = document.createElement('img');\n", + " icon_img.src = '_images/' + image + '.png';\n", + " icon_img.srcset = '_images/' + image + '_large.png 2x';\n", + " icon_img.alt = tooltip;\n", + " button.appendChild(icon_img);\n", + "\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " var fmt_picker = document.createElement('select');\n", + " fmt_picker.classList = 'mpl-widget';\n", + " toolbar.appendChild(fmt_picker);\n", + " this.format_dropdown = fmt_picker;\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = document.createElement('option');\n", + " option.selected = fmt === mpl.default_extension;\n", + " option.innerHTML = fmt;\n", + " fmt_picker.appendChild(option);\n", + " }\n", + "\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "};\n", + "\n", + "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', { width: x_pixels, height: y_pixels });\n", + "};\n", + "\n", + "mpl.figure.prototype.send_message = function (type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "};\n", + "\n", + "mpl.figure.prototype.send_draw_message = function () {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " var format_dropdown = fig.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " fig.ondownload(fig, format);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_resize = function (fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1], msg['forward']);\n", + " fig.send_message('refresh', {});\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n", + " var x0 = msg['x0'] / fig.ratio;\n", + " var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n", + " var x1 = msg['x1'] / fig.ratio;\n", + " var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0,\n", + " 0,\n", + " fig.canvas.width / fig.ratio,\n", + " fig.canvas.height / fig.ratio\n", + " );\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", + " fig.rubberband_canvas.style.cursor = msg['cursor'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_message = function (fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n", + " for (var key in msg) {\n", + " if (!(key in fig.buttons)) {\n", + " continue;\n", + " }\n", + " fig.buttons[key].disabled = !msg[key];\n", + " fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n", + " if (msg['mode'] === 'PAN') {\n", + " fig.buttons['Pan'].classList.add('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " } else if (msg['mode'] === 'ZOOM') {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.add('active');\n", + " } else {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message('ack', {});\n", + "};\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function (fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " var img = evt.data;\n", + " if (img.type !== 'image/png') {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " img.type = 'image/png';\n", + " }\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src\n", + " );\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " img\n", + " );\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " } else if (\n", + " typeof evt.data === 'string' &&\n", + " evt.data.slice(0, 21) === 'data:image/png;base64'\n", + " ) {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig['handle_' + msg_type];\n", + " } catch (e) {\n", + " console.log(\n", + " \"No handler for the '\" + msg_type + \"' message type: \",\n", + " msg\n", + " );\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\n", + " \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n", + " e,\n", + " e.stack,\n", + " msg\n", + " );\n", + " }\n", + " }\n", + " };\n", + "};\n", + "\n", + "// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function (e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e) {\n", + " e = window.event;\n", + " }\n", + " if (e.target) {\n", + " targ = e.target;\n", + " } else if (e.srcElement) {\n", + " targ = e.srcElement;\n", + " }\n", + " if (targ.nodeType === 3) {\n", + " // defeat Safari bug\n", + " targ = targ.parentNode;\n", + " }\n", + "\n", + " // pageX,Y are the mouse positions relative to the document\n", + " var boundingRect = targ.getBoundingClientRect();\n", + " var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n", + " var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n", + "\n", + " return { x: x, y: y };\n", + "};\n", + "\n", + "/*\n", + " * return a copy of an object with only non-object keys\n", + " * we need this to avoid circular references\n", + " * https://stackoverflow.com/a/24161582/3208463\n", + " */\n", + "function simpleKeys(original) {\n", + " return Object.keys(original).reduce(function (obj, key) {\n", + " if (typeof original[key] !== 'object') {\n", + " obj[key] = original[key];\n", + " }\n", + " return obj;\n", + " }, {});\n", + "}\n", + "\n", + "mpl.figure.prototype.mouse_event = function (event, name) {\n", + " var canvas_pos = mpl.findpos(event);\n", + "\n", + " if (name === 'button_press') {\n", + " this.canvas.focus();\n", + " this.canvas_div.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x * this.ratio;\n", + " var y = canvas_pos.y * this.ratio;\n", + "\n", + " this.send_message(name, {\n", + " x: x,\n", + " y: y,\n", + " button: event.button,\n", + " step: event.step,\n", + " guiEvent: simpleKeys(event),\n", + " });\n", + "\n", + " /* This prevents the web browser from automatically changing to\n", + " * the text insertion cursor when the button is pressed. We want\n", + " * to control all of the cursor setting manually through the\n", + " * 'cursor' event from matplotlib */\n", + " event.preventDefault();\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n", + " // Handle any extra behaviour associated with a key event\n", + "};\n", + "\n", + "mpl.figure.prototype.key_event = function (event, name) {\n", + " // Prevent repeat events\n", + " if (name === 'key_press') {\n", + " if (event.key === this._key) {\n", + " return;\n", + " } else {\n", + " this._key = event.key;\n", + " }\n", + " }\n", + " if (name === 'key_release') {\n", + " this._key = null;\n", + " }\n", + "\n", + " var value = '';\n", + " if (event.ctrlKey && event.key !== 'Control') {\n", + " value += 'ctrl+';\n", + " }\n", + " else if (event.altKey && event.key !== 'Alt') {\n", + " value += 'alt+';\n", + " }\n", + " else if (event.shiftKey && event.key !== 'Shift') {\n", + " value += 'shift+';\n", + " }\n", + "\n", + " value += 'k' + event.key;\n", + "\n", + " this._key_event_extra(event, name);\n", + "\n", + " this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n", + " if (name === 'download') {\n", + " this.handle_save(this, null);\n", + " } else {\n", + " this.send_message('toolbar_button', { name: name });\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n", + " this.message.textContent = tooltip;\n", + "};\n", + "\n", + "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", + "// prettier-ignore\n", + "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";/* global mpl */\n", + "\n", + "var comm_websocket_adapter = function (comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.binaryType = comm.kernel.ws.binaryType;\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " function updateReadyState(_event) {\n", + " if (comm.kernel.ws) {\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " } else {\n", + " ws.readyState = 3; // Closed state.\n", + " }\n", + " }\n", + " comm.kernel.ws.addEventListener('open', updateReadyState);\n", + " comm.kernel.ws.addEventListener('close', updateReadyState);\n", + " comm.kernel.ws.addEventListener('error', updateReadyState);\n", + "\n", + " ws.close = function () {\n", + " comm.close();\n", + " };\n", + " ws.send = function (m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function (msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " var data = msg['content']['data'];\n", + " if (data['blob'] !== undefined) {\n", + " data = {\n", + " data: new Blob(msg['buffers'], { type: data['blob'] }),\n", + " };\n", + " }\n", + " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", + " ws.onmessage(data);\n", + " });\n", + " return ws;\n", + "};\n", + "\n", + "mpl.mpl_figure_comm = function (comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = document.getElementById(id);\n", + " var ws_proxy = comm_websocket_adapter(comm);\n", + "\n", + " function ondownload(figure, _format) {\n", + " window.open(figure.canvas.toDataURL());\n", + " }\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element;\n", + " fig.cell_info = mpl.find_output_cell(\"
\");\n", + " if (!fig.cell_info) {\n", + " console.error('Failed to find cell for figure', id, fig);\n", + " return;\n", + " }\n", + " fig.cell_info[0].output_area.element.on(\n", + " 'cleared',\n", + " { fig: fig },\n", + " fig._remove_fig_handler\n", + " );\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function (fig, msg) {\n", + " var width = fig.canvas.width / fig.ratio;\n", + " fig.cell_info[0].output_area.element.off(\n", + " 'cleared',\n", + " fig._remove_fig_handler\n", + " );\n", + " fig.resizeObserverInstance.unobserve(fig.canvas_div);\n", + "\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable();\n", + " fig.parent_element.innerHTML =\n", + " '';\n", + " fig.close_ws(fig, msg);\n", + "};\n", + "\n", + "mpl.figure.prototype.close_ws = function (fig, msg) {\n", + " fig.send_message('closing', msg);\n", + " // fig.ws.close()\n", + "};\n", + "\n", + "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n", + " // Turn the data on the canvas into data in the output cell.\n", + " var width = this.canvas.width / this.ratio;\n", + " var dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] =\n", + " '';\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message('ack', {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () {\n", + " fig.push_to_output();\n", + " }, 1000);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'btn-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " var button;\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " continue;\n", + " }\n", + "\n", + " button = fig.buttons[name] = document.createElement('button');\n", + " button.classList = 'btn btn-default';\n", + " button.href = '#';\n", + " button.title = name;\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message pull-right';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = document.createElement('div');\n", + " buttongrp.classList = 'btn-group inline pull-right';\n", + " button = document.createElement('button');\n", + " button.classList = 'btn btn-mini btn-primary';\n", + " button.href = '#';\n", + " button.title = 'Stop Interaction';\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', function (_evt) {\n", + " fig.handle_close(fig, {});\n", + " });\n", + " button.addEventListener(\n", + " 'mouseover',\n", + " on_mouseover_closure('Stop Interaction')\n", + " );\n", + " buttongrp.appendChild(button);\n", + " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", + " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", + "};\n", + "\n", + "mpl.figure.prototype._remove_fig_handler = function (event) {\n", + " var fig = event.data.fig;\n", + " if (event.target !== this) {\n", + " // Ignore bubbled events from children.\n", + " return;\n", + " }\n", + " fig.close_ws(fig, {});\n", + "};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (el) {\n", + " el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (el) {\n", + " // this is important to make the div 'focusable\n", + " el.setAttribute('tabindex', 0);\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " } else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which === 13) {\n", + " this.canvas_div.blur();\n", + " // select the cell after this one\n", + " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", + " IPython.notebook.select(index + 1);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " fig.ondownload(fig, null);\n", + "};\n", + "\n", + "mpl.find_output_cell = function (html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i = 0; i < ncells; i++) {\n", + " var cell = cells[i];\n", + " if (cell.cell_type === 'code') {\n", + " for (var j = 0; j < cell.output_area.outputs.length; j++) {\n", + " var data = cell.output_area.outputs[j];\n", + " if (data.data) {\n", + " // IPython >= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] === html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "};\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel !== null) {\n", + " IPython.notebook.kernel.comm_manager.register_target(\n", + " 'matplotlib',\n", + " mpl.mpl_figure_comm\n", + " );\n", + "}\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%matplotlib notebook\n", + "\n", + "# Read in results\n", + "## Params - Either specify a file prefix or a whole folder\n", + "file_prefix = None # Path(\"./results/2022-07-05T20:00:06_throughput_write_dd_\")\n", + "folder = Path(\"./results/param_study_dd\")\n", + "\n", + "## Plot code\n", + "path = file_prefix if file_prefix else folder\n", + "\n", + "df_isilon = pd.concat({\n", + " \"Single\": load_data(path, metric=\"bw_in_MiB\", suffix=\"isilon_a.json\"),\n", + " \"Parallel\": load_data(path, metric=\"bw_in_MiB\", suffix=\"isilon_b.json\"),\n", + "})\n", + "\n", + "df_netapp = pd.concat({\n", + " \"Single\": load_data(path, metric=\"bw_in_MiB\", suffix=\"netapp_a.json\"),\n", + " \"Parallel\": load_data(path, metric=\"bw_in_MiB\", suffix=\"netapp_b.json\"),\n", + "})\n", + "\n", + "fig, (ax1, ax2) = plt.subplots(1, 2, sharey=True)\n", + "fig.suptitle('Parameter Study: dd')\n", + "\n", + "df_isilon[\"mean\"].unstack(level=0)[[\"Single\",\"Parallel\"]].plot(\n", + " ax=ax1,\n", + " kind='bar', rot=0,\n", + " xlabel='Transferred Data (GiB)',\n", + " ylabel='Bandwidth (MiB/s)',\n", + " title=f\"nfs_isilon\",\n", + " figsize=(8, 4),\n", + " yerr=df_isilon[\"std\"].unstack(level=0)[[\"Single\",\"Parallel\"]]\n", + ")\n", + "\n", + "df_netapp[\"mean\"].unstack(level=0)[[\"Single\",\"Parallel\"]].plot(\n", + " ax=ax2,\n", + " kind='bar', rot=0,\n", + " xlabel='Transferred Data (GiB)',\n", + " ylabel='Bandwidth (MiB/s)',\n", + " title=f\"nfs_netapp\",\n", + " figsize=(8, 4),\n", + " yerr=df_netapp[\"std\"].unstack(level=0)[[\"Single\",\"Parallel\"]],\n", + ")\n", + "\n", + "ax1.get_legend().remove()\n", + "ax2.legend(loc=\"upper left\")\n", + "plt.tight_layout()\n", + "plt.savefig(\"paramstudy_dd.png\",bbox_inches='tight')\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "1f2c0e77", + "metadata": {}, + "source": [ + "# Experiment: Parameter-evaluation: Fio" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "b1f156a9", + "metadata": {}, + "outputs": [ + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "/* global mpl */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function () {\n", + " if (typeof WebSocket !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof MozWebSocket !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert(\n", + " 'Your browser does not have WebSocket support. ' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.'\n", + " );\n", + " }\n", + "};\n", + "\n", + "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = this.ws.binaryType !== undefined;\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById('mpl-warnings');\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent =\n", + " 'This browser does not support binary websocket messages. ' +\n", + " 'Performance may be slow.';\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = document.createElement('div');\n", + " this.root.setAttribute('style', 'display: inline-block');\n", + " this._root_extra_style(this.root);\n", + "\n", + " parent_element.appendChild(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message('supports_binary', { value: fig.supports_binary });\n", + " fig.send_message('send_image_mode', {});\n", + " if (fig.ratio !== 1) {\n", + " fig.send_message('set_device_pixel_ratio', {\n", + " device_pixel_ratio: fig.ratio,\n", + " });\n", + " }\n", + " fig.send_message('refresh', {});\n", + " };\n", + "\n", + " this.imageObj.onload = function () {\n", + " if (fig.image_mode === 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function () {\n", + " fig.ws.close();\n", + " };\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "};\n", + "\n", + "mpl.figure.prototype._init_header = function () {\n", + " var titlebar = document.createElement('div');\n", + " titlebar.classList =\n", + " 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n", + " var titletext = document.createElement('div');\n", + " titletext.classList = 'ui-dialog-title';\n", + " titletext.setAttribute(\n", + " 'style',\n", + " 'width: 100%; text-align: center; padding: 3px;'\n", + " );\n", + " titlebar.appendChild(titletext);\n", + " this.root.appendChild(titlebar);\n", + " this.header = titletext;\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._init_canvas = function () {\n", + " var fig = this;\n", + "\n", + " var canvas_div = (this.canvas_div = document.createElement('div'));\n", + " canvas_div.setAttribute(\n", + " 'style',\n", + " 'border: 1px solid #ddd;' +\n", + " 'box-sizing: content-box;' +\n", + " 'clear: both;' +\n", + " 'min-height: 1px;' +\n", + " 'min-width: 1px;' +\n", + " 'outline: 0;' +\n", + " 'overflow: hidden;' +\n", + " 'position: relative;' +\n", + " 'resize: both;'\n", + " );\n", + "\n", + " function on_keyboard_event_closure(name) {\n", + " return function (event) {\n", + " return fig.key_event(event, name);\n", + " };\n", + " }\n", + "\n", + " canvas_div.addEventListener(\n", + " 'keydown',\n", + " on_keyboard_event_closure('key_press')\n", + " );\n", + " canvas_div.addEventListener(\n", + " 'keyup',\n", + " on_keyboard_event_closure('key_release')\n", + " );\n", + "\n", + " this._canvas_extra_style(canvas_div);\n", + " this.root.appendChild(canvas_div);\n", + "\n", + " var canvas = (this.canvas = document.createElement('canvas'));\n", + " canvas.classList.add('mpl-canvas');\n", + " canvas.setAttribute('style', 'box-sizing: content-box;');\n", + "\n", + " this.context = canvas.getContext('2d');\n", + "\n", + " var backingStore =\n", + " this.context.backingStorePixelRatio ||\n", + " this.context.webkitBackingStorePixelRatio ||\n", + " this.context.mozBackingStorePixelRatio ||\n", + " this.context.msBackingStorePixelRatio ||\n", + " this.context.oBackingStorePixelRatio ||\n", + " this.context.backingStorePixelRatio ||\n", + " 1;\n", + "\n", + " this.ratio = (window.devicePixelRatio || 1) / backingStore;\n", + "\n", + " var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n", + " 'canvas'\n", + " ));\n", + " rubberband_canvas.setAttribute(\n", + " 'style',\n", + " 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n", + " );\n", + "\n", + " // Apply a ponyfill if ResizeObserver is not implemented by browser.\n", + " if (this.ResizeObserver === undefined) {\n", + " if (window.ResizeObserver !== undefined) {\n", + " this.ResizeObserver = window.ResizeObserver;\n", + " } else {\n", + " var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n", + " this.ResizeObserver = obs.ResizeObserver;\n", + " }\n", + " }\n", + "\n", + " this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n", + " var nentries = entries.length;\n", + " for (var i = 0; i < nentries; i++) {\n", + " var entry = entries[i];\n", + " var width, height;\n", + " if (entry.contentBoxSize) {\n", + " if (entry.contentBoxSize instanceof Array) {\n", + " // Chrome 84 implements new version of spec.\n", + " width = entry.contentBoxSize[0].inlineSize;\n", + " height = entry.contentBoxSize[0].blockSize;\n", + " } else {\n", + " // Firefox implements old version of spec.\n", + " width = entry.contentBoxSize.inlineSize;\n", + " height = entry.contentBoxSize.blockSize;\n", + " }\n", + " } else {\n", + " // Chrome <84 implements even older version of spec.\n", + " width = entry.contentRect.width;\n", + " height = entry.contentRect.height;\n", + " }\n", + "\n", + " // Keep the size of the canvas and rubber band canvas in sync with\n", + " // the canvas container.\n", + " if (entry.devicePixelContentBoxSize) {\n", + " // Chrome 84 implements new version of spec.\n", + " canvas.setAttribute(\n", + " 'width',\n", + " entry.devicePixelContentBoxSize[0].inlineSize\n", + " );\n", + " canvas.setAttribute(\n", + " 'height',\n", + " entry.devicePixelContentBoxSize[0].blockSize\n", + " );\n", + " } else {\n", + " canvas.setAttribute('width', width * fig.ratio);\n", + " canvas.setAttribute('height', height * fig.ratio);\n", + " }\n", + " canvas.setAttribute(\n", + " 'style',\n", + " 'width: ' + width + 'px; height: ' + height + 'px;'\n", + " );\n", + "\n", + " rubberband_canvas.setAttribute('width', width);\n", + " rubberband_canvas.setAttribute('height', height);\n", + "\n", + " // And update the size in Python. We ignore the initial 0/0 size\n", + " // that occurs as the element is placed into the DOM, which should\n", + " // otherwise not happen due to the minimum size styling.\n", + " if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n", + " fig.request_resize(width, height);\n", + " }\n", + " }\n", + " });\n", + " this.resizeObserverInstance.observe(canvas_div);\n", + "\n", + " function on_mouse_event_closure(name) {\n", + " return function (event) {\n", + " return fig.mouse_event(event, name);\n", + " };\n", + " }\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mousedown',\n", + " on_mouse_event_closure('button_press')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseup',\n", + " on_mouse_event_closure('button_release')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'dblclick',\n", + " on_mouse_event_closure('dblclick')\n", + " );\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband_canvas.addEventListener(\n", + " 'mousemove',\n", + " on_mouse_event_closure('motion_notify')\n", + " );\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseenter',\n", + " on_mouse_event_closure('figure_enter')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseleave',\n", + " on_mouse_event_closure('figure_leave')\n", + " );\n", + "\n", + " canvas_div.addEventListener('wheel', function (event) {\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " on_mouse_event_closure('scroll')(event);\n", + " });\n", + "\n", + " canvas_div.appendChild(canvas);\n", + " canvas_div.appendChild(rubberband_canvas);\n", + "\n", + " this.rubberband_context = rubberband_canvas.getContext('2d');\n", + " this.rubberband_context.strokeStyle = '#000000';\n", + "\n", + " this._resize_canvas = function (width, height, forward) {\n", + " if (forward) {\n", + " canvas_div.style.width = width + 'px';\n", + " canvas_div.style.height = height + 'px';\n", + " }\n", + " };\n", + "\n", + " // Disable right mouse context menu.\n", + " this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n", + " event.preventDefault();\n", + " return false;\n", + " });\n", + "\n", + " function set_focus() {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'mpl-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " continue;\n", + " }\n", + "\n", + " var button = (fig.buttons[name] = document.createElement('button'));\n", + " button.classList = 'mpl-widget';\n", + " button.setAttribute('role', 'button');\n", + " button.setAttribute('aria-disabled', 'false');\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + "\n", + " var icon_img = document.createElement('img');\n", + " icon_img.src = '_images/' + image + '.png';\n", + " icon_img.srcset = '_images/' + image + '_large.png 2x';\n", + " icon_img.alt = tooltip;\n", + " button.appendChild(icon_img);\n", + "\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " var fmt_picker = document.createElement('select');\n", + " fmt_picker.classList = 'mpl-widget';\n", + " toolbar.appendChild(fmt_picker);\n", + " this.format_dropdown = fmt_picker;\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = document.createElement('option');\n", + " option.selected = fmt === mpl.default_extension;\n", + " option.innerHTML = fmt;\n", + " fmt_picker.appendChild(option);\n", + " }\n", + "\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "};\n", + "\n", + "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', { width: x_pixels, height: y_pixels });\n", + "};\n", + "\n", + "mpl.figure.prototype.send_message = function (type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "};\n", + "\n", + "mpl.figure.prototype.send_draw_message = function () {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " var format_dropdown = fig.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " fig.ondownload(fig, format);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_resize = function (fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1], msg['forward']);\n", + " fig.send_message('refresh', {});\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n", + " var x0 = msg['x0'] / fig.ratio;\n", + " var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n", + " var x1 = msg['x1'] / fig.ratio;\n", + " var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0,\n", + " 0,\n", + " fig.canvas.width / fig.ratio,\n", + " fig.canvas.height / fig.ratio\n", + " );\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", + " fig.rubberband_canvas.style.cursor = msg['cursor'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_message = function (fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n", + " for (var key in msg) {\n", + " if (!(key in fig.buttons)) {\n", + " continue;\n", + " }\n", + " fig.buttons[key].disabled = !msg[key];\n", + " fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n", + " if (msg['mode'] === 'PAN') {\n", + " fig.buttons['Pan'].classList.add('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " } else if (msg['mode'] === 'ZOOM') {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.add('active');\n", + " } else {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message('ack', {});\n", + "};\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function (fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " var img = evt.data;\n", + " if (img.type !== 'image/png') {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " img.type = 'image/png';\n", + " }\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src\n", + " );\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " img\n", + " );\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " } else if (\n", + " typeof evt.data === 'string' &&\n", + " evt.data.slice(0, 21) === 'data:image/png;base64'\n", + " ) {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig['handle_' + msg_type];\n", + " } catch (e) {\n", + " console.log(\n", + " \"No handler for the '\" + msg_type + \"' message type: \",\n", + " msg\n", + " );\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\n", + " \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n", + " e,\n", + " e.stack,\n", + " msg\n", + " );\n", + " }\n", + " }\n", + " };\n", + "};\n", + "\n", + "// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function (e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e) {\n", + " e = window.event;\n", + " }\n", + " if (e.target) {\n", + " targ = e.target;\n", + " } else if (e.srcElement) {\n", + " targ = e.srcElement;\n", + " }\n", + " if (targ.nodeType === 3) {\n", + " // defeat Safari bug\n", + " targ = targ.parentNode;\n", + " }\n", + "\n", + " // pageX,Y are the mouse positions relative to the document\n", + " var boundingRect = targ.getBoundingClientRect();\n", + " var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n", + " var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n", + "\n", + " return { x: x, y: y };\n", + "};\n", + "\n", + "/*\n", + " * return a copy of an object with only non-object keys\n", + " * we need this to avoid circular references\n", + " * https://stackoverflow.com/a/24161582/3208463\n", + " */\n", + "function simpleKeys(original) {\n", + " return Object.keys(original).reduce(function (obj, key) {\n", + " if (typeof original[key] !== 'object') {\n", + " obj[key] = original[key];\n", + " }\n", + " return obj;\n", + " }, {});\n", + "}\n", + "\n", + "mpl.figure.prototype.mouse_event = function (event, name) {\n", + " var canvas_pos = mpl.findpos(event);\n", + "\n", + " if (name === 'button_press') {\n", + " this.canvas.focus();\n", + " this.canvas_div.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x * this.ratio;\n", + " var y = canvas_pos.y * this.ratio;\n", + "\n", + " this.send_message(name, {\n", + " x: x,\n", + " y: y,\n", + " button: event.button,\n", + " step: event.step,\n", + " guiEvent: simpleKeys(event),\n", + " });\n", + "\n", + " /* This prevents the web browser from automatically changing to\n", + " * the text insertion cursor when the button is pressed. We want\n", + " * to control all of the cursor setting manually through the\n", + " * 'cursor' event from matplotlib */\n", + " event.preventDefault();\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n", + " // Handle any extra behaviour associated with a key event\n", + "};\n", + "\n", + "mpl.figure.prototype.key_event = function (event, name) {\n", + " // Prevent repeat events\n", + " if (name === 'key_press') {\n", + " if (event.key === this._key) {\n", + " return;\n", + " } else {\n", + " this._key = event.key;\n", + " }\n", + " }\n", + " if (name === 'key_release') {\n", + " this._key = null;\n", + " }\n", + "\n", + " var value = '';\n", + " if (event.ctrlKey && event.key !== 'Control') {\n", + " value += 'ctrl+';\n", + " }\n", + " else if (event.altKey && event.key !== 'Alt') {\n", + " value += 'alt+';\n", + " }\n", + " else if (event.shiftKey && event.key !== 'Shift') {\n", + " value += 'shift+';\n", + " }\n", + "\n", + " value += 'k' + event.key;\n", + "\n", + " this._key_event_extra(event, name);\n", + "\n", + " this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n", + " if (name === 'download') {\n", + " this.handle_save(this, null);\n", + " } else {\n", + " this.send_message('toolbar_button', { name: name });\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n", + " this.message.textContent = tooltip;\n", + "};\n", + "\n", + "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", + "// prettier-ignore\n", + "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";/* global mpl */\n", + "\n", + "var comm_websocket_adapter = function (comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.binaryType = comm.kernel.ws.binaryType;\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " function updateReadyState(_event) {\n", + " if (comm.kernel.ws) {\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " } else {\n", + " ws.readyState = 3; // Closed state.\n", + " }\n", + " }\n", + " comm.kernel.ws.addEventListener('open', updateReadyState);\n", + " comm.kernel.ws.addEventListener('close', updateReadyState);\n", + " comm.kernel.ws.addEventListener('error', updateReadyState);\n", + "\n", + " ws.close = function () {\n", + " comm.close();\n", + " };\n", + " ws.send = function (m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function (msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " var data = msg['content']['data'];\n", + " if (data['blob'] !== undefined) {\n", + " data = {\n", + " data: new Blob(msg['buffers'], { type: data['blob'] }),\n", + " };\n", + " }\n", + " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", + " ws.onmessage(data);\n", + " });\n", + " return ws;\n", + "};\n", + "\n", + "mpl.mpl_figure_comm = function (comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = document.getElementById(id);\n", + " var ws_proxy = comm_websocket_adapter(comm);\n", + "\n", + " function ondownload(figure, _format) {\n", + " window.open(figure.canvas.toDataURL());\n", + " }\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element;\n", + " fig.cell_info = mpl.find_output_cell(\"
\");\n", + " if (!fig.cell_info) {\n", + " console.error('Failed to find cell for figure', id, fig);\n", + " return;\n", + " }\n", + " fig.cell_info[0].output_area.element.on(\n", + " 'cleared',\n", + " { fig: fig },\n", + " fig._remove_fig_handler\n", + " );\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function (fig, msg) {\n", + " var width = fig.canvas.width / fig.ratio;\n", + " fig.cell_info[0].output_area.element.off(\n", + " 'cleared',\n", + " fig._remove_fig_handler\n", + " );\n", + " fig.resizeObserverInstance.unobserve(fig.canvas_div);\n", + "\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable();\n", + " fig.parent_element.innerHTML =\n", + " '';\n", + " fig.close_ws(fig, msg);\n", + "};\n", + "\n", + "mpl.figure.prototype.close_ws = function (fig, msg) {\n", + " fig.send_message('closing', msg);\n", + " // fig.ws.close()\n", + "};\n", + "\n", + "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n", + " // Turn the data on the canvas into data in the output cell.\n", + " var width = this.canvas.width / this.ratio;\n", + " var dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] =\n", + " '';\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message('ack', {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () {\n", + " fig.push_to_output();\n", + " }, 1000);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'btn-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " var button;\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " continue;\n", + " }\n", + "\n", + " button = fig.buttons[name] = document.createElement('button');\n", + " button.classList = 'btn btn-default';\n", + " button.href = '#';\n", + " button.title = name;\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message pull-right';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = document.createElement('div');\n", + " buttongrp.classList = 'btn-group inline pull-right';\n", + " button = document.createElement('button');\n", + " button.classList = 'btn btn-mini btn-primary';\n", + " button.href = '#';\n", + " button.title = 'Stop Interaction';\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', function (_evt) {\n", + " fig.handle_close(fig, {});\n", + " });\n", + " button.addEventListener(\n", + " 'mouseover',\n", + " on_mouseover_closure('Stop Interaction')\n", + " );\n", + " buttongrp.appendChild(button);\n", + " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", + " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", + "};\n", + "\n", + "mpl.figure.prototype._remove_fig_handler = function (event) {\n", + " var fig = event.data.fig;\n", + " if (event.target !== this) {\n", + " // Ignore bubbled events from children.\n", + " return;\n", + " }\n", + " fig.close_ws(fig, {});\n", + "};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (el) {\n", + " el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (el) {\n", + " // this is important to make the div 'focusable\n", + " el.setAttribute('tabindex', 0);\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " } else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which === 13) {\n", + " this.canvas_div.blur();\n", + " // select the cell after this one\n", + " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", + " IPython.notebook.select(index + 1);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " fig.ondownload(fig, null);\n", + "};\n", + "\n", + "mpl.find_output_cell = function (html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i = 0; i < ncells; i++) {\n", + " var cell = cells[i];\n", + " if (cell.cell_type === 'code') {\n", + " for (var j = 0; j < cell.output_area.outputs.length; j++) {\n", + " var data = cell.output_area.outputs[j];\n", + " if (data.data) {\n", + " // IPython >= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] === html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "};\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel !== null) {\n", + " IPython.notebook.kernel.comm_manager.register_target(\n", + " 'matplotlib',\n", + " mpl.mpl_figure_comm\n", + " );\n", + "}\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%matplotlib notebook\n", + "\n", + "# Read in results\n", + "## Params - Either specify a file prefix or a whole folder\n", + "file_prefix = None # Path(\"./results/2022-05-17T21:18:41_throughput_write_\")\n", + "folder = Path(\"./results/param_study_fio_throughput\")\n", + "\n", + "## Plot code\n", + "path = file_prefix if file_prefix else folder\n", + "\n", + "\n", + "post_i = \"_isilon.json\"\n", + "post_n = \"_netapp.json\"\n", + "metric = METRIC[\"throughput_write\"]\n", + "\n", + "df_1 = pd.concat({\n", + " \"nfs_isilon\": load_data(path, suffix=\"blocksize\"+post_i, metric=metric),\n", + " \"nfs_netapp\": load_data(path, suffix=\"blocksize\"+post_n, metric=metric),\n", + "})\n", + "df_2 = pd.concat({\n", + " \"nfs_isilon\": load_data(path, suffix=\"iodepth\"+post_i, metric=metric),\n", + " \"nfs_netapp\": load_data(path, suffix=\"iodepth\"+post_n, metric=metric),\n", + "})\n", + "df_3 = pd.concat({\n", + " \"nfs_isilon\": load_data(path, suffix=\"runtime\"+post_i, metric=metric),\n", + " \"nfs_netapp\": load_data(path, suffix=\"runtime\"+post_n, metric=metric),\n", + "})\n", + "df_4 = pd.concat({\n", + " \"nfs_isilon\": load_data(path, suffix=\"ramptime\"+post_i, metric=metric),\n", + " \"nfs_netapp\": load_data(path, suffix=\"ramptime\"+post_n, metric=metric),\n", + "})\n", + "df_5 = pd.concat({\n", + " \"nfs_isilon\": load_data(path, suffix=\"numjobs\"+post_i, metric=metric),\n", + " \"nfs_netapp\": load_data(path, suffix=\"numjobs\"+post_n, metric=metric),\n", + "})\n", + "df_6 = pd.concat({\n", + " \"nfs_isilon\": load_data(path, suffix=\"filesize\"+post_i, metric=metric),\n", + " \"nfs_netapp\": load_data(path, suffix=\"filesize\"+post_n, metric=metric),\n", + "})\n", + "\n", + "fig, axis = plt.subplots(3, 2, sharey=True)\n", + "fig.suptitle('Parameter Study: Fio Write Bandwidth')\n", + "\n", + "color = [\"tab:red\", \"tab:cyan\"]\n", + " \n", + "df_1[\"mean\"].unstack(level=0).plot(\n", + " ax=axis[0][0],\n", + " kind='bar', rot=0,\n", + " xlabel='Blocksize in KiB',\n", + " title='Variable Blocksize',\n", + " yerr=df_1[\"std\"].unstack(level=0),\n", + " color=color,\n", + ")\n", + "df_2[\"mean\"].unstack(level=0).plot(\n", + " ax=axis[0][1],\n", + " kind='bar', rot=0,\n", + " xlabel='IODepth',\n", + " title='Variable IODepth',\n", + " yerr=df_2[\"std\"].unstack(level=0),\n", + " color=color,\n", + ")\n", + "\n", + "df_3[\"mean\"].unstack(level=0).plot(\n", + " ax=axis[1][0],\n", + " kind='bar', rot=0,\n", + " xlabel='Time in s',\n", + " ylabel='Bandwidth (MiB/s)',\n", + " title='Variable Runtime',\n", + " yerr=df_3[\"std\"].unstack(level=0),\n", + " color=color,\n", + ")\n", + "\n", + "df_4[\"mean\"].unstack(level=0).plot(\n", + " ax=axis[1][1],\n", + " kind='bar', rot=0,\n", + " xlabel='Time in s',\n", + " title='Variable Ramptime',\n", + " yerr=df_4[\"std\"].unstack(level=0),\n", + " color=color,\n", + ")\n", + "df_5[\"mean\"].unstack(level=0).plot(\n", + " ax=axis[2][0],\n", + " kind='bar', rot=0,\n", + " xlabel='Jobs',\n", + " title='Variable Number of Jobs',\n", + " yerr=df_5[\"std\"].unstack(level=0),\n", + " color=color,\n", + ")\n", + "df_6[\"mean\"].unstack(level=0).plot(\n", + " ax=axis[2][1],\n", + " kind='bar', rot=0,\n", + " xlabel='Filesize in GiB',\n", + " title='Variable Filesize',\n", + " yerr=df_6[\"std\"].unstack(level=0),\n", + " color=color,\n", + ")\n", + "for i,j in [(0,0),(0,1),(1,0),(1,1),(2,0)]:\n", + " axis[i][j].get_legend().remove()\n", + " \n", + "plt.tight_layout()\n", + "plt.savefig(\"paramstudy_fio_throughput.png\",bbox_inches='tight')\n", + "plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "5a161036", + "metadata": {}, + "outputs": [ + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "/* global mpl */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function () {\n", + " if (typeof WebSocket !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof MozWebSocket !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert(\n", + " 'Your browser does not have WebSocket support. ' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.'\n", + " );\n", + " }\n", + "};\n", + "\n", + "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = this.ws.binaryType !== undefined;\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById('mpl-warnings');\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent =\n", + " 'This browser does not support binary websocket messages. ' +\n", + " 'Performance may be slow.';\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = document.createElement('div');\n", + " this.root.setAttribute('style', 'display: inline-block');\n", + " this._root_extra_style(this.root);\n", + "\n", + " parent_element.appendChild(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message('supports_binary', { value: fig.supports_binary });\n", + " fig.send_message('send_image_mode', {});\n", + " if (fig.ratio !== 1) {\n", + " fig.send_message('set_device_pixel_ratio', {\n", + " device_pixel_ratio: fig.ratio,\n", + " });\n", + " }\n", + " fig.send_message('refresh', {});\n", + " };\n", + "\n", + " this.imageObj.onload = function () {\n", + " if (fig.image_mode === 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function () {\n", + " fig.ws.close();\n", + " };\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "};\n", + "\n", + "mpl.figure.prototype._init_header = function () {\n", + " var titlebar = document.createElement('div');\n", + " titlebar.classList =\n", + " 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n", + " var titletext = document.createElement('div');\n", + " titletext.classList = 'ui-dialog-title';\n", + " titletext.setAttribute(\n", + " 'style',\n", + " 'width: 100%; text-align: center; padding: 3px;'\n", + " );\n", + " titlebar.appendChild(titletext);\n", + " this.root.appendChild(titlebar);\n", + " this.header = titletext;\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._init_canvas = function () {\n", + " var fig = this;\n", + "\n", + " var canvas_div = (this.canvas_div = document.createElement('div'));\n", + " canvas_div.setAttribute(\n", + " 'style',\n", + " 'border: 1px solid #ddd;' +\n", + " 'box-sizing: content-box;' +\n", + " 'clear: both;' +\n", + " 'min-height: 1px;' +\n", + " 'min-width: 1px;' +\n", + " 'outline: 0;' +\n", + " 'overflow: hidden;' +\n", + " 'position: relative;' +\n", + " 'resize: both;'\n", + " );\n", + "\n", + " function on_keyboard_event_closure(name) {\n", + " return function (event) {\n", + " return fig.key_event(event, name);\n", + " };\n", + " }\n", + "\n", + " canvas_div.addEventListener(\n", + " 'keydown',\n", + " on_keyboard_event_closure('key_press')\n", + " );\n", + " canvas_div.addEventListener(\n", + " 'keyup',\n", + " on_keyboard_event_closure('key_release')\n", + " );\n", + "\n", + " this._canvas_extra_style(canvas_div);\n", + " this.root.appendChild(canvas_div);\n", + "\n", + " var canvas = (this.canvas = document.createElement('canvas'));\n", + " canvas.classList.add('mpl-canvas');\n", + " canvas.setAttribute('style', 'box-sizing: content-box;');\n", + "\n", + " this.context = canvas.getContext('2d');\n", + "\n", + " var backingStore =\n", + " this.context.backingStorePixelRatio ||\n", + " this.context.webkitBackingStorePixelRatio ||\n", + " this.context.mozBackingStorePixelRatio ||\n", + " this.context.msBackingStorePixelRatio ||\n", + " this.context.oBackingStorePixelRatio ||\n", + " this.context.backingStorePixelRatio ||\n", + " 1;\n", + "\n", + " this.ratio = (window.devicePixelRatio || 1) / backingStore;\n", + "\n", + " var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n", + " 'canvas'\n", + " ));\n", + " rubberband_canvas.setAttribute(\n", + " 'style',\n", + " 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n", + " );\n", + "\n", + " // Apply a ponyfill if ResizeObserver is not implemented by browser.\n", + " if (this.ResizeObserver === undefined) {\n", + " if (window.ResizeObserver !== undefined) {\n", + " this.ResizeObserver = window.ResizeObserver;\n", + " } else {\n", + " var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n", + " this.ResizeObserver = obs.ResizeObserver;\n", + " }\n", + " }\n", + "\n", + " this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n", + " var nentries = entries.length;\n", + " for (var i = 0; i < nentries; i++) {\n", + " var entry = entries[i];\n", + " var width, height;\n", + " if (entry.contentBoxSize) {\n", + " if (entry.contentBoxSize instanceof Array) {\n", + " // Chrome 84 implements new version of spec.\n", + " width = entry.contentBoxSize[0].inlineSize;\n", + " height = entry.contentBoxSize[0].blockSize;\n", + " } else {\n", + " // Firefox implements old version of spec.\n", + " width = entry.contentBoxSize.inlineSize;\n", + " height = entry.contentBoxSize.blockSize;\n", + " }\n", + " } else {\n", + " // Chrome <84 implements even older version of spec.\n", + " width = entry.contentRect.width;\n", + " height = entry.contentRect.height;\n", + " }\n", + "\n", + " // Keep the size of the canvas and rubber band canvas in sync with\n", + " // the canvas container.\n", + " if (entry.devicePixelContentBoxSize) {\n", + " // Chrome 84 implements new version of spec.\n", + " canvas.setAttribute(\n", + " 'width',\n", + " entry.devicePixelContentBoxSize[0].inlineSize\n", + " );\n", + " canvas.setAttribute(\n", + " 'height',\n", + " entry.devicePixelContentBoxSize[0].blockSize\n", + " );\n", + " } else {\n", + " canvas.setAttribute('width', width * fig.ratio);\n", + " canvas.setAttribute('height', height * fig.ratio);\n", + " }\n", + " canvas.setAttribute(\n", + " 'style',\n", + " 'width: ' + width + 'px; height: ' + height + 'px;'\n", + " );\n", + "\n", + " rubberband_canvas.setAttribute('width', width);\n", + " rubberband_canvas.setAttribute('height', height);\n", + "\n", + " // And update the size in Python. We ignore the initial 0/0 size\n", + " // that occurs as the element is placed into the DOM, which should\n", + " // otherwise not happen due to the minimum size styling.\n", + " if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n", + " fig.request_resize(width, height);\n", + " }\n", + " }\n", + " });\n", + " this.resizeObserverInstance.observe(canvas_div);\n", + "\n", + " function on_mouse_event_closure(name) {\n", + " return function (event) {\n", + " return fig.mouse_event(event, name);\n", + " };\n", + " }\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mousedown',\n", + " on_mouse_event_closure('button_press')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseup',\n", + " on_mouse_event_closure('button_release')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'dblclick',\n", + " on_mouse_event_closure('dblclick')\n", + " );\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband_canvas.addEventListener(\n", + " 'mousemove',\n", + " on_mouse_event_closure('motion_notify')\n", + " );\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseenter',\n", + " on_mouse_event_closure('figure_enter')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseleave',\n", + " on_mouse_event_closure('figure_leave')\n", + " );\n", + "\n", + " canvas_div.addEventListener('wheel', function (event) {\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " on_mouse_event_closure('scroll')(event);\n", + " });\n", + "\n", + " canvas_div.appendChild(canvas);\n", + " canvas_div.appendChild(rubberband_canvas);\n", + "\n", + " this.rubberband_context = rubberband_canvas.getContext('2d');\n", + " this.rubberband_context.strokeStyle = '#000000';\n", + "\n", + " this._resize_canvas = function (width, height, forward) {\n", + " if (forward) {\n", + " canvas_div.style.width = width + 'px';\n", + " canvas_div.style.height = height + 'px';\n", + " }\n", + " };\n", + "\n", + " // Disable right mouse context menu.\n", + " this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n", + " event.preventDefault();\n", + " return false;\n", + " });\n", + "\n", + " function set_focus() {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'mpl-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " continue;\n", + " }\n", + "\n", + " var button = (fig.buttons[name] = document.createElement('button'));\n", + " button.classList = 'mpl-widget';\n", + " button.setAttribute('role', 'button');\n", + " button.setAttribute('aria-disabled', 'false');\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + "\n", + " var icon_img = document.createElement('img');\n", + " icon_img.src = '_images/' + image + '.png';\n", + " icon_img.srcset = '_images/' + image + '_large.png 2x';\n", + " icon_img.alt = tooltip;\n", + " button.appendChild(icon_img);\n", + "\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " var fmt_picker = document.createElement('select');\n", + " fmt_picker.classList = 'mpl-widget';\n", + " toolbar.appendChild(fmt_picker);\n", + " this.format_dropdown = fmt_picker;\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = document.createElement('option');\n", + " option.selected = fmt === mpl.default_extension;\n", + " option.innerHTML = fmt;\n", + " fmt_picker.appendChild(option);\n", + " }\n", + "\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "};\n", + "\n", + "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', { width: x_pixels, height: y_pixels });\n", + "};\n", + "\n", + "mpl.figure.prototype.send_message = function (type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "};\n", + "\n", + "mpl.figure.prototype.send_draw_message = function () {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " var format_dropdown = fig.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " fig.ondownload(fig, format);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_resize = function (fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1], msg['forward']);\n", + " fig.send_message('refresh', {});\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n", + " var x0 = msg['x0'] / fig.ratio;\n", + " var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n", + " var x1 = msg['x1'] / fig.ratio;\n", + " var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0,\n", + " 0,\n", + " fig.canvas.width / fig.ratio,\n", + " fig.canvas.height / fig.ratio\n", + " );\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", + " fig.rubberband_canvas.style.cursor = msg['cursor'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_message = function (fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n", + " for (var key in msg) {\n", + " if (!(key in fig.buttons)) {\n", + " continue;\n", + " }\n", + " fig.buttons[key].disabled = !msg[key];\n", + " fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n", + " if (msg['mode'] === 'PAN') {\n", + " fig.buttons['Pan'].classList.add('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " } else if (msg['mode'] === 'ZOOM') {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.add('active');\n", + " } else {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message('ack', {});\n", + "};\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function (fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " var img = evt.data;\n", + " if (img.type !== 'image/png') {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " img.type = 'image/png';\n", + " }\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src\n", + " );\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " img\n", + " );\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " } else if (\n", + " typeof evt.data === 'string' &&\n", + " evt.data.slice(0, 21) === 'data:image/png;base64'\n", + " ) {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig['handle_' + msg_type];\n", + " } catch (e) {\n", + " console.log(\n", + " \"No handler for the '\" + msg_type + \"' message type: \",\n", + " msg\n", + " );\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\n", + " \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n", + " e,\n", + " e.stack,\n", + " msg\n", + " );\n", + " }\n", + " }\n", + " };\n", + "};\n", + "\n", + "// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function (e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e) {\n", + " e = window.event;\n", + " }\n", + " if (e.target) {\n", + " targ = e.target;\n", + " } else if (e.srcElement) {\n", + " targ = e.srcElement;\n", + " }\n", + " if (targ.nodeType === 3) {\n", + " // defeat Safari bug\n", + " targ = targ.parentNode;\n", + " }\n", + "\n", + " // pageX,Y are the mouse positions relative to the document\n", + " var boundingRect = targ.getBoundingClientRect();\n", + " var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n", + " var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n", + "\n", + " return { x: x, y: y };\n", + "};\n", + "\n", + "/*\n", + " * return a copy of an object with only non-object keys\n", + " * we need this to avoid circular references\n", + " * https://stackoverflow.com/a/24161582/3208463\n", + " */\n", + "function simpleKeys(original) {\n", + " return Object.keys(original).reduce(function (obj, key) {\n", + " if (typeof original[key] !== 'object') {\n", + " obj[key] = original[key];\n", + " }\n", + " return obj;\n", + " }, {});\n", + "}\n", + "\n", + "mpl.figure.prototype.mouse_event = function (event, name) {\n", + " var canvas_pos = mpl.findpos(event);\n", + "\n", + " if (name === 'button_press') {\n", + " this.canvas.focus();\n", + " this.canvas_div.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x * this.ratio;\n", + " var y = canvas_pos.y * this.ratio;\n", + "\n", + " this.send_message(name, {\n", + " x: x,\n", + " y: y,\n", + " button: event.button,\n", + " step: event.step,\n", + " guiEvent: simpleKeys(event),\n", + " });\n", + "\n", + " /* This prevents the web browser from automatically changing to\n", + " * the text insertion cursor when the button is pressed. We want\n", + " * to control all of the cursor setting manually through the\n", + " * 'cursor' event from matplotlib */\n", + " event.preventDefault();\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n", + " // Handle any extra behaviour associated with a key event\n", + "};\n", + "\n", + "mpl.figure.prototype.key_event = function (event, name) {\n", + " // Prevent repeat events\n", + " if (name === 'key_press') {\n", + " if (event.key === this._key) {\n", + " return;\n", + " } else {\n", + " this._key = event.key;\n", + " }\n", + " }\n", + " if (name === 'key_release') {\n", + " this._key = null;\n", + " }\n", + "\n", + " var value = '';\n", + " if (event.ctrlKey && event.key !== 'Control') {\n", + " value += 'ctrl+';\n", + " }\n", + " else if (event.altKey && event.key !== 'Alt') {\n", + " value += 'alt+';\n", + " }\n", + " else if (event.shiftKey && event.key !== 'Shift') {\n", + " value += 'shift+';\n", + " }\n", + "\n", + " value += 'k' + event.key;\n", + "\n", + " this._key_event_extra(event, name);\n", + "\n", + " this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n", + " if (name === 'download') {\n", + " this.handle_save(this, null);\n", + " } else {\n", + " this.send_message('toolbar_button', { name: name });\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n", + " this.message.textContent = tooltip;\n", + "};\n", + "\n", + "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", + "// prettier-ignore\n", + "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";/* global mpl */\n", + "\n", + "var comm_websocket_adapter = function (comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.binaryType = comm.kernel.ws.binaryType;\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " function updateReadyState(_event) {\n", + " if (comm.kernel.ws) {\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " } else {\n", + " ws.readyState = 3; // Closed state.\n", + " }\n", + " }\n", + " comm.kernel.ws.addEventListener('open', updateReadyState);\n", + " comm.kernel.ws.addEventListener('close', updateReadyState);\n", + " comm.kernel.ws.addEventListener('error', updateReadyState);\n", + "\n", + " ws.close = function () {\n", + " comm.close();\n", + " };\n", + " ws.send = function (m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function (msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " var data = msg['content']['data'];\n", + " if (data['blob'] !== undefined) {\n", + " data = {\n", + " data: new Blob(msg['buffers'], { type: data['blob'] }),\n", + " };\n", + " }\n", + " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", + " ws.onmessage(data);\n", + " });\n", + " return ws;\n", + "};\n", + "\n", + "mpl.mpl_figure_comm = function (comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = document.getElementById(id);\n", + " var ws_proxy = comm_websocket_adapter(comm);\n", + "\n", + " function ondownload(figure, _format) {\n", + " window.open(figure.canvas.toDataURL());\n", + " }\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element;\n", + " fig.cell_info = mpl.find_output_cell(\"
\");\n", + " if (!fig.cell_info) {\n", + " console.error('Failed to find cell for figure', id, fig);\n", + " return;\n", + " }\n", + " fig.cell_info[0].output_area.element.on(\n", + " 'cleared',\n", + " { fig: fig },\n", + " fig._remove_fig_handler\n", + " );\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function (fig, msg) {\n", + " var width = fig.canvas.width / fig.ratio;\n", + " fig.cell_info[0].output_area.element.off(\n", + " 'cleared',\n", + " fig._remove_fig_handler\n", + " );\n", + " fig.resizeObserverInstance.unobserve(fig.canvas_div);\n", + "\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable();\n", + " fig.parent_element.innerHTML =\n", + " '';\n", + " fig.close_ws(fig, msg);\n", + "};\n", + "\n", + "mpl.figure.prototype.close_ws = function (fig, msg) {\n", + " fig.send_message('closing', msg);\n", + " // fig.ws.close()\n", + "};\n", + "\n", + "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n", + " // Turn the data on the canvas into data in the output cell.\n", + " var width = this.canvas.width / this.ratio;\n", + " var dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] =\n", + " '';\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message('ack', {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () {\n", + " fig.push_to_output();\n", + " }, 1000);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'btn-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " var button;\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " continue;\n", + " }\n", + "\n", + " button = fig.buttons[name] = document.createElement('button');\n", + " button.classList = 'btn btn-default';\n", + " button.href = '#';\n", + " button.title = name;\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message pull-right';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = document.createElement('div');\n", + " buttongrp.classList = 'btn-group inline pull-right';\n", + " button = document.createElement('button');\n", + " button.classList = 'btn btn-mini btn-primary';\n", + " button.href = '#';\n", + " button.title = 'Stop Interaction';\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', function (_evt) {\n", + " fig.handle_close(fig, {});\n", + " });\n", + " button.addEventListener(\n", + " 'mouseover',\n", + " on_mouseover_closure('Stop Interaction')\n", + " );\n", + " buttongrp.appendChild(button);\n", + " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", + " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", + "};\n", + "\n", + "mpl.figure.prototype._remove_fig_handler = function (event) {\n", + " var fig = event.data.fig;\n", + " if (event.target !== this) {\n", + " // Ignore bubbled events from children.\n", + " return;\n", + " }\n", + " fig.close_ws(fig, {});\n", + "};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (el) {\n", + " el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (el) {\n", + " // this is important to make the div 'focusable\n", + " el.setAttribute('tabindex', 0);\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " } else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which === 13) {\n", + " this.canvas_div.blur();\n", + " // select the cell after this one\n", + " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", + " IPython.notebook.select(index + 1);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " fig.ondownload(fig, null);\n", + "};\n", + "\n", + "mpl.find_output_cell = function (html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i = 0; i < ncells; i++) {\n", + " var cell = cells[i];\n", + " if (cell.cell_type === 'code') {\n", + " for (var j = 0; j < cell.output_area.outputs.length; j++) {\n", + " var data = cell.output_area.outputs[j];\n", + " if (data.data) {\n", + " // IPython >= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] === html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "};\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel !== null) {\n", + " IPython.notebook.kernel.comm_manager.register_target(\n", + " 'matplotlib',\n", + " mpl.mpl_figure_comm\n", + " );\n", + "}\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%matplotlib notebook\n", + "\n", + "# Read in results\n", + "## Params - Either specify a file prefix or a whole folder\n", + "file_prefix = None # Path(\"./results/2022-07-05T22:22:50_iops_write_\")\n", + "folder = Path(\"./results/param_study_fio_iops\")\n", + "\n", + "## Plot code\n", + "path = file_prefix if file_prefix else folder\n", + "\n", + "\n", + "post_i = \"_isilon.json\"\n", + "post_n = \"_netapp.json\"\n", + "metric = METRIC[\"iops_write\"]\n", + "\n", + "df_1 = pd.concat({\n", + " \"nfs_isilon\": load_data(path, suffix=\"blocksize\"+post_i, metric=metric),\n", + " \"nfs_netapp\": load_data(path, suffix=\"blocksize\"+post_n, metric=metric),\n", + "})\n", + "df_2 = pd.concat({\n", + " \"nfs_isilon\": load_data(path, suffix=\"filesize\"+post_i, metric=metric),\n", + " \"nfs_netapp\": load_data(path, suffix=\"filesize\"+post_n, metric=metric),\n", + "})\n", + "df_3 = pd.concat({\n", + " \"nfs_isilon\": load_data(path, suffix=\"iodepth\"+post_i, metric=metric),\n", + " \"nfs_netapp\": load_data(path, suffix=\"iodepth\"+post_n, metric=metric),\n", + "})\n", + "df_4 = pd.concat({\n", + " \"nfs_isilon\": load_data(path, suffix=\"numjobs\"+post_i, metric=metric),\n", + " \"nfs_netapp\": load_data(path, suffix=\"numjobs\"+post_n, metric=metric),\n", + "})\n", + "\n", + "fig, axis = plt.subplots(2, 2, sharey=True)\n", + "fig.suptitle('Parameter Study: Fio Write IOPS')\n", + "\n", + "color = [\"tab:red\", \"tab:cyan\"]\n", + " \n", + "df_1[\"mean\"].unstack(level=0).plot(\n", + " ax=axis[0][0],\n", + " kind='bar', rot=0,\n", + " xlabel='Blocksize in KiB',\n", + " ylabel='IOPS',\n", + " title='Variable Blocksize',\n", + " yerr=df_1[\"std\"].unstack(level=0),\n", + " color=color,\n", + ")\n", + "df_2[\"mean\"].unstack(level=0).plot(\n", + " ax=axis[0][1],\n", + " kind='bar', rot=0,\n", + " xlabel='Filesize in GiB',\n", + " title='Variable Filesize',\n", + " yerr=df_2[\"std\"].unstack(level=0),\n", + " color=color,\n", + ")\n", + "\n", + "df_3[\"mean\"].unstack(level=0).plot(\n", + " ax=axis[1][0],\n", + " kind='bar', rot=0,\n", + " xlabel='IODepth',\n", + " ylabel='IOPS',\n", + " title='Variable IODepth',\n", + " yerr=df_3[\"std\"].unstack(level=0),\n", + " color=color,\n", + ")\n", + "\n", + "df_4[\"mean\"].unstack(level=0).plot(\n", + " ax=axis[1][1],\n", + " kind='bar', rot=0,\n", + " xlabel='Jobs',\n", + " title='Variable Number of Jobs',\n", + " yerr=df_4[\"std\"].unstack(level=0),\n", + " color=color,\n", + ")\n", + "\n", + "for i,j in [(0,1),(1,0),(1,1)]:\n", + " axis[i][j].get_legend().remove()\n", + " \n", + "plt.tight_layout()\n", + "plt.savefig(\"paramstudy_fio_iops.png\",bbox_inches='tight')\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "0ed3bfbb", + "metadata": {}, + "source": [ + "# Experiment: Parameter-evaluation: MDTest" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "8f15ae0a", + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "/* global mpl */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function () {\n", + " if (typeof WebSocket !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof MozWebSocket !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert(\n", + " 'Your browser does not have WebSocket support. ' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.'\n", + " );\n", + " }\n", + "};\n", + "\n", + "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = this.ws.binaryType !== undefined;\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById('mpl-warnings');\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent =\n", + " 'This browser does not support binary websocket messages. ' +\n", + " 'Performance may be slow.';\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = document.createElement('div');\n", + " this.root.setAttribute('style', 'display: inline-block');\n", + " this._root_extra_style(this.root);\n", + "\n", + " parent_element.appendChild(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message('supports_binary', { value: fig.supports_binary });\n", + " fig.send_message('send_image_mode', {});\n", + " if (fig.ratio !== 1) {\n", + " fig.send_message('set_device_pixel_ratio', {\n", + " device_pixel_ratio: fig.ratio,\n", + " });\n", + " }\n", + " fig.send_message('refresh', {});\n", + " };\n", + "\n", + " this.imageObj.onload = function () {\n", + " if (fig.image_mode === 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function () {\n", + " fig.ws.close();\n", + " };\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "};\n", + "\n", + "mpl.figure.prototype._init_header = function () {\n", + " var titlebar = document.createElement('div');\n", + " titlebar.classList =\n", + " 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n", + " var titletext = document.createElement('div');\n", + " titletext.classList = 'ui-dialog-title';\n", + " titletext.setAttribute(\n", + " 'style',\n", + " 'width: 100%; text-align: center; padding: 3px;'\n", + " );\n", + " titlebar.appendChild(titletext);\n", + " this.root.appendChild(titlebar);\n", + " this.header = titletext;\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._init_canvas = function () {\n", + " var fig = this;\n", + "\n", + " var canvas_div = (this.canvas_div = document.createElement('div'));\n", + " canvas_div.setAttribute(\n", + " 'style',\n", + " 'border: 1px solid #ddd;' +\n", + " 'box-sizing: content-box;' +\n", + " 'clear: both;' +\n", + " 'min-height: 1px;' +\n", + " 'min-width: 1px;' +\n", + " 'outline: 0;' +\n", + " 'overflow: hidden;' +\n", + " 'position: relative;' +\n", + " 'resize: both;'\n", + " );\n", + "\n", + " function on_keyboard_event_closure(name) {\n", + " return function (event) {\n", + " return fig.key_event(event, name);\n", + " };\n", + " }\n", + "\n", + " canvas_div.addEventListener(\n", + " 'keydown',\n", + " on_keyboard_event_closure('key_press')\n", + " );\n", + " canvas_div.addEventListener(\n", + " 'keyup',\n", + " on_keyboard_event_closure('key_release')\n", + " );\n", + "\n", + " this._canvas_extra_style(canvas_div);\n", + " this.root.appendChild(canvas_div);\n", + "\n", + " var canvas = (this.canvas = document.createElement('canvas'));\n", + " canvas.classList.add('mpl-canvas');\n", + " canvas.setAttribute('style', 'box-sizing: content-box;');\n", + "\n", + " this.context = canvas.getContext('2d');\n", + "\n", + " var backingStore =\n", + " this.context.backingStorePixelRatio ||\n", + " this.context.webkitBackingStorePixelRatio ||\n", + " this.context.mozBackingStorePixelRatio ||\n", + " this.context.msBackingStorePixelRatio ||\n", + " this.context.oBackingStorePixelRatio ||\n", + " this.context.backingStorePixelRatio ||\n", + " 1;\n", + "\n", + " this.ratio = (window.devicePixelRatio || 1) / backingStore;\n", + "\n", + " var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n", + " 'canvas'\n", + " ));\n", + " rubberband_canvas.setAttribute(\n", + " 'style',\n", + " 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n", + " );\n", + "\n", + " // Apply a ponyfill if ResizeObserver is not implemented by browser.\n", + " if (this.ResizeObserver === undefined) {\n", + " if (window.ResizeObserver !== undefined) {\n", + " this.ResizeObserver = window.ResizeObserver;\n", + " } else {\n", + " var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n", + " this.ResizeObserver = obs.ResizeObserver;\n", + " }\n", + " }\n", + "\n", + " this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n", + " var nentries = entries.length;\n", + " for (var i = 0; i < nentries; i++) {\n", + " var entry = entries[i];\n", + " var width, height;\n", + " if (entry.contentBoxSize) {\n", + " if (entry.contentBoxSize instanceof Array) {\n", + " // Chrome 84 implements new version of spec.\n", + " width = entry.contentBoxSize[0].inlineSize;\n", + " height = entry.contentBoxSize[0].blockSize;\n", + " } else {\n", + " // Firefox implements old version of spec.\n", + " width = entry.contentBoxSize.inlineSize;\n", + " height = entry.contentBoxSize.blockSize;\n", + " }\n", + " } else {\n", + " // Chrome <84 implements even older version of spec.\n", + " width = entry.contentRect.width;\n", + " height = entry.contentRect.height;\n", + " }\n", + "\n", + " // Keep the size of the canvas and rubber band canvas in sync with\n", + " // the canvas container.\n", + " if (entry.devicePixelContentBoxSize) {\n", + " // Chrome 84 implements new version of spec.\n", + " canvas.setAttribute(\n", + " 'width',\n", + " entry.devicePixelContentBoxSize[0].inlineSize\n", + " );\n", + " canvas.setAttribute(\n", + " 'height',\n", + " entry.devicePixelContentBoxSize[0].blockSize\n", + " );\n", + " } else {\n", + " canvas.setAttribute('width', width * fig.ratio);\n", + " canvas.setAttribute('height', height * fig.ratio);\n", + " }\n", + " canvas.setAttribute(\n", + " 'style',\n", + " 'width: ' + width + 'px; height: ' + height + 'px;'\n", + " );\n", + "\n", + " rubberband_canvas.setAttribute('width', width);\n", + " rubberband_canvas.setAttribute('height', height);\n", + "\n", + " // And update the size in Python. We ignore the initial 0/0 size\n", + " // that occurs as the element is placed into the DOM, which should\n", + " // otherwise not happen due to the minimum size styling.\n", + " if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n", + " fig.request_resize(width, height);\n", + " }\n", + " }\n", + " });\n", + " this.resizeObserverInstance.observe(canvas_div);\n", + "\n", + " function on_mouse_event_closure(name) {\n", + " return function (event) {\n", + " return fig.mouse_event(event, name);\n", + " };\n", + " }\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mousedown',\n", + " on_mouse_event_closure('button_press')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseup',\n", + " on_mouse_event_closure('button_release')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'dblclick',\n", + " on_mouse_event_closure('dblclick')\n", + " );\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband_canvas.addEventListener(\n", + " 'mousemove',\n", + " on_mouse_event_closure('motion_notify')\n", + " );\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseenter',\n", + " on_mouse_event_closure('figure_enter')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseleave',\n", + " on_mouse_event_closure('figure_leave')\n", + " );\n", + "\n", + " canvas_div.addEventListener('wheel', function (event) {\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " on_mouse_event_closure('scroll')(event);\n", + " });\n", + "\n", + " canvas_div.appendChild(canvas);\n", + " canvas_div.appendChild(rubberband_canvas);\n", + "\n", + " this.rubberband_context = rubberband_canvas.getContext('2d');\n", + " this.rubberband_context.strokeStyle = '#000000';\n", + "\n", + " this._resize_canvas = function (width, height, forward) {\n", + " if (forward) {\n", + " canvas_div.style.width = width + 'px';\n", + " canvas_div.style.height = height + 'px';\n", + " }\n", + " };\n", + "\n", + " // Disable right mouse context menu.\n", + " this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n", + " event.preventDefault();\n", + " return false;\n", + " });\n", + "\n", + " function set_focus() {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'mpl-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " continue;\n", + " }\n", + "\n", + " var button = (fig.buttons[name] = document.createElement('button'));\n", + " button.classList = 'mpl-widget';\n", + " button.setAttribute('role', 'button');\n", + " button.setAttribute('aria-disabled', 'false');\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + "\n", + " var icon_img = document.createElement('img');\n", + " icon_img.src = '_images/' + image + '.png';\n", + " icon_img.srcset = '_images/' + image + '_large.png 2x';\n", + " icon_img.alt = tooltip;\n", + " button.appendChild(icon_img);\n", + "\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " var fmt_picker = document.createElement('select');\n", + " fmt_picker.classList = 'mpl-widget';\n", + " toolbar.appendChild(fmt_picker);\n", + " this.format_dropdown = fmt_picker;\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = document.createElement('option');\n", + " option.selected = fmt === mpl.default_extension;\n", + " option.innerHTML = fmt;\n", + " fmt_picker.appendChild(option);\n", + " }\n", + "\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "};\n", + "\n", + "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', { width: x_pixels, height: y_pixels });\n", + "};\n", + "\n", + "mpl.figure.prototype.send_message = function (type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "};\n", + "\n", + "mpl.figure.prototype.send_draw_message = function () {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " var format_dropdown = fig.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " fig.ondownload(fig, format);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_resize = function (fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1], msg['forward']);\n", + " fig.send_message('refresh', {});\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n", + " var x0 = msg['x0'] / fig.ratio;\n", + " var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n", + " var x1 = msg['x1'] / fig.ratio;\n", + " var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0,\n", + " 0,\n", + " fig.canvas.width / fig.ratio,\n", + " fig.canvas.height / fig.ratio\n", + " );\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", + " fig.rubberband_canvas.style.cursor = msg['cursor'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_message = function (fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n", + " for (var key in msg) {\n", + " if (!(key in fig.buttons)) {\n", + " continue;\n", + " }\n", + " fig.buttons[key].disabled = !msg[key];\n", + " fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n", + " if (msg['mode'] === 'PAN') {\n", + " fig.buttons['Pan'].classList.add('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " } else if (msg['mode'] === 'ZOOM') {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.add('active');\n", + " } else {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message('ack', {});\n", + "};\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function (fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " var img = evt.data;\n", + " if (img.type !== 'image/png') {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " img.type = 'image/png';\n", + " }\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src\n", + " );\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " img\n", + " );\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " } else if (\n", + " typeof evt.data === 'string' &&\n", + " evt.data.slice(0, 21) === 'data:image/png;base64'\n", + " ) {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig['handle_' + msg_type];\n", + " } catch (e) {\n", + " console.log(\n", + " \"No handler for the '\" + msg_type + \"' message type: \",\n", + " msg\n", + " );\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\n", + " \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n", + " e,\n", + " e.stack,\n", + " msg\n", + " );\n", + " }\n", + " }\n", + " };\n", + "};\n", + "\n", + "// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function (e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e) {\n", + " e = window.event;\n", + " }\n", + " if (e.target) {\n", + " targ = e.target;\n", + " } else if (e.srcElement) {\n", + " targ = e.srcElement;\n", + " }\n", + " if (targ.nodeType === 3) {\n", + " // defeat Safari bug\n", + " targ = targ.parentNode;\n", + " }\n", + "\n", + " // pageX,Y are the mouse positions relative to the document\n", + " var boundingRect = targ.getBoundingClientRect();\n", + " var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n", + " var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n", + "\n", + " return { x: x, y: y };\n", + "};\n", + "\n", + "/*\n", + " * return a copy of an object with only non-object keys\n", + " * we need this to avoid circular references\n", + " * https://stackoverflow.com/a/24161582/3208463\n", + " */\n", + "function simpleKeys(original) {\n", + " return Object.keys(original).reduce(function (obj, key) {\n", + " if (typeof original[key] !== 'object') {\n", + " obj[key] = original[key];\n", + " }\n", + " return obj;\n", + " }, {});\n", + "}\n", + "\n", + "mpl.figure.prototype.mouse_event = function (event, name) {\n", + " var canvas_pos = mpl.findpos(event);\n", + "\n", + " if (name === 'button_press') {\n", + " this.canvas.focus();\n", + " this.canvas_div.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x * this.ratio;\n", + " var y = canvas_pos.y * this.ratio;\n", + "\n", + " this.send_message(name, {\n", + " x: x,\n", + " y: y,\n", + " button: event.button,\n", + " step: event.step,\n", + " guiEvent: simpleKeys(event),\n", + " });\n", + "\n", + " /* This prevents the web browser from automatically changing to\n", + " * the text insertion cursor when the button is pressed. We want\n", + " * to control all of the cursor setting manually through the\n", + " * 'cursor' event from matplotlib */\n", + " event.preventDefault();\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n", + " // Handle any extra behaviour associated with a key event\n", + "};\n", + "\n", + "mpl.figure.prototype.key_event = function (event, name) {\n", + " // Prevent repeat events\n", + " if (name === 'key_press') {\n", + " if (event.key === this._key) {\n", + " return;\n", + " } else {\n", + " this._key = event.key;\n", + " }\n", + " }\n", + " if (name === 'key_release') {\n", + " this._key = null;\n", + " }\n", + "\n", + " var value = '';\n", + " if (event.ctrlKey && event.key !== 'Control') {\n", + " value += 'ctrl+';\n", + " }\n", + " else if (event.altKey && event.key !== 'Alt') {\n", + " value += 'alt+';\n", + " }\n", + " else if (event.shiftKey && event.key !== 'Shift') {\n", + " value += 'shift+';\n", + " }\n", + "\n", + " value += 'k' + event.key;\n", + "\n", + " this._key_event_extra(event, name);\n", + "\n", + " this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n", + " if (name === 'download') {\n", + " this.handle_save(this, null);\n", + " } else {\n", + " this.send_message('toolbar_button', { name: name });\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n", + " this.message.textContent = tooltip;\n", + "};\n", + "\n", + "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", + "// prettier-ignore\n", + "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";/* global mpl */\n", + "\n", + "var comm_websocket_adapter = function (comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.binaryType = comm.kernel.ws.binaryType;\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " function updateReadyState(_event) {\n", + " if (comm.kernel.ws) {\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " } else {\n", + " ws.readyState = 3; // Closed state.\n", + " }\n", + " }\n", + " comm.kernel.ws.addEventListener('open', updateReadyState);\n", + " comm.kernel.ws.addEventListener('close', updateReadyState);\n", + " comm.kernel.ws.addEventListener('error', updateReadyState);\n", + "\n", + " ws.close = function () {\n", + " comm.close();\n", + " };\n", + " ws.send = function (m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function (msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " var data = msg['content']['data'];\n", + " if (data['blob'] !== undefined) {\n", + " data = {\n", + " data: new Blob(msg['buffers'], { type: data['blob'] }),\n", + " };\n", + " }\n", + " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", + " ws.onmessage(data);\n", + " });\n", + " return ws;\n", + "};\n", + "\n", + "mpl.mpl_figure_comm = function (comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = document.getElementById(id);\n", + " var ws_proxy = comm_websocket_adapter(comm);\n", + "\n", + " function ondownload(figure, _format) {\n", + " window.open(figure.canvas.toDataURL());\n", + " }\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element;\n", + " fig.cell_info = mpl.find_output_cell(\"
\");\n", + " if (!fig.cell_info) {\n", + " console.error('Failed to find cell for figure', id, fig);\n", + " return;\n", + " }\n", + " fig.cell_info[0].output_area.element.on(\n", + " 'cleared',\n", + " { fig: fig },\n", + " fig._remove_fig_handler\n", + " );\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function (fig, msg) {\n", + " var width = fig.canvas.width / fig.ratio;\n", + " fig.cell_info[0].output_area.element.off(\n", + " 'cleared',\n", + " fig._remove_fig_handler\n", + " );\n", + " fig.resizeObserverInstance.unobserve(fig.canvas_div);\n", + "\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable();\n", + " fig.parent_element.innerHTML =\n", + " '';\n", + " fig.close_ws(fig, msg);\n", + "};\n", + "\n", + "mpl.figure.prototype.close_ws = function (fig, msg) {\n", + " fig.send_message('closing', msg);\n", + " // fig.ws.close()\n", + "};\n", + "\n", + "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n", + " // Turn the data on the canvas into data in the output cell.\n", + " var width = this.canvas.width / this.ratio;\n", + " var dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] =\n", + " '';\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message('ack', {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () {\n", + " fig.push_to_output();\n", + " }, 1000);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'btn-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " var button;\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " continue;\n", + " }\n", + "\n", + " button = fig.buttons[name] = document.createElement('button');\n", + " button.classList = 'btn btn-default';\n", + " button.href = '#';\n", + " button.title = name;\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message pull-right';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = document.createElement('div');\n", + " buttongrp.classList = 'btn-group inline pull-right';\n", + " button = document.createElement('button');\n", + " button.classList = 'btn btn-mini btn-primary';\n", + " button.href = '#';\n", + " button.title = 'Stop Interaction';\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', function (_evt) {\n", + " fig.handle_close(fig, {});\n", + " });\n", + " button.addEventListener(\n", + " 'mouseover',\n", + " on_mouseover_closure('Stop Interaction')\n", + " );\n", + " buttongrp.appendChild(button);\n", + " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", + " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", + "};\n", + "\n", + "mpl.figure.prototype._remove_fig_handler = function (event) {\n", + " var fig = event.data.fig;\n", + " if (event.target !== this) {\n", + " // Ignore bubbled events from children.\n", + " return;\n", + " }\n", + " fig.close_ws(fig, {});\n", + "};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (el) {\n", + " el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (el) {\n", + " // this is important to make the div 'focusable\n", + " el.setAttribute('tabindex', 0);\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " } else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which === 13) {\n", + " this.canvas_div.blur();\n", + " // select the cell after this one\n", + " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", + " IPython.notebook.select(index + 1);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " fig.ondownload(fig, null);\n", + "};\n", + "\n", + "mpl.find_output_cell = function (html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i = 0; i < ncells; i++) {\n", + " var cell = cells[i];\n", + " if (cell.cell_type === 'code') {\n", + " for (var j = 0; j < cell.output_area.outputs.length; j++) {\n", + " var data = cell.output_area.outputs[j];\n", + " if (data.data) {\n", + " // IPython >= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] === html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "};\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel !== null) {\n", + " IPython.notebook.kernel.comm_manager.register_target(\n", + " 'matplotlib',\n", + " mpl.mpl_figure_comm\n", + " );\n", + "}\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " mean std num\n", + "dir_create 10^1 280.55660 367.812856 20\n", + " 10^2 870.04705 214.024901 20\n", + " 10^3 1095.08435 216.723094 20\n", + " 10^4 1161.64235 166.196069 20\n", + " 10^5 1180.50980 53.700815 20\n", + " mean std num\n", + "dir_stat 10^1 59722.32145 131417.670234 20\n", + " 10^2 99183.02005 135083.384508 20\n", + " 10^3 233443.53515 69215.281521 20\n", + " 10^4 348868.19885 37224.326942 20\n", + " 10^5 5740.32620 776.724857 20\n", + " mean std num\n", + "dir_remove 10^1 721.58990 150.587023 20\n", + " 10^2 804.84300 174.153828 20\n", + " 10^3 798.34010 136.877770 20\n", + " 10^4 669.08225 85.009592 20\n", + " 10^5 692.32010 28.690286 20\n", + " mean std num\n", + "file_create 10^1 776.37855 151.638302 20\n", + " 10^2 903.83465 126.004420 20\n", + " 10^3 890.59460 164.678292 20\n", + " 10^4 933.20065 44.686408 20\n", + " 10^5 918.76760 20.496545 20\n", + " mean std num\n", + "file_stat 10^1 54006.49675 118169.827073 20\n", + " 10^2 102877.26100 140927.550124 20\n", + " 10^3 241069.36130 84737.679978 20\n", + " 10^4 5632.01315 662.772729 20\n", + " 10^5 4866.17720 390.810018 20\n", + " mean std num\n", + "file_read 10^1 1212.11170 283.114533 20\n", + " 10^2 1577.01555 101.303439 20\n", + " 10^3 1623.92030 117.784612 20\n", + " 10^4 1622.45610 92.810976 20\n", + " 10^5 1590.42615 101.705627 20\n", + " mean std num\n", + "file_remove 10^1 1147.01635 240.201849 20\n", + " 10^2 1411.83485 74.184542 20\n", + " 10^3 1327.03895 203.794847 20\n", + " 10^4 1089.15860 110.579144 20\n", + " 10^5 1011.23390 24.363037 20\n" + ] + } + ], + "source": [ + "%matplotlib notebook\n", + "\n", + "# Read in results\n", + "## Params - Either specify a file prefix or a whole folder\n", + "file_prefix = None # Path(\"./results/2022-07-13T11:04:51_parameval_warp_\")\n", + "folder = Path(\"./results/param_study_mdtest\")\n", + "\n", + "## Plot code\n", + "path = file_prefix if file_prefix else folder\n", + "\n", + "metrics = [\n", + " \"dir_create\",\n", + " \"dir_stat\",\n", + " \"dir_remove\",\n", + " \"file_create\",\n", + " \"file_stat\",\n", + " \"file_read\",\n", + " \"file_remove\",\n", + " #\"tree_create\",\n", + " #\"tree_remove\",\n", + "]\n", + "dfs = []\n", + "\n", + "for metric in metrics:\n", + " dfs.append(pd.concat({\n", + " #\"local\": load_data(path, metric=metric, suffix=\"mdtest_local.json\"),\n", + " metric: load_data(path, metric=metric, suffix=\"mdtest_isilon.json\"),\n", + " #\"nfs_netapp\": load_data(path, metric=metric, suffix=\"mdtest_netapp.json\"),\n", + " }))\n", + " dfs[-1].rename(index={10: \"10^1\", \"100\": \"10^2\", \"1000\": \"10^3\", \"10000\": \"10^4\", \"100000\": \"10^5\"}, inplace=True)\n", + "\n", + " \n", + "\n", + "fig, axis = plt.subplots(1, 2)\n", + "fig.suptitle('Parameter Study: mdtest on nfs_isilon')\n", + "\n", + "dfs[1][\"mean\"].unstack(level=0).plot(\n", + " ax=axis[0],\n", + " kind='line', rot=0,\n", + " xlabel='num_files',\n", + " ylabel='Operations (1/s)',\n", + " yerr=dfs[1][\"std\"].unstack(level=0)\n", + ")\n", + "dfs[4][\"mean\"].unstack(level=0).plot(\n", + " ax=axis[0],\n", + " kind='line', rot=0,\n", + " yerr=dfs[4][\"std\"].unstack(level=0)\n", + ")\n", + "\n", + "dfs[0][\"mean\"].unstack(level=0).plot(\n", + " ax=axis[1],\n", + " kind='line', rot=0,\n", + " xlabel='num_files',\n", + " ylabel='Operations (1/s)',\n", + " yerr=dfs[0][\"std\"].unstack(level=0)\n", + ")\n", + "\n", + "dfs[2][\"mean\"].unstack(level=0).plot(\n", + " ax=axis[1],\n", + " kind='line', rot=0,\n", + " yerr=dfs[2][\"std\"].unstack(level=0)\n", + ")\n", + "dfs[3][\"mean\"].unstack(level=0).plot(\n", + " ax=axis[1],\n", + " kind='line', rot=0,\n", + " yerr=dfs[3][\"std\"].unstack(level=0)\n", + ")\n", + "dfs[5][\"mean\"].unstack(level=0).plot(\n", + " ax=axis[1],\n", + " kind='line', rot=0,\n", + " yerr=dfs[5][\"std\"].unstack(level=0)\n", + ")\n", + "dfs[6][\"mean\"].unstack(level=0).plot(\n", + " ax=axis[1],\n", + " kind='line', rot=0,\n", + " yerr=dfs[6][\"std\"].unstack(level=0)\n", + ")\n", + "#dfs[7][\"mean\"].unstack(level=0).plot(\n", + "# ax=axis[1],\n", + "# kind='line', rot=0,\n", + "# yerr=dfs[7][\"std\"].unstack(level=0)\n", + "#)\n", + "#dfs[8][\"mean\"].unstack(level=0).plot(\n", + "# ax=axis[1],\n", + "# kind='line', rot=0,\n", + "# yerr=dfs[8][\"std\"].unstack(level=0)\n", + "#)\n", + "\n", + "\n", + "for df in dfs:\n", + " print(df)\n", + "\n", + "plt.tight_layout()\n", + "plt.savefig(\"paramstudy_mdtest.png\",bbox_inches='tight')\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "3f5cbb01", + "metadata": {}, + "source": [ + "# Experiment: Parameter-evaluation: s3-benchmark" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "8ec03515", + "metadata": {}, + "outputs": [ + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "/* global mpl */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function () {\n", + " if (typeof WebSocket !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof MozWebSocket !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert(\n", + " 'Your browser does not have WebSocket support. ' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.'\n", + " );\n", + " }\n", + "};\n", + "\n", + "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = this.ws.binaryType !== undefined;\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById('mpl-warnings');\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent =\n", + " 'This browser does not support binary websocket messages. ' +\n", + " 'Performance may be slow.';\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = document.createElement('div');\n", + " this.root.setAttribute('style', 'display: inline-block');\n", + " this._root_extra_style(this.root);\n", + "\n", + " parent_element.appendChild(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message('supports_binary', { value: fig.supports_binary });\n", + " fig.send_message('send_image_mode', {});\n", + " if (fig.ratio !== 1) {\n", + " fig.send_message('set_device_pixel_ratio', {\n", + " device_pixel_ratio: fig.ratio,\n", + " });\n", + " }\n", + " fig.send_message('refresh', {});\n", + " };\n", + "\n", + " this.imageObj.onload = function () {\n", + " if (fig.image_mode === 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function () {\n", + " fig.ws.close();\n", + " };\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "};\n", + "\n", + "mpl.figure.prototype._init_header = function () {\n", + " var titlebar = document.createElement('div');\n", + " titlebar.classList =\n", + " 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n", + " var titletext = document.createElement('div');\n", + " titletext.classList = 'ui-dialog-title';\n", + " titletext.setAttribute(\n", + " 'style',\n", + " 'width: 100%; text-align: center; padding: 3px;'\n", + " );\n", + " titlebar.appendChild(titletext);\n", + " this.root.appendChild(titlebar);\n", + " this.header = titletext;\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._init_canvas = function () {\n", + " var fig = this;\n", + "\n", + " var canvas_div = (this.canvas_div = document.createElement('div'));\n", + " canvas_div.setAttribute(\n", + " 'style',\n", + " 'border: 1px solid #ddd;' +\n", + " 'box-sizing: content-box;' +\n", + " 'clear: both;' +\n", + " 'min-height: 1px;' +\n", + " 'min-width: 1px;' +\n", + " 'outline: 0;' +\n", + " 'overflow: hidden;' +\n", + " 'position: relative;' +\n", + " 'resize: both;'\n", + " );\n", + "\n", + " function on_keyboard_event_closure(name) {\n", + " return function (event) {\n", + " return fig.key_event(event, name);\n", + " };\n", + " }\n", + "\n", + " canvas_div.addEventListener(\n", + " 'keydown',\n", + " on_keyboard_event_closure('key_press')\n", + " );\n", + " canvas_div.addEventListener(\n", + " 'keyup',\n", + " on_keyboard_event_closure('key_release')\n", + " );\n", + "\n", + " this._canvas_extra_style(canvas_div);\n", + " this.root.appendChild(canvas_div);\n", + "\n", + " var canvas = (this.canvas = document.createElement('canvas'));\n", + " canvas.classList.add('mpl-canvas');\n", + " canvas.setAttribute('style', 'box-sizing: content-box;');\n", + "\n", + " this.context = canvas.getContext('2d');\n", + "\n", + " var backingStore =\n", + " this.context.backingStorePixelRatio ||\n", + " this.context.webkitBackingStorePixelRatio ||\n", + " this.context.mozBackingStorePixelRatio ||\n", + " this.context.msBackingStorePixelRatio ||\n", + " this.context.oBackingStorePixelRatio ||\n", + " this.context.backingStorePixelRatio ||\n", + " 1;\n", + "\n", + " this.ratio = (window.devicePixelRatio || 1) / backingStore;\n", + "\n", + " var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n", + " 'canvas'\n", + " ));\n", + " rubberband_canvas.setAttribute(\n", + " 'style',\n", + " 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n", + " );\n", + "\n", + " // Apply a ponyfill if ResizeObserver is not implemented by browser.\n", + " if (this.ResizeObserver === undefined) {\n", + " if (window.ResizeObserver !== undefined) {\n", + " this.ResizeObserver = window.ResizeObserver;\n", + " } else {\n", + " var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n", + " this.ResizeObserver = obs.ResizeObserver;\n", + " }\n", + " }\n", + "\n", + " this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n", + " var nentries = entries.length;\n", + " for (var i = 0; i < nentries; i++) {\n", + " var entry = entries[i];\n", + " var width, height;\n", + " if (entry.contentBoxSize) {\n", + " if (entry.contentBoxSize instanceof Array) {\n", + " // Chrome 84 implements new version of spec.\n", + " width = entry.contentBoxSize[0].inlineSize;\n", + " height = entry.contentBoxSize[0].blockSize;\n", + " } else {\n", + " // Firefox implements old version of spec.\n", + " width = entry.contentBoxSize.inlineSize;\n", + " height = entry.contentBoxSize.blockSize;\n", + " }\n", + " } else {\n", + " // Chrome <84 implements even older version of spec.\n", + " width = entry.contentRect.width;\n", + " height = entry.contentRect.height;\n", + " }\n", + "\n", + " // Keep the size of the canvas and rubber band canvas in sync with\n", + " // the canvas container.\n", + " if (entry.devicePixelContentBoxSize) {\n", + " // Chrome 84 implements new version of spec.\n", + " canvas.setAttribute(\n", + " 'width',\n", + " entry.devicePixelContentBoxSize[0].inlineSize\n", + " );\n", + " canvas.setAttribute(\n", + " 'height',\n", + " entry.devicePixelContentBoxSize[0].blockSize\n", + " );\n", + " } else {\n", + " canvas.setAttribute('width', width * fig.ratio);\n", + " canvas.setAttribute('height', height * fig.ratio);\n", + " }\n", + " canvas.setAttribute(\n", + " 'style',\n", + " 'width: ' + width + 'px; height: ' + height + 'px;'\n", + " );\n", + "\n", + " rubberband_canvas.setAttribute('width', width);\n", + " rubberband_canvas.setAttribute('height', height);\n", + "\n", + " // And update the size in Python. We ignore the initial 0/0 size\n", + " // that occurs as the element is placed into the DOM, which should\n", + " // otherwise not happen due to the minimum size styling.\n", + " if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n", + " fig.request_resize(width, height);\n", + " }\n", + " }\n", + " });\n", + " this.resizeObserverInstance.observe(canvas_div);\n", + "\n", + " function on_mouse_event_closure(name) {\n", + " return function (event) {\n", + " return fig.mouse_event(event, name);\n", + " };\n", + " }\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mousedown',\n", + " on_mouse_event_closure('button_press')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseup',\n", + " on_mouse_event_closure('button_release')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'dblclick',\n", + " on_mouse_event_closure('dblclick')\n", + " );\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband_canvas.addEventListener(\n", + " 'mousemove',\n", + " on_mouse_event_closure('motion_notify')\n", + " );\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseenter',\n", + " on_mouse_event_closure('figure_enter')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseleave',\n", + " on_mouse_event_closure('figure_leave')\n", + " );\n", + "\n", + " canvas_div.addEventListener('wheel', function (event) {\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " on_mouse_event_closure('scroll')(event);\n", + " });\n", + "\n", + " canvas_div.appendChild(canvas);\n", + " canvas_div.appendChild(rubberband_canvas);\n", + "\n", + " this.rubberband_context = rubberband_canvas.getContext('2d');\n", + " this.rubberband_context.strokeStyle = '#000000';\n", + "\n", + " this._resize_canvas = function (width, height, forward) {\n", + " if (forward) {\n", + " canvas_div.style.width = width + 'px';\n", + " canvas_div.style.height = height + 'px';\n", + " }\n", + " };\n", + "\n", + " // Disable right mouse context menu.\n", + " this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n", + " event.preventDefault();\n", + " return false;\n", + " });\n", + "\n", + " function set_focus() {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'mpl-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " continue;\n", + " }\n", + "\n", + " var button = (fig.buttons[name] = document.createElement('button'));\n", + " button.classList = 'mpl-widget';\n", + " button.setAttribute('role', 'button');\n", + " button.setAttribute('aria-disabled', 'false');\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + "\n", + " var icon_img = document.createElement('img');\n", + " icon_img.src = '_images/' + image + '.png';\n", + " icon_img.srcset = '_images/' + image + '_large.png 2x';\n", + " icon_img.alt = tooltip;\n", + " button.appendChild(icon_img);\n", + "\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " var fmt_picker = document.createElement('select');\n", + " fmt_picker.classList = 'mpl-widget';\n", + " toolbar.appendChild(fmt_picker);\n", + " this.format_dropdown = fmt_picker;\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = document.createElement('option');\n", + " option.selected = fmt === mpl.default_extension;\n", + " option.innerHTML = fmt;\n", + " fmt_picker.appendChild(option);\n", + " }\n", + "\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "};\n", + "\n", + "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', { width: x_pixels, height: y_pixels });\n", + "};\n", + "\n", + "mpl.figure.prototype.send_message = function (type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "};\n", + "\n", + "mpl.figure.prototype.send_draw_message = function () {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " var format_dropdown = fig.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " fig.ondownload(fig, format);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_resize = function (fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1], msg['forward']);\n", + " fig.send_message('refresh', {});\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n", + " var x0 = msg['x0'] / fig.ratio;\n", + " var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n", + " var x1 = msg['x1'] / fig.ratio;\n", + " var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0,\n", + " 0,\n", + " fig.canvas.width / fig.ratio,\n", + " fig.canvas.height / fig.ratio\n", + " );\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", + " fig.rubberband_canvas.style.cursor = msg['cursor'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_message = function (fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n", + " for (var key in msg) {\n", + " if (!(key in fig.buttons)) {\n", + " continue;\n", + " }\n", + " fig.buttons[key].disabled = !msg[key];\n", + " fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n", + " if (msg['mode'] === 'PAN') {\n", + " fig.buttons['Pan'].classList.add('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " } else if (msg['mode'] === 'ZOOM') {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.add('active');\n", + " } else {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message('ack', {});\n", + "};\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function (fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " var img = evt.data;\n", + " if (img.type !== 'image/png') {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " img.type = 'image/png';\n", + " }\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src\n", + " );\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " img\n", + " );\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " } else if (\n", + " typeof evt.data === 'string' &&\n", + " evt.data.slice(0, 21) === 'data:image/png;base64'\n", + " ) {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig['handle_' + msg_type];\n", + " } catch (e) {\n", + " console.log(\n", + " \"No handler for the '\" + msg_type + \"' message type: \",\n", + " msg\n", + " );\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\n", + " \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n", + " e,\n", + " e.stack,\n", + " msg\n", + " );\n", + " }\n", + " }\n", + " };\n", + "};\n", + "\n", + "// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function (e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e) {\n", + " e = window.event;\n", + " }\n", + " if (e.target) {\n", + " targ = e.target;\n", + " } else if (e.srcElement) {\n", + " targ = e.srcElement;\n", + " }\n", + " if (targ.nodeType === 3) {\n", + " // defeat Safari bug\n", + " targ = targ.parentNode;\n", + " }\n", + "\n", + " // pageX,Y are the mouse positions relative to the document\n", + " var boundingRect = targ.getBoundingClientRect();\n", + " var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n", + " var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n", + "\n", + " return { x: x, y: y };\n", + "};\n", + "\n", + "/*\n", + " * return a copy of an object with only non-object keys\n", + " * we need this to avoid circular references\n", + " * https://stackoverflow.com/a/24161582/3208463\n", + " */\n", + "function simpleKeys(original) {\n", + " return Object.keys(original).reduce(function (obj, key) {\n", + " if (typeof original[key] !== 'object') {\n", + " obj[key] = original[key];\n", + " }\n", + " return obj;\n", + " }, {});\n", + "}\n", + "\n", + "mpl.figure.prototype.mouse_event = function (event, name) {\n", + " var canvas_pos = mpl.findpos(event);\n", + "\n", + " if (name === 'button_press') {\n", + " this.canvas.focus();\n", + " this.canvas_div.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x * this.ratio;\n", + " var y = canvas_pos.y * this.ratio;\n", + "\n", + " this.send_message(name, {\n", + " x: x,\n", + " y: y,\n", + " button: event.button,\n", + " step: event.step,\n", + " guiEvent: simpleKeys(event),\n", + " });\n", + "\n", + " /* This prevents the web browser from automatically changing to\n", + " * the text insertion cursor when the button is pressed. We want\n", + " * to control all of the cursor setting manually through the\n", + " * 'cursor' event from matplotlib */\n", + " event.preventDefault();\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n", + " // Handle any extra behaviour associated with a key event\n", + "};\n", + "\n", + "mpl.figure.prototype.key_event = function (event, name) {\n", + " // Prevent repeat events\n", + " if (name === 'key_press') {\n", + " if (event.key === this._key) {\n", + " return;\n", + " } else {\n", + " this._key = event.key;\n", + " }\n", + " }\n", + " if (name === 'key_release') {\n", + " this._key = null;\n", + " }\n", + "\n", + " var value = '';\n", + " if (event.ctrlKey && event.key !== 'Control') {\n", + " value += 'ctrl+';\n", + " }\n", + " else if (event.altKey && event.key !== 'Alt') {\n", + " value += 'alt+';\n", + " }\n", + " else if (event.shiftKey && event.key !== 'Shift') {\n", + " value += 'shift+';\n", + " }\n", + "\n", + " value += 'k' + event.key;\n", + "\n", + " this._key_event_extra(event, name);\n", + "\n", + " this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n", + " if (name === 'download') {\n", + " this.handle_save(this, null);\n", + " } else {\n", + " this.send_message('toolbar_button', { name: name });\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n", + " this.message.textContent = tooltip;\n", + "};\n", + "\n", + "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", + "// prettier-ignore\n", + "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";/* global mpl */\n", + "\n", + "var comm_websocket_adapter = function (comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.binaryType = comm.kernel.ws.binaryType;\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " function updateReadyState(_event) {\n", + " if (comm.kernel.ws) {\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " } else {\n", + " ws.readyState = 3; // Closed state.\n", + " }\n", + " }\n", + " comm.kernel.ws.addEventListener('open', updateReadyState);\n", + " comm.kernel.ws.addEventListener('close', updateReadyState);\n", + " comm.kernel.ws.addEventListener('error', updateReadyState);\n", + "\n", + " ws.close = function () {\n", + " comm.close();\n", + " };\n", + " ws.send = function (m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function (msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " var data = msg['content']['data'];\n", + " if (data['blob'] !== undefined) {\n", + " data = {\n", + " data: new Blob(msg['buffers'], { type: data['blob'] }),\n", + " };\n", + " }\n", + " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", + " ws.onmessage(data);\n", + " });\n", + " return ws;\n", + "};\n", + "\n", + "mpl.mpl_figure_comm = function (comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = document.getElementById(id);\n", + " var ws_proxy = comm_websocket_adapter(comm);\n", + "\n", + " function ondownload(figure, _format) {\n", + " window.open(figure.canvas.toDataURL());\n", + " }\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element;\n", + " fig.cell_info = mpl.find_output_cell(\"
\");\n", + " if (!fig.cell_info) {\n", + " console.error('Failed to find cell for figure', id, fig);\n", + " return;\n", + " }\n", + " fig.cell_info[0].output_area.element.on(\n", + " 'cleared',\n", + " { fig: fig },\n", + " fig._remove_fig_handler\n", + " );\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function (fig, msg) {\n", + " var width = fig.canvas.width / fig.ratio;\n", + " fig.cell_info[0].output_area.element.off(\n", + " 'cleared',\n", + " fig._remove_fig_handler\n", + " );\n", + " fig.resizeObserverInstance.unobserve(fig.canvas_div);\n", + "\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable();\n", + " fig.parent_element.innerHTML =\n", + " '';\n", + " fig.close_ws(fig, msg);\n", + "};\n", + "\n", + "mpl.figure.prototype.close_ws = function (fig, msg) {\n", + " fig.send_message('closing', msg);\n", + " // fig.ws.close()\n", + "};\n", + "\n", + "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n", + " // Turn the data on the canvas into data in the output cell.\n", + " var width = this.canvas.width / this.ratio;\n", + " var dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] =\n", + " '';\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message('ack', {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () {\n", + " fig.push_to_output();\n", + " }, 1000);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'btn-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " var button;\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " continue;\n", + " }\n", + "\n", + " button = fig.buttons[name] = document.createElement('button');\n", + " button.classList = 'btn btn-default';\n", + " button.href = '#';\n", + " button.title = name;\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message pull-right';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = document.createElement('div');\n", + " buttongrp.classList = 'btn-group inline pull-right';\n", + " button = document.createElement('button');\n", + " button.classList = 'btn btn-mini btn-primary';\n", + " button.href = '#';\n", + " button.title = 'Stop Interaction';\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', function (_evt) {\n", + " fig.handle_close(fig, {});\n", + " });\n", + " button.addEventListener(\n", + " 'mouseover',\n", + " on_mouseover_closure('Stop Interaction')\n", + " );\n", + " buttongrp.appendChild(button);\n", + " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", + " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", + "};\n", + "\n", + "mpl.figure.prototype._remove_fig_handler = function (event) {\n", + " var fig = event.data.fig;\n", + " if (event.target !== this) {\n", + " // Ignore bubbled events from children.\n", + " return;\n", + " }\n", + " fig.close_ws(fig, {});\n", + "};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (el) {\n", + " el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (el) {\n", + " // this is important to make the div 'focusable\n", + " el.setAttribute('tabindex', 0);\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " } else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which === 13) {\n", + " this.canvas_div.blur();\n", + " // select the cell after this one\n", + " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", + " IPython.notebook.select(index + 1);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " fig.ondownload(fig, null);\n", + "};\n", + "\n", + "mpl.find_output_cell = function (html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i = 0; i < ncells; i++) {\n", + " var cell = cells[i];\n", + " if (cell.cell_type === 'code') {\n", + " for (var j = 0; j < cell.output_area.outputs.length; j++) {\n", + " var data = cell.output_area.outputs[j];\n", + " if (data.data) {\n", + " // IPython >= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] === html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "};\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel !== null) {\n", + " IPython.notebook.kernel.comm_manager.register_target(\n", + " 'matplotlib',\n", + " mpl.mpl_figure_comm\n", + " );\n", + "}\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%matplotlib notebook\n", + "\n", + "# Read in results\n", + "## Params - Either specify a file prefix or a whole folder\n", + "file_prefix = None # Path(\"./results/2022-07-17T20:00:04_parameval_s3benchmark_\")\n", + "folder = Path(\"./results/param_study_s3benchmark\")\n", + "\n", + "## Plot code\n", + "path = file_prefix if file_prefix else folder\n", + "\n", + "metric = \"put_bw_in_mibi\"\n", + "df_1 = load_data(path, suffix=\"runtime.json\", metric=metric)\n", + "df_2 = load_data(path, suffix=\"threads.json\", metric=metric, x_labels=[\"1\", \"2\", \"4\", \"8\", \"16\", \"32\", \"64\", \"128\"])\n", + "\n", + "fig, axis = plt.subplots(2, 1)\n", + "fig.suptitle('Parameter Study: s3-benchmark')\n", + "\n", + " \n", + "df_1[\"mean\"].plot(\n", + " ax=axis[0],\n", + " kind='bar', rot=0,\n", + " xlabel='Runtime in s',\n", + " ylabel='Bandwidth (MiB/s)',\n", + " title='Variable Runtime',\n", + " yerr=df_1[\"std\"]\n", + ")\n", + "df_2[\"mean\"].plot(\n", + " ax=axis[1],\n", + " kind='bar', rot=0,\n", + " xlabel='Threads',\n", + " ylabel='Bandwidth (MiB/s)',\n", + " title='Variable Number of Threads',\n", + " yerr=df_2[\"std\"]\n", + ")\n", + "\n", + "plt.tight_layout()\n", + "plt.savefig(\"paramstudy_s3benchmark.png\",bbox_inches='tight')\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "ab9fc54e", + "metadata": {}, + "source": [ + "# Experiment: Parameter-evaluation: Warp" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "c3c99db2", + "metadata": {}, + "outputs": [ + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "/* global mpl */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function () {\n", + " if (typeof WebSocket !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof MozWebSocket !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert(\n", + " 'Your browser does not have WebSocket support. ' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.'\n", + " );\n", + " }\n", + "};\n", + "\n", + "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = this.ws.binaryType !== undefined;\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById('mpl-warnings');\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent =\n", + " 'This browser does not support binary websocket messages. ' +\n", + " 'Performance may be slow.';\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = document.createElement('div');\n", + " this.root.setAttribute('style', 'display: inline-block');\n", + " this._root_extra_style(this.root);\n", + "\n", + " parent_element.appendChild(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message('supports_binary', { value: fig.supports_binary });\n", + " fig.send_message('send_image_mode', {});\n", + " if (fig.ratio !== 1) {\n", + " fig.send_message('set_device_pixel_ratio', {\n", + " device_pixel_ratio: fig.ratio,\n", + " });\n", + " }\n", + " fig.send_message('refresh', {});\n", + " };\n", + "\n", + " this.imageObj.onload = function () {\n", + " if (fig.image_mode === 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function () {\n", + " fig.ws.close();\n", + " };\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "};\n", + "\n", + "mpl.figure.prototype._init_header = function () {\n", + " var titlebar = document.createElement('div');\n", + " titlebar.classList =\n", + " 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n", + " var titletext = document.createElement('div');\n", + " titletext.classList = 'ui-dialog-title';\n", + " titletext.setAttribute(\n", + " 'style',\n", + " 'width: 100%; text-align: center; padding: 3px;'\n", + " );\n", + " titlebar.appendChild(titletext);\n", + " this.root.appendChild(titlebar);\n", + " this.header = titletext;\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._init_canvas = function () {\n", + " var fig = this;\n", + "\n", + " var canvas_div = (this.canvas_div = document.createElement('div'));\n", + " canvas_div.setAttribute(\n", + " 'style',\n", + " 'border: 1px solid #ddd;' +\n", + " 'box-sizing: content-box;' +\n", + " 'clear: both;' +\n", + " 'min-height: 1px;' +\n", + " 'min-width: 1px;' +\n", + " 'outline: 0;' +\n", + " 'overflow: hidden;' +\n", + " 'position: relative;' +\n", + " 'resize: both;'\n", + " );\n", + "\n", + " function on_keyboard_event_closure(name) {\n", + " return function (event) {\n", + " return fig.key_event(event, name);\n", + " };\n", + " }\n", + "\n", + " canvas_div.addEventListener(\n", + " 'keydown',\n", + " on_keyboard_event_closure('key_press')\n", + " );\n", + " canvas_div.addEventListener(\n", + " 'keyup',\n", + " on_keyboard_event_closure('key_release')\n", + " );\n", + "\n", + " this._canvas_extra_style(canvas_div);\n", + " this.root.appendChild(canvas_div);\n", + "\n", + " var canvas = (this.canvas = document.createElement('canvas'));\n", + " canvas.classList.add('mpl-canvas');\n", + " canvas.setAttribute('style', 'box-sizing: content-box;');\n", + "\n", + " this.context = canvas.getContext('2d');\n", + "\n", + " var backingStore =\n", + " this.context.backingStorePixelRatio ||\n", + " this.context.webkitBackingStorePixelRatio ||\n", + " this.context.mozBackingStorePixelRatio ||\n", + " this.context.msBackingStorePixelRatio ||\n", + " this.context.oBackingStorePixelRatio ||\n", + " this.context.backingStorePixelRatio ||\n", + " 1;\n", + "\n", + " this.ratio = (window.devicePixelRatio || 1) / backingStore;\n", + "\n", + " var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n", + " 'canvas'\n", + " ));\n", + " rubberband_canvas.setAttribute(\n", + " 'style',\n", + " 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n", + " );\n", + "\n", + " // Apply a ponyfill if ResizeObserver is not implemented by browser.\n", + " if (this.ResizeObserver === undefined) {\n", + " if (window.ResizeObserver !== undefined) {\n", + " this.ResizeObserver = window.ResizeObserver;\n", + " } else {\n", + " var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n", + " this.ResizeObserver = obs.ResizeObserver;\n", + " }\n", + " }\n", + "\n", + " this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n", + " var nentries = entries.length;\n", + " for (var i = 0; i < nentries; i++) {\n", + " var entry = entries[i];\n", + " var width, height;\n", + " if (entry.contentBoxSize) {\n", + " if (entry.contentBoxSize instanceof Array) {\n", + " // Chrome 84 implements new version of spec.\n", + " width = entry.contentBoxSize[0].inlineSize;\n", + " height = entry.contentBoxSize[0].blockSize;\n", + " } else {\n", + " // Firefox implements old version of spec.\n", + " width = entry.contentBoxSize.inlineSize;\n", + " height = entry.contentBoxSize.blockSize;\n", + " }\n", + " } else {\n", + " // Chrome <84 implements even older version of spec.\n", + " width = entry.contentRect.width;\n", + " height = entry.contentRect.height;\n", + " }\n", + "\n", + " // Keep the size of the canvas and rubber band canvas in sync with\n", + " // the canvas container.\n", + " if (entry.devicePixelContentBoxSize) {\n", + " // Chrome 84 implements new version of spec.\n", + " canvas.setAttribute(\n", + " 'width',\n", + " entry.devicePixelContentBoxSize[0].inlineSize\n", + " );\n", + " canvas.setAttribute(\n", + " 'height',\n", + " entry.devicePixelContentBoxSize[0].blockSize\n", + " );\n", + " } else {\n", + " canvas.setAttribute('width', width * fig.ratio);\n", + " canvas.setAttribute('height', height * fig.ratio);\n", + " }\n", + " canvas.setAttribute(\n", + " 'style',\n", + " 'width: ' + width + 'px; height: ' + height + 'px;'\n", + " );\n", + "\n", + " rubberband_canvas.setAttribute('width', width);\n", + " rubberband_canvas.setAttribute('height', height);\n", + "\n", + " // And update the size in Python. We ignore the initial 0/0 size\n", + " // that occurs as the element is placed into the DOM, which should\n", + " // otherwise not happen due to the minimum size styling.\n", + " if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n", + " fig.request_resize(width, height);\n", + " }\n", + " }\n", + " });\n", + " this.resizeObserverInstance.observe(canvas_div);\n", + "\n", + " function on_mouse_event_closure(name) {\n", + " return function (event) {\n", + " return fig.mouse_event(event, name);\n", + " };\n", + " }\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mousedown',\n", + " on_mouse_event_closure('button_press')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseup',\n", + " on_mouse_event_closure('button_release')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'dblclick',\n", + " on_mouse_event_closure('dblclick')\n", + " );\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband_canvas.addEventListener(\n", + " 'mousemove',\n", + " on_mouse_event_closure('motion_notify')\n", + " );\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseenter',\n", + " on_mouse_event_closure('figure_enter')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseleave',\n", + " on_mouse_event_closure('figure_leave')\n", + " );\n", + "\n", + " canvas_div.addEventListener('wheel', function (event) {\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " on_mouse_event_closure('scroll')(event);\n", + " });\n", + "\n", + " canvas_div.appendChild(canvas);\n", + " canvas_div.appendChild(rubberband_canvas);\n", + "\n", + " this.rubberband_context = rubberband_canvas.getContext('2d');\n", + " this.rubberband_context.strokeStyle = '#000000';\n", + "\n", + " this._resize_canvas = function (width, height, forward) {\n", + " if (forward) {\n", + " canvas_div.style.width = width + 'px';\n", + " canvas_div.style.height = height + 'px';\n", + " }\n", + " };\n", + "\n", + " // Disable right mouse context menu.\n", + " this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n", + " event.preventDefault();\n", + " return false;\n", + " });\n", + "\n", + " function set_focus() {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'mpl-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " continue;\n", + " }\n", + "\n", + " var button = (fig.buttons[name] = document.createElement('button'));\n", + " button.classList = 'mpl-widget';\n", + " button.setAttribute('role', 'button');\n", + " button.setAttribute('aria-disabled', 'false');\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + "\n", + " var icon_img = document.createElement('img');\n", + " icon_img.src = '_images/' + image + '.png';\n", + " icon_img.srcset = '_images/' + image + '_large.png 2x';\n", + " icon_img.alt = tooltip;\n", + " button.appendChild(icon_img);\n", + "\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " var fmt_picker = document.createElement('select');\n", + " fmt_picker.classList = 'mpl-widget';\n", + " toolbar.appendChild(fmt_picker);\n", + " this.format_dropdown = fmt_picker;\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = document.createElement('option');\n", + " option.selected = fmt === mpl.default_extension;\n", + " option.innerHTML = fmt;\n", + " fmt_picker.appendChild(option);\n", + " }\n", + "\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "};\n", + "\n", + "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', { width: x_pixels, height: y_pixels });\n", + "};\n", + "\n", + "mpl.figure.prototype.send_message = function (type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "};\n", + "\n", + "mpl.figure.prototype.send_draw_message = function () {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " var format_dropdown = fig.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " fig.ondownload(fig, format);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_resize = function (fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1], msg['forward']);\n", + " fig.send_message('refresh', {});\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n", + " var x0 = msg['x0'] / fig.ratio;\n", + " var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n", + " var x1 = msg['x1'] / fig.ratio;\n", + " var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0,\n", + " 0,\n", + " fig.canvas.width / fig.ratio,\n", + " fig.canvas.height / fig.ratio\n", + " );\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", + " fig.rubberband_canvas.style.cursor = msg['cursor'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_message = function (fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n", + " for (var key in msg) {\n", + " if (!(key in fig.buttons)) {\n", + " continue;\n", + " }\n", + " fig.buttons[key].disabled = !msg[key];\n", + " fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n", + " if (msg['mode'] === 'PAN') {\n", + " fig.buttons['Pan'].classList.add('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " } else if (msg['mode'] === 'ZOOM') {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.add('active');\n", + " } else {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message('ack', {});\n", + "};\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function (fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " var img = evt.data;\n", + " if (img.type !== 'image/png') {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " img.type = 'image/png';\n", + " }\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src\n", + " );\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " img\n", + " );\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " } else if (\n", + " typeof evt.data === 'string' &&\n", + " evt.data.slice(0, 21) === 'data:image/png;base64'\n", + " ) {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig['handle_' + msg_type];\n", + " } catch (e) {\n", + " console.log(\n", + " \"No handler for the '\" + msg_type + \"' message type: \",\n", + " msg\n", + " );\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\n", + " \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n", + " e,\n", + " e.stack,\n", + " msg\n", + " );\n", + " }\n", + " }\n", + " };\n", + "};\n", + "\n", + "// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function (e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e) {\n", + " e = window.event;\n", + " }\n", + " if (e.target) {\n", + " targ = e.target;\n", + " } else if (e.srcElement) {\n", + " targ = e.srcElement;\n", + " }\n", + " if (targ.nodeType === 3) {\n", + " // defeat Safari bug\n", + " targ = targ.parentNode;\n", + " }\n", + "\n", + " // pageX,Y are the mouse positions relative to the document\n", + " var boundingRect = targ.getBoundingClientRect();\n", + " var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n", + " var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n", + "\n", + " return { x: x, y: y };\n", + "};\n", + "\n", + "/*\n", + " * return a copy of an object with only non-object keys\n", + " * we need this to avoid circular references\n", + " * https://stackoverflow.com/a/24161582/3208463\n", + " */\n", + "function simpleKeys(original) {\n", + " return Object.keys(original).reduce(function (obj, key) {\n", + " if (typeof original[key] !== 'object') {\n", + " obj[key] = original[key];\n", + " }\n", + " return obj;\n", + " }, {});\n", + "}\n", + "\n", + "mpl.figure.prototype.mouse_event = function (event, name) {\n", + " var canvas_pos = mpl.findpos(event);\n", + "\n", + " if (name === 'button_press') {\n", + " this.canvas.focus();\n", + " this.canvas_div.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x * this.ratio;\n", + " var y = canvas_pos.y * this.ratio;\n", + "\n", + " this.send_message(name, {\n", + " x: x,\n", + " y: y,\n", + " button: event.button,\n", + " step: event.step,\n", + " guiEvent: simpleKeys(event),\n", + " });\n", + "\n", + " /* This prevents the web browser from automatically changing to\n", + " * the text insertion cursor when the button is pressed. We want\n", + " * to control all of the cursor setting manually through the\n", + " * 'cursor' event from matplotlib */\n", + " event.preventDefault();\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n", + " // Handle any extra behaviour associated with a key event\n", + "};\n", + "\n", + "mpl.figure.prototype.key_event = function (event, name) {\n", + " // Prevent repeat events\n", + " if (name === 'key_press') {\n", + " if (event.key === this._key) {\n", + " return;\n", + " } else {\n", + " this._key = event.key;\n", + " }\n", + " }\n", + " if (name === 'key_release') {\n", + " this._key = null;\n", + " }\n", + "\n", + " var value = '';\n", + " if (event.ctrlKey && event.key !== 'Control') {\n", + " value += 'ctrl+';\n", + " }\n", + " else if (event.altKey && event.key !== 'Alt') {\n", + " value += 'alt+';\n", + " }\n", + " else if (event.shiftKey && event.key !== 'Shift') {\n", + " value += 'shift+';\n", + " }\n", + "\n", + " value += 'k' + event.key;\n", + "\n", + " this._key_event_extra(event, name);\n", + "\n", + " this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n", + " if (name === 'download') {\n", + " this.handle_save(this, null);\n", + " } else {\n", + " this.send_message('toolbar_button', { name: name });\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n", + " this.message.textContent = tooltip;\n", + "};\n", + "\n", + "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", + "// prettier-ignore\n", + "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";/* global mpl */\n", + "\n", + "var comm_websocket_adapter = function (comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.binaryType = comm.kernel.ws.binaryType;\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " function updateReadyState(_event) {\n", + " if (comm.kernel.ws) {\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " } else {\n", + " ws.readyState = 3; // Closed state.\n", + " }\n", + " }\n", + " comm.kernel.ws.addEventListener('open', updateReadyState);\n", + " comm.kernel.ws.addEventListener('close', updateReadyState);\n", + " comm.kernel.ws.addEventListener('error', updateReadyState);\n", + "\n", + " ws.close = function () {\n", + " comm.close();\n", + " };\n", + " ws.send = function (m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function (msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " var data = msg['content']['data'];\n", + " if (data['blob'] !== undefined) {\n", + " data = {\n", + " data: new Blob(msg['buffers'], { type: data['blob'] }),\n", + " };\n", + " }\n", + " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", + " ws.onmessage(data);\n", + " });\n", + " return ws;\n", + "};\n", + "\n", + "mpl.mpl_figure_comm = function (comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = document.getElementById(id);\n", + " var ws_proxy = comm_websocket_adapter(comm);\n", + "\n", + " function ondownload(figure, _format) {\n", + " window.open(figure.canvas.toDataURL());\n", + " }\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element;\n", + " fig.cell_info = mpl.find_output_cell(\"
\");\n", + " if (!fig.cell_info) {\n", + " console.error('Failed to find cell for figure', id, fig);\n", + " return;\n", + " }\n", + " fig.cell_info[0].output_area.element.on(\n", + " 'cleared',\n", + " { fig: fig },\n", + " fig._remove_fig_handler\n", + " );\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function (fig, msg) {\n", + " var width = fig.canvas.width / fig.ratio;\n", + " fig.cell_info[0].output_area.element.off(\n", + " 'cleared',\n", + " fig._remove_fig_handler\n", + " );\n", + " fig.resizeObserverInstance.unobserve(fig.canvas_div);\n", + "\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable();\n", + " fig.parent_element.innerHTML =\n", + " '';\n", + " fig.close_ws(fig, msg);\n", + "};\n", + "\n", + "mpl.figure.prototype.close_ws = function (fig, msg) {\n", + " fig.send_message('closing', msg);\n", + " // fig.ws.close()\n", + "};\n", + "\n", + "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n", + " // Turn the data on the canvas into data in the output cell.\n", + " var width = this.canvas.width / this.ratio;\n", + " var dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] =\n", + " '';\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message('ack', {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () {\n", + " fig.push_to_output();\n", + " }, 1000);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'btn-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " var button;\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " continue;\n", + " }\n", + "\n", + " button = fig.buttons[name] = document.createElement('button');\n", + " button.classList = 'btn btn-default';\n", + " button.href = '#';\n", + " button.title = name;\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message pull-right';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = document.createElement('div');\n", + " buttongrp.classList = 'btn-group inline pull-right';\n", + " button = document.createElement('button');\n", + " button.classList = 'btn btn-mini btn-primary';\n", + " button.href = '#';\n", + " button.title = 'Stop Interaction';\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', function (_evt) {\n", + " fig.handle_close(fig, {});\n", + " });\n", + " button.addEventListener(\n", + " 'mouseover',\n", + " on_mouseover_closure('Stop Interaction')\n", + " );\n", + " buttongrp.appendChild(button);\n", + " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", + " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", + "};\n", + "\n", + "mpl.figure.prototype._remove_fig_handler = function (event) {\n", + " var fig = event.data.fig;\n", + " if (event.target !== this) {\n", + " // Ignore bubbled events from children.\n", + " return;\n", + " }\n", + " fig.close_ws(fig, {});\n", + "};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (el) {\n", + " el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (el) {\n", + " // this is important to make the div 'focusable\n", + " el.setAttribute('tabindex', 0);\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " } else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which === 13) {\n", + " this.canvas_div.blur();\n", + " // select the cell after this one\n", + " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", + " IPython.notebook.select(index + 1);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " fig.ondownload(fig, null);\n", + "};\n", + "\n", + "mpl.find_output_cell = function (html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i = 0; i < ncells; i++) {\n", + " var cell = cells[i];\n", + " if (cell.cell_type === 'code') {\n", + " for (var j = 0; j < cell.output_area.outputs.length; j++) {\n", + " var data = cell.output_area.outputs[j];\n", + " if (data.data) {\n", + " // IPython >= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] === html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "};\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel !== null) {\n", + " IPython.notebook.kernel.comm_manager.register_target(\n", + " 'matplotlib',\n", + " mpl.mpl_figure_comm\n", + " );\n", + "}\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%matplotlib notebook\n", + "\n", + "# Read in results\n", + "## Params - Either specify a file prefix or a whole folder\n", + "file_prefix = None # Path(\"./results/2022-07-13T11:04:51_parameval_warp_\")\n", + "folder = Path(\"./results/param_study_warp\")\n", + "\n", + "## Plot code\n", + "path = file_prefix if file_prefix else folder\n", + "\n", + "metric = \"put_bw_in_MiB\"\n", + "df_1 = load_data(path, suffix=\"runtime.json\", metric=metric)\n", + "df_2 = load_data(path, suffix=\"concurrent.json\", metric=metric, x_labels=[\"1\", \"2\", \"4\", \"8\", \"16\", \"32\", \"64\"])\n", + "\n", + "fig, axis = plt.subplots(2, 1, sharey=True)\n", + "fig.suptitle('Parameter Study: WARP')\n", + "\n", + " \n", + "df_1[\"mean\"].plot(\n", + " ax=axis[0],\n", + " kind='bar', rot=0,\n", + " xlabel='Runtime in s',\n", + " ylabel='Bandwidth (MiB/s)',\n", + " title='Variable Runtime',\n", + " yerr=df_1[\"std\"]\n", + ")\n", + "df_2[\"mean\"].plot(\n", + " ax=axis[1],\n", + " kind='bar', rot=0,\n", + " xlabel='Number of Parallel Operations',\n", + " ylabel='Bandwidth (MiB/s)',\n", + " title='Variable Number of Threads',\n", + " yerr=df_2[\"std\"]\n", + ")\n", + "\n", + "plt.tight_layout()\n", + "plt.savefig(\"paramstudy_warp.png\",bbox_inches='tight')\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "66eb19e5", + "metadata": {}, + "source": [ + "# Experiment: Compare dd and fio" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "160204ff", + "metadata": {}, + "outputs": [ + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "/* global mpl */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function () {\n", + " if (typeof WebSocket !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof MozWebSocket !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert(\n", + " 'Your browser does not have WebSocket support. ' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.'\n", + " );\n", + " }\n", + "};\n", + "\n", + "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = this.ws.binaryType !== undefined;\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById('mpl-warnings');\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent =\n", + " 'This browser does not support binary websocket messages. ' +\n", + " 'Performance may be slow.';\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = document.createElement('div');\n", + " this.root.setAttribute('style', 'display: inline-block');\n", + " this._root_extra_style(this.root);\n", + "\n", + " parent_element.appendChild(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message('supports_binary', { value: fig.supports_binary });\n", + " fig.send_message('send_image_mode', {});\n", + " if (fig.ratio !== 1) {\n", + " fig.send_message('set_device_pixel_ratio', {\n", + " device_pixel_ratio: fig.ratio,\n", + " });\n", + " }\n", + " fig.send_message('refresh', {});\n", + " };\n", + "\n", + " this.imageObj.onload = function () {\n", + " if (fig.image_mode === 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function () {\n", + " fig.ws.close();\n", + " };\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "};\n", + "\n", + "mpl.figure.prototype._init_header = function () {\n", + " var titlebar = document.createElement('div');\n", + " titlebar.classList =\n", + " 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n", + " var titletext = document.createElement('div');\n", + " titletext.classList = 'ui-dialog-title';\n", + " titletext.setAttribute(\n", + " 'style',\n", + " 'width: 100%; text-align: center; padding: 3px;'\n", + " );\n", + " titlebar.appendChild(titletext);\n", + " this.root.appendChild(titlebar);\n", + " this.header = titletext;\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._init_canvas = function () {\n", + " var fig = this;\n", + "\n", + " var canvas_div = (this.canvas_div = document.createElement('div'));\n", + " canvas_div.setAttribute(\n", + " 'style',\n", + " 'border: 1px solid #ddd;' +\n", + " 'box-sizing: content-box;' +\n", + " 'clear: both;' +\n", + " 'min-height: 1px;' +\n", + " 'min-width: 1px;' +\n", + " 'outline: 0;' +\n", + " 'overflow: hidden;' +\n", + " 'position: relative;' +\n", + " 'resize: both;'\n", + " );\n", + "\n", + " function on_keyboard_event_closure(name) {\n", + " return function (event) {\n", + " return fig.key_event(event, name);\n", + " };\n", + " }\n", + "\n", + " canvas_div.addEventListener(\n", + " 'keydown',\n", + " on_keyboard_event_closure('key_press')\n", + " );\n", + " canvas_div.addEventListener(\n", + " 'keyup',\n", + " on_keyboard_event_closure('key_release')\n", + " );\n", + "\n", + " this._canvas_extra_style(canvas_div);\n", + " this.root.appendChild(canvas_div);\n", + "\n", + " var canvas = (this.canvas = document.createElement('canvas'));\n", + " canvas.classList.add('mpl-canvas');\n", + " canvas.setAttribute('style', 'box-sizing: content-box;');\n", + "\n", + " this.context = canvas.getContext('2d');\n", + "\n", + " var backingStore =\n", + " this.context.backingStorePixelRatio ||\n", + " this.context.webkitBackingStorePixelRatio ||\n", + " this.context.mozBackingStorePixelRatio ||\n", + " this.context.msBackingStorePixelRatio ||\n", + " this.context.oBackingStorePixelRatio ||\n", + " this.context.backingStorePixelRatio ||\n", + " 1;\n", + "\n", + " this.ratio = (window.devicePixelRatio || 1) / backingStore;\n", + "\n", + " var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n", + " 'canvas'\n", + " ));\n", + " rubberband_canvas.setAttribute(\n", + " 'style',\n", + " 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n", + " );\n", + "\n", + " // Apply a ponyfill if ResizeObserver is not implemented by browser.\n", + " if (this.ResizeObserver === undefined) {\n", + " if (window.ResizeObserver !== undefined) {\n", + " this.ResizeObserver = window.ResizeObserver;\n", + " } else {\n", + " var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n", + " this.ResizeObserver = obs.ResizeObserver;\n", + " }\n", + " }\n", + "\n", + " this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n", + " var nentries = entries.length;\n", + " for (var i = 0; i < nentries; i++) {\n", + " var entry = entries[i];\n", + " var width, height;\n", + " if (entry.contentBoxSize) {\n", + " if (entry.contentBoxSize instanceof Array) {\n", + " // Chrome 84 implements new version of spec.\n", + " width = entry.contentBoxSize[0].inlineSize;\n", + " height = entry.contentBoxSize[0].blockSize;\n", + " } else {\n", + " // Firefox implements old version of spec.\n", + " width = entry.contentBoxSize.inlineSize;\n", + " height = entry.contentBoxSize.blockSize;\n", + " }\n", + " } else {\n", + " // Chrome <84 implements even older version of spec.\n", + " width = entry.contentRect.width;\n", + " height = entry.contentRect.height;\n", + " }\n", + "\n", + " // Keep the size of the canvas and rubber band canvas in sync with\n", + " // the canvas container.\n", + " if (entry.devicePixelContentBoxSize) {\n", + " // Chrome 84 implements new version of spec.\n", + " canvas.setAttribute(\n", + " 'width',\n", + " entry.devicePixelContentBoxSize[0].inlineSize\n", + " );\n", + " canvas.setAttribute(\n", + " 'height',\n", + " entry.devicePixelContentBoxSize[0].blockSize\n", + " );\n", + " } else {\n", + " canvas.setAttribute('width', width * fig.ratio);\n", + " canvas.setAttribute('height', height * fig.ratio);\n", + " }\n", + " canvas.setAttribute(\n", + " 'style',\n", + " 'width: ' + width + 'px; height: ' + height + 'px;'\n", + " );\n", + "\n", + " rubberband_canvas.setAttribute('width', width);\n", + " rubberband_canvas.setAttribute('height', height);\n", + "\n", + " // And update the size in Python. We ignore the initial 0/0 size\n", + " // that occurs as the element is placed into the DOM, which should\n", + " // otherwise not happen due to the minimum size styling.\n", + " if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n", + " fig.request_resize(width, height);\n", + " }\n", + " }\n", + " });\n", + " this.resizeObserverInstance.observe(canvas_div);\n", + "\n", + " function on_mouse_event_closure(name) {\n", + " return function (event) {\n", + " return fig.mouse_event(event, name);\n", + " };\n", + " }\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mousedown',\n", + " on_mouse_event_closure('button_press')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseup',\n", + " on_mouse_event_closure('button_release')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'dblclick',\n", + " on_mouse_event_closure('dblclick')\n", + " );\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband_canvas.addEventListener(\n", + " 'mousemove',\n", + " on_mouse_event_closure('motion_notify')\n", + " );\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseenter',\n", + " on_mouse_event_closure('figure_enter')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseleave',\n", + " on_mouse_event_closure('figure_leave')\n", + " );\n", + "\n", + " canvas_div.addEventListener('wheel', function (event) {\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " on_mouse_event_closure('scroll')(event);\n", + " });\n", + "\n", + " canvas_div.appendChild(canvas);\n", + " canvas_div.appendChild(rubberband_canvas);\n", + "\n", + " this.rubberband_context = rubberband_canvas.getContext('2d');\n", + " this.rubberband_context.strokeStyle = '#000000';\n", + "\n", + " this._resize_canvas = function (width, height, forward) {\n", + " if (forward) {\n", + " canvas_div.style.width = width + 'px';\n", + " canvas_div.style.height = height + 'px';\n", + " }\n", + " };\n", + "\n", + " // Disable right mouse context menu.\n", + " this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n", + " event.preventDefault();\n", + " return false;\n", + " });\n", + "\n", + " function set_focus() {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'mpl-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " continue;\n", + " }\n", + "\n", + " var button = (fig.buttons[name] = document.createElement('button'));\n", + " button.classList = 'mpl-widget';\n", + " button.setAttribute('role', 'button');\n", + " button.setAttribute('aria-disabled', 'false');\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + "\n", + " var icon_img = document.createElement('img');\n", + " icon_img.src = '_images/' + image + '.png';\n", + " icon_img.srcset = '_images/' + image + '_large.png 2x';\n", + " icon_img.alt = tooltip;\n", + " button.appendChild(icon_img);\n", + "\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " var fmt_picker = document.createElement('select');\n", + " fmt_picker.classList = 'mpl-widget';\n", + " toolbar.appendChild(fmt_picker);\n", + " this.format_dropdown = fmt_picker;\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = document.createElement('option');\n", + " option.selected = fmt === mpl.default_extension;\n", + " option.innerHTML = fmt;\n", + " fmt_picker.appendChild(option);\n", + " }\n", + "\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "};\n", + "\n", + "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', { width: x_pixels, height: y_pixels });\n", + "};\n", + "\n", + "mpl.figure.prototype.send_message = function (type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "};\n", + "\n", + "mpl.figure.prototype.send_draw_message = function () {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " var format_dropdown = fig.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " fig.ondownload(fig, format);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_resize = function (fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1], msg['forward']);\n", + " fig.send_message('refresh', {});\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n", + " var x0 = msg['x0'] / fig.ratio;\n", + " var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n", + " var x1 = msg['x1'] / fig.ratio;\n", + " var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0,\n", + " 0,\n", + " fig.canvas.width / fig.ratio,\n", + " fig.canvas.height / fig.ratio\n", + " );\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", + " fig.rubberband_canvas.style.cursor = msg['cursor'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_message = function (fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n", + " for (var key in msg) {\n", + " if (!(key in fig.buttons)) {\n", + " continue;\n", + " }\n", + " fig.buttons[key].disabled = !msg[key];\n", + " fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n", + " if (msg['mode'] === 'PAN') {\n", + " fig.buttons['Pan'].classList.add('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " } else if (msg['mode'] === 'ZOOM') {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.add('active');\n", + " } else {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message('ack', {});\n", + "};\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function (fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " var img = evt.data;\n", + " if (img.type !== 'image/png') {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " img.type = 'image/png';\n", + " }\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src\n", + " );\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " img\n", + " );\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " } else if (\n", + " typeof evt.data === 'string' &&\n", + " evt.data.slice(0, 21) === 'data:image/png;base64'\n", + " ) {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig['handle_' + msg_type];\n", + " } catch (e) {\n", + " console.log(\n", + " \"No handler for the '\" + msg_type + \"' message type: \",\n", + " msg\n", + " );\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\n", + " \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n", + " e,\n", + " e.stack,\n", + " msg\n", + " );\n", + " }\n", + " }\n", + " };\n", + "};\n", + "\n", + "// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function (e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e) {\n", + " e = window.event;\n", + " }\n", + " if (e.target) {\n", + " targ = e.target;\n", + " } else if (e.srcElement) {\n", + " targ = e.srcElement;\n", + " }\n", + " if (targ.nodeType === 3) {\n", + " // defeat Safari bug\n", + " targ = targ.parentNode;\n", + " }\n", + "\n", + " // pageX,Y are the mouse positions relative to the document\n", + " var boundingRect = targ.getBoundingClientRect();\n", + " var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n", + " var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n", + "\n", + " return { x: x, y: y };\n", + "};\n", + "\n", + "/*\n", + " * return a copy of an object with only non-object keys\n", + " * we need this to avoid circular references\n", + " * https://stackoverflow.com/a/24161582/3208463\n", + " */\n", + "function simpleKeys(original) {\n", + " return Object.keys(original).reduce(function (obj, key) {\n", + " if (typeof original[key] !== 'object') {\n", + " obj[key] = original[key];\n", + " }\n", + " return obj;\n", + " }, {});\n", + "}\n", + "\n", + "mpl.figure.prototype.mouse_event = function (event, name) {\n", + " var canvas_pos = mpl.findpos(event);\n", + "\n", + " if (name === 'button_press') {\n", + " this.canvas.focus();\n", + " this.canvas_div.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x * this.ratio;\n", + " var y = canvas_pos.y * this.ratio;\n", + "\n", + " this.send_message(name, {\n", + " x: x,\n", + " y: y,\n", + " button: event.button,\n", + " step: event.step,\n", + " guiEvent: simpleKeys(event),\n", + " });\n", + "\n", + " /* This prevents the web browser from automatically changing to\n", + " * the text insertion cursor when the button is pressed. We want\n", + " * to control all of the cursor setting manually through the\n", + " * 'cursor' event from matplotlib */\n", + " event.preventDefault();\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n", + " // Handle any extra behaviour associated with a key event\n", + "};\n", + "\n", + "mpl.figure.prototype.key_event = function (event, name) {\n", + " // Prevent repeat events\n", + " if (name === 'key_press') {\n", + " if (event.key === this._key) {\n", + " return;\n", + " } else {\n", + " this._key = event.key;\n", + " }\n", + " }\n", + " if (name === 'key_release') {\n", + " this._key = null;\n", + " }\n", + "\n", + " var value = '';\n", + " if (event.ctrlKey && event.key !== 'Control') {\n", + " value += 'ctrl+';\n", + " }\n", + " else if (event.altKey && event.key !== 'Alt') {\n", + " value += 'alt+';\n", + " }\n", + " else if (event.shiftKey && event.key !== 'Shift') {\n", + " value += 'shift+';\n", + " }\n", + "\n", + " value += 'k' + event.key;\n", + "\n", + " this._key_event_extra(event, name);\n", + "\n", + " this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n", + " if (name === 'download') {\n", + " this.handle_save(this, null);\n", + " } else {\n", + " this.send_message('toolbar_button', { name: name });\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n", + " this.message.textContent = tooltip;\n", + "};\n", + "\n", + "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", + "// prettier-ignore\n", + "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";/* global mpl */\n", + "\n", + "var comm_websocket_adapter = function (comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.binaryType = comm.kernel.ws.binaryType;\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " function updateReadyState(_event) {\n", + " if (comm.kernel.ws) {\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " } else {\n", + " ws.readyState = 3; // Closed state.\n", + " }\n", + " }\n", + " comm.kernel.ws.addEventListener('open', updateReadyState);\n", + " comm.kernel.ws.addEventListener('close', updateReadyState);\n", + " comm.kernel.ws.addEventListener('error', updateReadyState);\n", + "\n", + " ws.close = function () {\n", + " comm.close();\n", + " };\n", + " ws.send = function (m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function (msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " var data = msg['content']['data'];\n", + " if (data['blob'] !== undefined) {\n", + " data = {\n", + " data: new Blob(msg['buffers'], { type: data['blob'] }),\n", + " };\n", + " }\n", + " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", + " ws.onmessage(data);\n", + " });\n", + " return ws;\n", + "};\n", + "\n", + "mpl.mpl_figure_comm = function (comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = document.getElementById(id);\n", + " var ws_proxy = comm_websocket_adapter(comm);\n", + "\n", + " function ondownload(figure, _format) {\n", + " window.open(figure.canvas.toDataURL());\n", + " }\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element;\n", + " fig.cell_info = mpl.find_output_cell(\"
\");\n", + " if (!fig.cell_info) {\n", + " console.error('Failed to find cell for figure', id, fig);\n", + " return;\n", + " }\n", + " fig.cell_info[0].output_area.element.on(\n", + " 'cleared',\n", + " { fig: fig },\n", + " fig._remove_fig_handler\n", + " );\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function (fig, msg) {\n", + " var width = fig.canvas.width / fig.ratio;\n", + " fig.cell_info[0].output_area.element.off(\n", + " 'cleared',\n", + " fig._remove_fig_handler\n", + " );\n", + " fig.resizeObserverInstance.unobserve(fig.canvas_div);\n", + "\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable();\n", + " fig.parent_element.innerHTML =\n", + " '';\n", + " fig.close_ws(fig, msg);\n", + "};\n", + "\n", + "mpl.figure.prototype.close_ws = function (fig, msg) {\n", + " fig.send_message('closing', msg);\n", + " // fig.ws.close()\n", + "};\n", + "\n", + "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n", + " // Turn the data on the canvas into data in the output cell.\n", + " var width = this.canvas.width / this.ratio;\n", + " var dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] =\n", + " '';\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message('ack', {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () {\n", + " fig.push_to_output();\n", + " }, 1000);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'btn-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " var button;\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " continue;\n", + " }\n", + "\n", + " button = fig.buttons[name] = document.createElement('button');\n", + " button.classList = 'btn btn-default';\n", + " button.href = '#';\n", + " button.title = name;\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message pull-right';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = document.createElement('div');\n", + " buttongrp.classList = 'btn-group inline pull-right';\n", + " button = document.createElement('button');\n", + " button.classList = 'btn btn-mini btn-primary';\n", + " button.href = '#';\n", + " button.title = 'Stop Interaction';\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', function (_evt) {\n", + " fig.handle_close(fig, {});\n", + " });\n", + " button.addEventListener(\n", + " 'mouseover',\n", + " on_mouseover_closure('Stop Interaction')\n", + " );\n", + " buttongrp.appendChild(button);\n", + " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", + " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", + "};\n", + "\n", + "mpl.figure.prototype._remove_fig_handler = function (event) {\n", + " var fig = event.data.fig;\n", + " if (event.target !== this) {\n", + " // Ignore bubbled events from children.\n", + " return;\n", + " }\n", + " fig.close_ws(fig, {});\n", + "};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (el) {\n", + " el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (el) {\n", + " // this is important to make the div 'focusable\n", + " el.setAttribute('tabindex', 0);\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " } else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which === 13) {\n", + " this.canvas_div.blur();\n", + " // select the cell after this one\n", + " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", + " IPython.notebook.select(index + 1);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " fig.ondownload(fig, null);\n", + "};\n", + "\n", + "mpl.find_output_cell = function (html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i = 0; i < ncells; i++) {\n", + " var cell = cells[i];\n", + " if (cell.cell_type === 'code') {\n", + " for (var j = 0; j < cell.output_area.outputs.length; j++) {\n", + " var data = cell.output_area.outputs[j];\n", + " if (data.data) {\n", + " // IPython >= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] === html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "};\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel !== null) {\n", + " IPython.notebook.kernel.comm_manager.register_target(\n", + " 'matplotlib',\n", + " mpl.mpl_figure_comm\n", + " );\n", + "}\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%matplotlib notebook\n", + "\n", + "# Read in results\n", + "## Params - Either specify a file prefix or a whole folder\n", + "file_prefix = None # Path(\"./results/2022-07-05T20:00:06_throughput_write_\")\n", + "folder = Path(\"./results/compare_dd_vs_fio\")\n", + "\n", + "## Plot code\n", + "path = file_prefix if file_prefix else folder\n", + "\n", + "df_isilon = pd.concat({\n", + " \"fio\": load_data(path, metric=\"write_bw_mean_in_MiB\", suffix=\"isilon_a.json\"),\n", + " \"dd\": load_data(path, metric=\"bw_in_MiB\", suffix=\"isilon_b.json\"),\n", + "})\n", + "\n", + "df_netapp = pd.concat({\n", + " \"fio\": load_data(path, metric=\"write_bw_mean_in_MiB\", suffix=\"netapp_a.json\"),\n", + " \"dd\": load_data(path, metric=\"bw_in_MiB\", suffix=\"netapp_b.json\"),\n", + "})\n", + "\n", + "fig, (ax1, ax2) = plt.subplots(1, 2, sharey=True)\n", + "fig.suptitle('Tool comparison: dd vs. fio')\n", + "\n", + "df_isilon[\"mean\"].unstack(level=0).plot(\n", + " ax=ax1,\n", + " kind='bar', rot=0,\n", + " xlabel='Transferred Data (GiB)',\n", + " ylabel='Bandwidth (MiB/s)',\n", + " title=f\"nfs_isilon\",\n", + " figsize=(8, 4),\n", + " yerr=df_isilon[\"std\"].unstack(level=0)\n", + ")\n", + "\n", + "df_netapp[\"mean\"].unstack(level=0).plot(\n", + " ax=ax2,\n", + " kind='bar', rot=0,\n", + " xlabel='Transferred Data (GiB)',\n", + " ylabel='Bandwidth (MiB/s)',\n", + " title=f\"nfs_netapp\",\n", + " figsize=(8, 4),\n", + " yerr=df_netapp[\"std\"].unstack(level=0)\n", + ")\n", + "\n", + "ax1.get_legend().remove()\n", + "plt.tight_layout()\n", + "plt.savefig(\"dd_vs_fio.png\",bbox_inches='tight')\n", + "plt.show()\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "1a1d799e", + "metadata": {}, + "source": [ + "# Experiment: Native vs Container" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "a8cfed3e", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " & & \\multicolumn{3}{c}{Native} & \\multicolumn{3}{c}{Container}\\\\\n", + "Bandwidth (MiB/s) & write & 669 & $\\pm$ & 195 & 652 & $\\pm$ & 182\\\\\n", + " & read & 543 & $\\pm$ & 161 & 542 & $\\pm$ & 157\\\\\n", + "IOPS (ops/s) & write & 3652 & $\\pm$ & 608 & 3796 & $\\pm$ & 599\\\\\n", + " & read & 22705 & $\\pm$ & 1107 & 22624 & $\\pm$ & 1196\\\\\n", + "Latency (ms) & write & 1.02 & $\\pm$ & 0.059 & 0.99 & $\\pm$ & 0.044\\\\\n", + " & read & 0.55 & $\\pm$ & 0.022 & 0.55 & $\\pm$ & 0.020\\\\\n" + ] + } + ], + "source": [ + "%matplotlib notebook\n", + "\n", + "# Read in results\n", + "## Params - Either specify a file prefix or a whole folder\n", + "file_prefix = None # Path(\"./results/2022-05-31T21:42:26_con_vs_nativ_\")\n", + "folder = Path(\"./results/compare_native_vs_container\")\n", + "\n", + "## Plot code\n", + "path = file_prefix if file_prefix else folder\n", + "\n", + "def load_df(benchmark):\n", + "\n", + " df = pd.concat([\n", + " load_data(path, suffix=benchmark+\"_con.json\", metric=METRIC[benchmark]).rename(index={\"con_vs_nativ_\"+benchmark+\"_con\": \"container\"}),\n", + " load_data(path, suffix=benchmark+\"_nativ.json\", metric=METRIC[benchmark]).rename(index={\"con_vs_nativ_\"+benchmark+\"_nativ\": \"nativ\"}),\n", + " ])\n", + "\n", + " return df\n", + "df_1 = load_df(\"throughput_write\")\n", + "df_2 = load_df(\"iops_write\")\n", + "df_3 = load_df(\"latency_write\")\n", + "df_4 = load_df(\"throughput_read\")\n", + "df_5 = load_df(\"iops_read\")\n", + "df_6 = load_df(\"latency_read\")\n", + "\n", + "def extract(df):\n", + " return (\n", + " df.loc['nativ'].at['mean'],\n", + " df.loc['nativ'].at['std'],\n", + " df.loc['container'].at['mean'],\n", + " df.loc['container'].at['std'],\n", + " )\n", + "\n", + "## {1}{2}{3}{4}\n", + "# 1: b=troughput, i=iops, l=latency\n", + "# 2: w=write, r=read\n", + "# 3: n=nativ, c=container\n", + "# 4: m=mean, c=conf\n", + " \n", + "bwnm, bwnc, bwcm, bwcc = extract(df_1)\n", + "brnm, brnc, brcm, brcc = extract(df_4)\n", + "iwnm, iwnc, iwcm, iwcc = extract(df_2)\n", + "irnm, irnc, ircm, ircc = extract(df_5)\n", + "lwnm, lwnc, lwcm, lwcc = extract(df_3)\n", + "lrnm, lrnc, lrcm, lrcc = extract(df_6)\n", + "\n", + "\n", + "tex_table([\n", + " [\"\", \"\", \"\\multicolumn{3}{c}{Native}\", \"\\multicolumn{3}{c}{Container}\"],\n", + " [\"Bandwidth (MiB/s)\", \"write\", f\"{bwnm:.0f}\", \"$\\pm$\", f\"{bwnc:.0f}\", f\"{bwcm:.0f}\", \"$\\pm$\", f\"{bwcc:.0f}\"],\n", + " [\"\" , \"read\" , f\"{brnm:.0f}\", \"$\\pm$\", f\"{brnc:.0f}\", f\"{brcm:.0f}\", \"$\\pm$\", f\"{brcc:.0f}\"],\n", + " [\"IOPS (ops/s)\" , \"write\", f\"{iwnm:.0f}\", \"$\\pm$\", f\"{iwnc:.0f}\", f\"{iwcm:.0f}\", \"$\\pm$\", f\"{iwcc:.0f}\"],\n", + " [\"\" , \"read\" , f\"{irnm:.0f}\", \"$\\pm$\", f\"{irnc:.0f}\", f\"{ircm:.0f}\", \"$\\pm$\", f\"{ircc:.0f}\"],\n", + " [\"Latency (ms)\" , \"write\", f\"{lwnm:.2f}\", \"$\\pm$\", f\"{lwnc:.3f}\", f\"{lwcm:.2f}\", \"$\\pm$\", f\"{lwcc:.3f}\"],\n", + " [\"\" , \"read\" , f\"{lrnm:.2f}\", \"$\\pm$\", f\"{lrnc:.3f}\", f\"{lrcm:.2f}\", \"$\\pm$\", f\"{lrcc:.3f}\"],\n", + "])" + ] + }, + { + "cell_type": "markdown", + "id": "59d156c3", + "metadata": {}, + "source": [ + "# Experiment: Compare S3Benchmark vs Warp" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "f4ad9d58", + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "/* global mpl */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function () {\n", + " if (typeof WebSocket !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof MozWebSocket !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert(\n", + " 'Your browser does not have WebSocket support. ' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.'\n", + " );\n", + " }\n", + "};\n", + "\n", + "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = this.ws.binaryType !== undefined;\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById('mpl-warnings');\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent =\n", + " 'This browser does not support binary websocket messages. ' +\n", + " 'Performance may be slow.';\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = document.createElement('div');\n", + " this.root.setAttribute('style', 'display: inline-block');\n", + " this._root_extra_style(this.root);\n", + "\n", + " parent_element.appendChild(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message('supports_binary', { value: fig.supports_binary });\n", + " fig.send_message('send_image_mode', {});\n", + " if (fig.ratio !== 1) {\n", + " fig.send_message('set_device_pixel_ratio', {\n", + " device_pixel_ratio: fig.ratio,\n", + " });\n", + " }\n", + " fig.send_message('refresh', {});\n", + " };\n", + "\n", + " this.imageObj.onload = function () {\n", + " if (fig.image_mode === 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function () {\n", + " fig.ws.close();\n", + " };\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "};\n", + "\n", + "mpl.figure.prototype._init_header = function () {\n", + " var titlebar = document.createElement('div');\n", + " titlebar.classList =\n", + " 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n", + " var titletext = document.createElement('div');\n", + " titletext.classList = 'ui-dialog-title';\n", + " titletext.setAttribute(\n", + " 'style',\n", + " 'width: 100%; text-align: center; padding: 3px;'\n", + " );\n", + " titlebar.appendChild(titletext);\n", + " this.root.appendChild(titlebar);\n", + " this.header = titletext;\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._init_canvas = function () {\n", + " var fig = this;\n", + "\n", + " var canvas_div = (this.canvas_div = document.createElement('div'));\n", + " canvas_div.setAttribute(\n", + " 'style',\n", + " 'border: 1px solid #ddd;' +\n", + " 'box-sizing: content-box;' +\n", + " 'clear: both;' +\n", + " 'min-height: 1px;' +\n", + " 'min-width: 1px;' +\n", + " 'outline: 0;' +\n", + " 'overflow: hidden;' +\n", + " 'position: relative;' +\n", + " 'resize: both;'\n", + " );\n", + "\n", + " function on_keyboard_event_closure(name) {\n", + " return function (event) {\n", + " return fig.key_event(event, name);\n", + " };\n", + " }\n", + "\n", + " canvas_div.addEventListener(\n", + " 'keydown',\n", + " on_keyboard_event_closure('key_press')\n", + " );\n", + " canvas_div.addEventListener(\n", + " 'keyup',\n", + " on_keyboard_event_closure('key_release')\n", + " );\n", + "\n", + " this._canvas_extra_style(canvas_div);\n", + " this.root.appendChild(canvas_div);\n", + "\n", + " var canvas = (this.canvas = document.createElement('canvas'));\n", + " canvas.classList.add('mpl-canvas');\n", + " canvas.setAttribute('style', 'box-sizing: content-box;');\n", + "\n", + " this.context = canvas.getContext('2d');\n", + "\n", + " var backingStore =\n", + " this.context.backingStorePixelRatio ||\n", + " this.context.webkitBackingStorePixelRatio ||\n", + " this.context.mozBackingStorePixelRatio ||\n", + " this.context.msBackingStorePixelRatio ||\n", + " this.context.oBackingStorePixelRatio ||\n", + " this.context.backingStorePixelRatio ||\n", + " 1;\n", + "\n", + " this.ratio = (window.devicePixelRatio || 1) / backingStore;\n", + "\n", + " var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n", + " 'canvas'\n", + " ));\n", + " rubberband_canvas.setAttribute(\n", + " 'style',\n", + " 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n", + " );\n", + "\n", + " // Apply a ponyfill if ResizeObserver is not implemented by browser.\n", + " if (this.ResizeObserver === undefined) {\n", + " if (window.ResizeObserver !== undefined) {\n", + " this.ResizeObserver = window.ResizeObserver;\n", + " } else {\n", + " var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n", + " this.ResizeObserver = obs.ResizeObserver;\n", + " }\n", + " }\n", + "\n", + " this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n", + " var nentries = entries.length;\n", + " for (var i = 0; i < nentries; i++) {\n", + " var entry = entries[i];\n", + " var width, height;\n", + " if (entry.contentBoxSize) {\n", + " if (entry.contentBoxSize instanceof Array) {\n", + " // Chrome 84 implements new version of spec.\n", + " width = entry.contentBoxSize[0].inlineSize;\n", + " height = entry.contentBoxSize[0].blockSize;\n", + " } else {\n", + " // Firefox implements old version of spec.\n", + " width = entry.contentBoxSize.inlineSize;\n", + " height = entry.contentBoxSize.blockSize;\n", + " }\n", + " } else {\n", + " // Chrome <84 implements even older version of spec.\n", + " width = entry.contentRect.width;\n", + " height = entry.contentRect.height;\n", + " }\n", + "\n", + " // Keep the size of the canvas and rubber band canvas in sync with\n", + " // the canvas container.\n", + " if (entry.devicePixelContentBoxSize) {\n", + " // Chrome 84 implements new version of spec.\n", + " canvas.setAttribute(\n", + " 'width',\n", + " entry.devicePixelContentBoxSize[0].inlineSize\n", + " );\n", + " canvas.setAttribute(\n", + " 'height',\n", + " entry.devicePixelContentBoxSize[0].blockSize\n", + " );\n", + " } else {\n", + " canvas.setAttribute('width', width * fig.ratio);\n", + " canvas.setAttribute('height', height * fig.ratio);\n", + " }\n", + " canvas.setAttribute(\n", + " 'style',\n", + " 'width: ' + width + 'px; height: ' + height + 'px;'\n", + " );\n", + "\n", + " rubberband_canvas.setAttribute('width', width);\n", + " rubberband_canvas.setAttribute('height', height);\n", + "\n", + " // And update the size in Python. We ignore the initial 0/0 size\n", + " // that occurs as the element is placed into the DOM, which should\n", + " // otherwise not happen due to the minimum size styling.\n", + " if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n", + " fig.request_resize(width, height);\n", + " }\n", + " }\n", + " });\n", + " this.resizeObserverInstance.observe(canvas_div);\n", + "\n", + " function on_mouse_event_closure(name) {\n", + " return function (event) {\n", + " return fig.mouse_event(event, name);\n", + " };\n", + " }\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mousedown',\n", + " on_mouse_event_closure('button_press')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseup',\n", + " on_mouse_event_closure('button_release')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'dblclick',\n", + " on_mouse_event_closure('dblclick')\n", + " );\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband_canvas.addEventListener(\n", + " 'mousemove',\n", + " on_mouse_event_closure('motion_notify')\n", + " );\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseenter',\n", + " on_mouse_event_closure('figure_enter')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseleave',\n", + " on_mouse_event_closure('figure_leave')\n", + " );\n", + "\n", + " canvas_div.addEventListener('wheel', function (event) {\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " on_mouse_event_closure('scroll')(event);\n", + " });\n", + "\n", + " canvas_div.appendChild(canvas);\n", + " canvas_div.appendChild(rubberband_canvas);\n", + "\n", + " this.rubberband_context = rubberband_canvas.getContext('2d');\n", + " this.rubberband_context.strokeStyle = '#000000';\n", + "\n", + " this._resize_canvas = function (width, height, forward) {\n", + " if (forward) {\n", + " canvas_div.style.width = width + 'px';\n", + " canvas_div.style.height = height + 'px';\n", + " }\n", + " };\n", + "\n", + " // Disable right mouse context menu.\n", + " this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n", + " event.preventDefault();\n", + " return false;\n", + " });\n", + "\n", + " function set_focus() {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'mpl-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " continue;\n", + " }\n", + "\n", + " var button = (fig.buttons[name] = document.createElement('button'));\n", + " button.classList = 'mpl-widget';\n", + " button.setAttribute('role', 'button');\n", + " button.setAttribute('aria-disabled', 'false');\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + "\n", + " var icon_img = document.createElement('img');\n", + " icon_img.src = '_images/' + image + '.png';\n", + " icon_img.srcset = '_images/' + image + '_large.png 2x';\n", + " icon_img.alt = tooltip;\n", + " button.appendChild(icon_img);\n", + "\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " var fmt_picker = document.createElement('select');\n", + " fmt_picker.classList = 'mpl-widget';\n", + " toolbar.appendChild(fmt_picker);\n", + " this.format_dropdown = fmt_picker;\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = document.createElement('option');\n", + " option.selected = fmt === mpl.default_extension;\n", + " option.innerHTML = fmt;\n", + " fmt_picker.appendChild(option);\n", + " }\n", + "\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "};\n", + "\n", + "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', { width: x_pixels, height: y_pixels });\n", + "};\n", + "\n", + "mpl.figure.prototype.send_message = function (type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "};\n", + "\n", + "mpl.figure.prototype.send_draw_message = function () {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " var format_dropdown = fig.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " fig.ondownload(fig, format);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_resize = function (fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1], msg['forward']);\n", + " fig.send_message('refresh', {});\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n", + " var x0 = msg['x0'] / fig.ratio;\n", + " var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n", + " var x1 = msg['x1'] / fig.ratio;\n", + " var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0,\n", + " 0,\n", + " fig.canvas.width / fig.ratio,\n", + " fig.canvas.height / fig.ratio\n", + " );\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", + " fig.rubberband_canvas.style.cursor = msg['cursor'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_message = function (fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n", + " for (var key in msg) {\n", + " if (!(key in fig.buttons)) {\n", + " continue;\n", + " }\n", + " fig.buttons[key].disabled = !msg[key];\n", + " fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n", + " if (msg['mode'] === 'PAN') {\n", + " fig.buttons['Pan'].classList.add('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " } else if (msg['mode'] === 'ZOOM') {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.add('active');\n", + " } else {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message('ack', {});\n", + "};\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function (fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " var img = evt.data;\n", + " if (img.type !== 'image/png') {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " img.type = 'image/png';\n", + " }\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src\n", + " );\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " img\n", + " );\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " } else if (\n", + " typeof evt.data === 'string' &&\n", + " evt.data.slice(0, 21) === 'data:image/png;base64'\n", + " ) {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig['handle_' + msg_type];\n", + " } catch (e) {\n", + " console.log(\n", + " \"No handler for the '\" + msg_type + \"' message type: \",\n", + " msg\n", + " );\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\n", + " \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n", + " e,\n", + " e.stack,\n", + " msg\n", + " );\n", + " }\n", + " }\n", + " };\n", + "};\n", + "\n", + "// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function (e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e) {\n", + " e = window.event;\n", + " }\n", + " if (e.target) {\n", + " targ = e.target;\n", + " } else if (e.srcElement) {\n", + " targ = e.srcElement;\n", + " }\n", + " if (targ.nodeType === 3) {\n", + " // defeat Safari bug\n", + " targ = targ.parentNode;\n", + " }\n", + "\n", + " // pageX,Y are the mouse positions relative to the document\n", + " var boundingRect = targ.getBoundingClientRect();\n", + " var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n", + " var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n", + "\n", + " return { x: x, y: y };\n", + "};\n", + "\n", + "/*\n", + " * return a copy of an object with only non-object keys\n", + " * we need this to avoid circular references\n", + " * https://stackoverflow.com/a/24161582/3208463\n", + " */\n", + "function simpleKeys(original) {\n", + " return Object.keys(original).reduce(function (obj, key) {\n", + " if (typeof original[key] !== 'object') {\n", + " obj[key] = original[key];\n", + " }\n", + " return obj;\n", + " }, {});\n", + "}\n", + "\n", + "mpl.figure.prototype.mouse_event = function (event, name) {\n", + " var canvas_pos = mpl.findpos(event);\n", + "\n", + " if (name === 'button_press') {\n", + " this.canvas.focus();\n", + " this.canvas_div.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x * this.ratio;\n", + " var y = canvas_pos.y * this.ratio;\n", + "\n", + " this.send_message(name, {\n", + " x: x,\n", + " y: y,\n", + " button: event.button,\n", + " step: event.step,\n", + " guiEvent: simpleKeys(event),\n", + " });\n", + "\n", + " /* This prevents the web browser from automatically changing to\n", + " * the text insertion cursor when the button is pressed. We want\n", + " * to control all of the cursor setting manually through the\n", + " * 'cursor' event from matplotlib */\n", + " event.preventDefault();\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n", + " // Handle any extra behaviour associated with a key event\n", + "};\n", + "\n", + "mpl.figure.prototype.key_event = function (event, name) {\n", + " // Prevent repeat events\n", + " if (name === 'key_press') {\n", + " if (event.key === this._key) {\n", + " return;\n", + " } else {\n", + " this._key = event.key;\n", + " }\n", + " }\n", + " if (name === 'key_release') {\n", + " this._key = null;\n", + " }\n", + "\n", + " var value = '';\n", + " if (event.ctrlKey && event.key !== 'Control') {\n", + " value += 'ctrl+';\n", + " }\n", + " else if (event.altKey && event.key !== 'Alt') {\n", + " value += 'alt+';\n", + " }\n", + " else if (event.shiftKey && event.key !== 'Shift') {\n", + " value += 'shift+';\n", + " }\n", + "\n", + " value += 'k' + event.key;\n", + "\n", + " this._key_event_extra(event, name);\n", + "\n", + " this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n", + " if (name === 'download') {\n", + " this.handle_save(this, null);\n", + " } else {\n", + " this.send_message('toolbar_button', { name: name });\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n", + " this.message.textContent = tooltip;\n", + "};\n", + "\n", + "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", + "// prettier-ignore\n", + "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";/* global mpl */\n", + "\n", + "var comm_websocket_adapter = function (comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.binaryType = comm.kernel.ws.binaryType;\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " function updateReadyState(_event) {\n", + " if (comm.kernel.ws) {\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " } else {\n", + " ws.readyState = 3; // Closed state.\n", + " }\n", + " }\n", + " comm.kernel.ws.addEventListener('open', updateReadyState);\n", + " comm.kernel.ws.addEventListener('close', updateReadyState);\n", + " comm.kernel.ws.addEventListener('error', updateReadyState);\n", + "\n", + " ws.close = function () {\n", + " comm.close();\n", + " };\n", + " ws.send = function (m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function (msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " var data = msg['content']['data'];\n", + " if (data['blob'] !== undefined) {\n", + " data = {\n", + " data: new Blob(msg['buffers'], { type: data['blob'] }),\n", + " };\n", + " }\n", + " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", + " ws.onmessage(data);\n", + " });\n", + " return ws;\n", + "};\n", + "\n", + "mpl.mpl_figure_comm = function (comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = document.getElementById(id);\n", + " var ws_proxy = comm_websocket_adapter(comm);\n", + "\n", + " function ondownload(figure, _format) {\n", + " window.open(figure.canvas.toDataURL());\n", + " }\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element;\n", + " fig.cell_info = mpl.find_output_cell(\"
\");\n", + " if (!fig.cell_info) {\n", + " console.error('Failed to find cell for figure', id, fig);\n", + " return;\n", + " }\n", + " fig.cell_info[0].output_area.element.on(\n", + " 'cleared',\n", + " { fig: fig },\n", + " fig._remove_fig_handler\n", + " );\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function (fig, msg) {\n", + " var width = fig.canvas.width / fig.ratio;\n", + " fig.cell_info[0].output_area.element.off(\n", + " 'cleared',\n", + " fig._remove_fig_handler\n", + " );\n", + " fig.resizeObserverInstance.unobserve(fig.canvas_div);\n", + "\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable();\n", + " fig.parent_element.innerHTML =\n", + " '';\n", + " fig.close_ws(fig, msg);\n", + "};\n", + "\n", + "mpl.figure.prototype.close_ws = function (fig, msg) {\n", + " fig.send_message('closing', msg);\n", + " // fig.ws.close()\n", + "};\n", + "\n", + "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n", + " // Turn the data on the canvas into data in the output cell.\n", + " var width = this.canvas.width / this.ratio;\n", + " var dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] =\n", + " '';\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message('ack', {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () {\n", + " fig.push_to_output();\n", + " }, 1000);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'btn-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " var button;\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " continue;\n", + " }\n", + "\n", + " button = fig.buttons[name] = document.createElement('button');\n", + " button.classList = 'btn btn-default';\n", + " button.href = '#';\n", + " button.title = name;\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message pull-right';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = document.createElement('div');\n", + " buttongrp.classList = 'btn-group inline pull-right';\n", + " button = document.createElement('button');\n", + " button.classList = 'btn btn-mini btn-primary';\n", + " button.href = '#';\n", + " button.title = 'Stop Interaction';\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', function (_evt) {\n", + " fig.handle_close(fig, {});\n", + " });\n", + " button.addEventListener(\n", + " 'mouseover',\n", + " on_mouseover_closure('Stop Interaction')\n", + " );\n", + " buttongrp.appendChild(button);\n", + " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", + " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", + "};\n", + "\n", + "mpl.figure.prototype._remove_fig_handler = function (event) {\n", + " var fig = event.data.fig;\n", + " if (event.target !== this) {\n", + " // Ignore bubbled events from children.\n", + " return;\n", + " }\n", + " fig.close_ws(fig, {});\n", + "};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (el) {\n", + " el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (el) {\n", + " // this is important to make the div 'focusable\n", + " el.setAttribute('tabindex', 0);\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " } else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which === 13) {\n", + " this.canvas_div.blur();\n", + " // select the cell after this one\n", + " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", + " IPython.notebook.select(index + 1);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " fig.ondownload(fig, null);\n", + "};\n", + "\n", + "mpl.find_output_cell = function (html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i = 0; i < ncells; i++) {\n", + " var cell = cells[i];\n", + " if (cell.cell_type === 'code') {\n", + " for (var j = 0; j < cell.output_area.outputs.length; j++) {\n", + " var data = cell.output_area.outputs[j];\n", + " if (data.data) {\n", + " // IPython >= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] === html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "};\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel !== null) {\n", + " IPython.notebook.kernel.comm_manager.register_target(\n", + " 'matplotlib',\n", + " mpl.mpl_figure_comm\n", + " );\n", + "}\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " mean std num\n", + "s3-benchmark GET 708.292857 208.375645 14\n", + "WARP GET 4542.232857 77.069324 14\n", + "s3-benchmark PUT 802.750000 34.320751 14\n", + "WARP PUT 1105.368571 81.658908 14\n", + "s3-benchmark DELETE 739.635714 196.806662 14\n", + "WARP DELETE 2425.240714 155.261849 14\n" + ] + } + ], + "source": [ + "%matplotlib notebook\n", + "\n", + "# Read in results\n", + "## Params - Either specify a file prefix or a whole folder\n", + "file_prefix = None # Path(\"./results/2022-07-05T20:00:06_throughput_write_\")\n", + "folder = Path(\"./results/compare_s3benchmark_vs_warp\")\n", + "\n", + "## Plot code\n", + "path = file_prefix if file_prefix else folder\n", + "\n", + "\n", + "df_get = pd.concat({\n", + " \"s3-benchmark\": load_data(path, metric=\"get_bw_in_mibi\", suffix=\"_get_put_a.json\"),\n", + " \"WARP\": load_data(path, metric=\"get_bw_in_MiB\", suffix=\"_get_put_b.json\"),\n", + "})\n", + "df_get.rename(index={\"s3benchmark_vs_warp_get_put_a\": \"GET\", \"s3benchmark_vs_warp_get_put_b\": \"GET\"}, inplace=True)\n", + "\n", + "df_put = pd.concat({\n", + " \"s3-benchmark\": load_data(path, metric=\"put_bw_in_mibi\", suffix=\"_get_put_a.json\"),\n", + " \"WARP\": load_data(path, metric=\"put_bw_in_MiB\", suffix=\"_get_put_b.json\"),\n", + "})\n", + "df_put.rename(index={\"s3benchmark_vs_warp_get_put_a\": \"PUT\", \"s3benchmark_vs_warp_get_put_b\": \"PUT\"}, inplace=True)\n", + "\n", + "df_get_put = pd.concat([df_get, df_put])\n", + "\n", + "\n", + "df_get_ops = pd.concat({\n", + " \"s3-benchmark\": load_data(path, metric=\"get_op_per_s\", suffix=\"get_put_ops_a.json\"),\n", + " \"WARP\": load_data(path, metric=\"get_ops\", suffix=\"get_put_ops_b.json\"),\n", + "})\n", + "df_put_ops = pd.concat({\n", + " \"s3-benchmark\": load_data(path, metric=\"put_op_per_s\", suffix=\"get_put_ops_a.json\"),\n", + " \"WARP\": load_data(path, metric=\"put_ops\", suffix=\"get_put_ops_b.json\"),\n", + "})\n", + "df_del_ops = pd.concat({\n", + " \"s3-benchmark\": load_data(path, metric=\"del_op_per_s\", suffix=\"delete_a.json\"),\n", + " \"WARP\": load_data(path, metric=\"delete_ops\", suffix=\"delete_b.json\"),\n", + "})\n", + "df_get_ops.rename(index={\"s3benchmark_vs_warp_get_put_ops_a\": \"GET\", \"s3benchmark_vs_warp_get_put_ops_b\": \"GET\"}, inplace=True)\n", + "df_put_ops.rename(index={\"s3benchmark_vs_warp_get_put_ops_a\": \"PUT\", \"s3benchmark_vs_warp_get_put_ops_b\": \"PUT\"}, inplace=True)\n", + "df_del_ops.rename(index={\"s3benchmark_vs_warp_delete_a\": \"DELETE\", \"s3benchmark_vs_warp_delete_b\": \"DELETE\"}, inplace=True)\n", + "\n", + "df_ops = pd.concat([df_get_ops, df_put_ops, df_del_ops])\n", + "\n", + "fig, (ax1, ax2) = plt.subplots(1, 2)\n", + "fig.suptitle('Tool comparison: s3-benchmark vs. WARP')\n", + "\n", + "i2 = [\"GET\",\"PUT\"]\n", + "i3 = [\"GET\",\"PUT\",\"DELETE\"]\n", + "\n", + "def fix_df(df, index):\n", + " \"\"\"unstack changes the order, fix order and rename index'\"\"\"\n", + " df = df.reindex(columns=[\"s3-benchmark\",\"WARP\"], index=index)\n", + " return df\n", + "\n", + "fix_df(df_get_put[\"mean\"].unstack(level=0), i2).plot(\n", + " ax=ax1,\n", + " kind='bar', rot=0,\n", + " xlabel='Mode',\n", + " ylabel='Bandwidth (MiB/s)',\n", + " title=f\"Bandwidth\",\n", + " yerr=fix_df(df_get_put[\"std\"].unstack(level=0), i2)\n", + ")\n", + "\n", + "fix_df(df_ops[\"mean\"].unstack(level=0), i3).plot(\n", + " ax=ax2,\n", + " kind='bar', rot=0,\n", + " xlabel='',\n", + " ylabel='Operations ($1/s$)',\n", + " title=\"Operations\",\n", + " yerr=fix_df(df_ops[\"std\"].unstack(level=0), i3)\n", + ")\n", + "print(df_ops)\n", + "\n", + "ax1.get_legend().remove()\n", + "ax2.legend(loc=\"center right\")\n", + "plt.tight_layout()\n", + "plt.savefig(\"s3benchmark_vs_warp.png\",bbox_inches='tight')\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "25d0706c", + "metadata": {}, + "source": [ + "# Experiment: Daytime evaluation" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "4fccebe5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Netapp A400 & 217 & 539 & 383 & 4 & 55\\\\\n", + " & 191 & 540 & 384 & 4 & 57\\\\\n", + "Isilon & 250 & 1036 & 791 & 13 & 172\\\\\n", + " & 297 & 1034 & 785 & 14 & 172\\\\\n", + "Netapp StorageGRID & 428 & 1110 & 1053 & 6 & 78\\\\\n", + " & 715 & 1110 & 1055 & 6 & 70\\\\\n" + ] + }, + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "/* global mpl */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function () {\n", + " if (typeof WebSocket !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof MozWebSocket !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert(\n", + " 'Your browser does not have WebSocket support. ' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.'\n", + " );\n", + " }\n", + "};\n", + "\n", + "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = this.ws.binaryType !== undefined;\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById('mpl-warnings');\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent =\n", + " 'This browser does not support binary websocket messages. ' +\n", + " 'Performance may be slow.';\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = document.createElement('div');\n", + " this.root.setAttribute('style', 'display: inline-block');\n", + " this._root_extra_style(this.root);\n", + "\n", + " parent_element.appendChild(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message('supports_binary', { value: fig.supports_binary });\n", + " fig.send_message('send_image_mode', {});\n", + " if (fig.ratio !== 1) {\n", + " fig.send_message('set_device_pixel_ratio', {\n", + " device_pixel_ratio: fig.ratio,\n", + " });\n", + " }\n", + " fig.send_message('refresh', {});\n", + " };\n", + "\n", + " this.imageObj.onload = function () {\n", + " if (fig.image_mode === 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function () {\n", + " fig.ws.close();\n", + " };\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "};\n", + "\n", + "mpl.figure.prototype._init_header = function () {\n", + " var titlebar = document.createElement('div');\n", + " titlebar.classList =\n", + " 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n", + " var titletext = document.createElement('div');\n", + " titletext.classList = 'ui-dialog-title';\n", + " titletext.setAttribute(\n", + " 'style',\n", + " 'width: 100%; text-align: center; padding: 3px;'\n", + " );\n", + " titlebar.appendChild(titletext);\n", + " this.root.appendChild(titlebar);\n", + " this.header = titletext;\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._init_canvas = function () {\n", + " var fig = this;\n", + "\n", + " var canvas_div = (this.canvas_div = document.createElement('div'));\n", + " canvas_div.setAttribute(\n", + " 'style',\n", + " 'border: 1px solid #ddd;' +\n", + " 'box-sizing: content-box;' +\n", + " 'clear: both;' +\n", + " 'min-height: 1px;' +\n", + " 'min-width: 1px;' +\n", + " 'outline: 0;' +\n", + " 'overflow: hidden;' +\n", + " 'position: relative;' +\n", + " 'resize: both;'\n", + " );\n", + "\n", + " function on_keyboard_event_closure(name) {\n", + " return function (event) {\n", + " return fig.key_event(event, name);\n", + " };\n", + " }\n", + "\n", + " canvas_div.addEventListener(\n", + " 'keydown',\n", + " on_keyboard_event_closure('key_press')\n", + " );\n", + " canvas_div.addEventListener(\n", + " 'keyup',\n", + " on_keyboard_event_closure('key_release')\n", + " );\n", + "\n", + " this._canvas_extra_style(canvas_div);\n", + " this.root.appendChild(canvas_div);\n", + "\n", + " var canvas = (this.canvas = document.createElement('canvas'));\n", + " canvas.classList.add('mpl-canvas');\n", + " canvas.setAttribute('style', 'box-sizing: content-box;');\n", + "\n", + " this.context = canvas.getContext('2d');\n", + "\n", + " var backingStore =\n", + " this.context.backingStorePixelRatio ||\n", + " this.context.webkitBackingStorePixelRatio ||\n", + " this.context.mozBackingStorePixelRatio ||\n", + " this.context.msBackingStorePixelRatio ||\n", + " this.context.oBackingStorePixelRatio ||\n", + " this.context.backingStorePixelRatio ||\n", + " 1;\n", + "\n", + " this.ratio = (window.devicePixelRatio || 1) / backingStore;\n", + "\n", + " var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n", + " 'canvas'\n", + " ));\n", + " rubberband_canvas.setAttribute(\n", + " 'style',\n", + " 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n", + " );\n", + "\n", + " // Apply a ponyfill if ResizeObserver is not implemented by browser.\n", + " if (this.ResizeObserver === undefined) {\n", + " if (window.ResizeObserver !== undefined) {\n", + " this.ResizeObserver = window.ResizeObserver;\n", + " } else {\n", + " var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n", + " this.ResizeObserver = obs.ResizeObserver;\n", + " }\n", + " }\n", + "\n", + " this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n", + " var nentries = entries.length;\n", + " for (var i = 0; i < nentries; i++) {\n", + " var entry = entries[i];\n", + " var width, height;\n", + " if (entry.contentBoxSize) {\n", + " if (entry.contentBoxSize instanceof Array) {\n", + " // Chrome 84 implements new version of spec.\n", + " width = entry.contentBoxSize[0].inlineSize;\n", + " height = entry.contentBoxSize[0].blockSize;\n", + " } else {\n", + " // Firefox implements old version of spec.\n", + " width = entry.contentBoxSize.inlineSize;\n", + " height = entry.contentBoxSize.blockSize;\n", + " }\n", + " } else {\n", + " // Chrome <84 implements even older version of spec.\n", + " width = entry.contentRect.width;\n", + " height = entry.contentRect.height;\n", + " }\n", + "\n", + " // Keep the size of the canvas and rubber band canvas in sync with\n", + " // the canvas container.\n", + " if (entry.devicePixelContentBoxSize) {\n", + " // Chrome 84 implements new version of spec.\n", + " canvas.setAttribute(\n", + " 'width',\n", + " entry.devicePixelContentBoxSize[0].inlineSize\n", + " );\n", + " canvas.setAttribute(\n", + " 'height',\n", + " entry.devicePixelContentBoxSize[0].blockSize\n", + " );\n", + " } else {\n", + " canvas.setAttribute('width', width * fig.ratio);\n", + " canvas.setAttribute('height', height * fig.ratio);\n", + " }\n", + " canvas.setAttribute(\n", + " 'style',\n", + " 'width: ' + width + 'px; height: ' + height + 'px;'\n", + " );\n", + "\n", + " rubberband_canvas.setAttribute('width', width);\n", + " rubberband_canvas.setAttribute('height', height);\n", + "\n", + " // And update the size in Python. We ignore the initial 0/0 size\n", + " // that occurs as the element is placed into the DOM, which should\n", + " // otherwise not happen due to the minimum size styling.\n", + " if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n", + " fig.request_resize(width, height);\n", + " }\n", + " }\n", + " });\n", + " this.resizeObserverInstance.observe(canvas_div);\n", + "\n", + " function on_mouse_event_closure(name) {\n", + " return function (event) {\n", + " return fig.mouse_event(event, name);\n", + " };\n", + " }\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mousedown',\n", + " on_mouse_event_closure('button_press')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseup',\n", + " on_mouse_event_closure('button_release')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'dblclick',\n", + " on_mouse_event_closure('dblclick')\n", + " );\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband_canvas.addEventListener(\n", + " 'mousemove',\n", + " on_mouse_event_closure('motion_notify')\n", + " );\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseenter',\n", + " on_mouse_event_closure('figure_enter')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseleave',\n", + " on_mouse_event_closure('figure_leave')\n", + " );\n", + "\n", + " canvas_div.addEventListener('wheel', function (event) {\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " on_mouse_event_closure('scroll')(event);\n", + " });\n", + "\n", + " canvas_div.appendChild(canvas);\n", + " canvas_div.appendChild(rubberband_canvas);\n", + "\n", + " this.rubberband_context = rubberband_canvas.getContext('2d');\n", + " this.rubberband_context.strokeStyle = '#000000';\n", + "\n", + " this._resize_canvas = function (width, height, forward) {\n", + " if (forward) {\n", + " canvas_div.style.width = width + 'px';\n", + " canvas_div.style.height = height + 'px';\n", + " }\n", + " };\n", + "\n", + " // Disable right mouse context menu.\n", + " this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n", + " event.preventDefault();\n", + " return false;\n", + " });\n", + "\n", + " function set_focus() {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'mpl-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " continue;\n", + " }\n", + "\n", + " var button = (fig.buttons[name] = document.createElement('button'));\n", + " button.classList = 'mpl-widget';\n", + " button.setAttribute('role', 'button');\n", + " button.setAttribute('aria-disabled', 'false');\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + "\n", + " var icon_img = document.createElement('img');\n", + " icon_img.src = '_images/' + image + '.png';\n", + " icon_img.srcset = '_images/' + image + '_large.png 2x';\n", + " icon_img.alt = tooltip;\n", + " button.appendChild(icon_img);\n", + "\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " var fmt_picker = document.createElement('select');\n", + " fmt_picker.classList = 'mpl-widget';\n", + " toolbar.appendChild(fmt_picker);\n", + " this.format_dropdown = fmt_picker;\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = document.createElement('option');\n", + " option.selected = fmt === mpl.default_extension;\n", + " option.innerHTML = fmt;\n", + " fmt_picker.appendChild(option);\n", + " }\n", + "\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "};\n", + "\n", + "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', { width: x_pixels, height: y_pixels });\n", + "};\n", + "\n", + "mpl.figure.prototype.send_message = function (type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "};\n", + "\n", + "mpl.figure.prototype.send_draw_message = function () {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " var format_dropdown = fig.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " fig.ondownload(fig, format);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_resize = function (fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1], msg['forward']);\n", + " fig.send_message('refresh', {});\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n", + " var x0 = msg['x0'] / fig.ratio;\n", + " var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n", + " var x1 = msg['x1'] / fig.ratio;\n", + " var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0,\n", + " 0,\n", + " fig.canvas.width / fig.ratio,\n", + " fig.canvas.height / fig.ratio\n", + " );\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", + " fig.rubberband_canvas.style.cursor = msg['cursor'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_message = function (fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n", + " for (var key in msg) {\n", + " if (!(key in fig.buttons)) {\n", + " continue;\n", + " }\n", + " fig.buttons[key].disabled = !msg[key];\n", + " fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n", + " if (msg['mode'] === 'PAN') {\n", + " fig.buttons['Pan'].classList.add('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " } else if (msg['mode'] === 'ZOOM') {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.add('active');\n", + " } else {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message('ack', {});\n", + "};\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function (fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " var img = evt.data;\n", + " if (img.type !== 'image/png') {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " img.type = 'image/png';\n", + " }\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src\n", + " );\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " img\n", + " );\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " } else if (\n", + " typeof evt.data === 'string' &&\n", + " evt.data.slice(0, 21) === 'data:image/png;base64'\n", + " ) {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig['handle_' + msg_type];\n", + " } catch (e) {\n", + " console.log(\n", + " \"No handler for the '\" + msg_type + \"' message type: \",\n", + " msg\n", + " );\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\n", + " \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n", + " e,\n", + " e.stack,\n", + " msg\n", + " );\n", + " }\n", + " }\n", + " };\n", + "};\n", + "\n", + "// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function (e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e) {\n", + " e = window.event;\n", + " }\n", + " if (e.target) {\n", + " targ = e.target;\n", + " } else if (e.srcElement) {\n", + " targ = e.srcElement;\n", + " }\n", + " if (targ.nodeType === 3) {\n", + " // defeat Safari bug\n", + " targ = targ.parentNode;\n", + " }\n", + "\n", + " // pageX,Y are the mouse positions relative to the document\n", + " var boundingRect = targ.getBoundingClientRect();\n", + " var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n", + " var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n", + "\n", + " return { x: x, y: y };\n", + "};\n", + "\n", + "/*\n", + " * return a copy of an object with only non-object keys\n", + " * we need this to avoid circular references\n", + " * https://stackoverflow.com/a/24161582/3208463\n", + " */\n", + "function simpleKeys(original) {\n", + " return Object.keys(original).reduce(function (obj, key) {\n", + " if (typeof original[key] !== 'object') {\n", + " obj[key] = original[key];\n", + " }\n", + " return obj;\n", + " }, {});\n", + "}\n", + "\n", + "mpl.figure.prototype.mouse_event = function (event, name) {\n", + " var canvas_pos = mpl.findpos(event);\n", + "\n", + " if (name === 'button_press') {\n", + " this.canvas.focus();\n", + " this.canvas_div.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x * this.ratio;\n", + " var y = canvas_pos.y * this.ratio;\n", + "\n", + " this.send_message(name, {\n", + " x: x,\n", + " y: y,\n", + " button: event.button,\n", + " step: event.step,\n", + " guiEvent: simpleKeys(event),\n", + " });\n", + "\n", + " /* This prevents the web browser from automatically changing to\n", + " * the text insertion cursor when the button is pressed. We want\n", + " * to control all of the cursor setting manually through the\n", + " * 'cursor' event from matplotlib */\n", + " event.preventDefault();\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n", + " // Handle any extra behaviour associated with a key event\n", + "};\n", + "\n", + "mpl.figure.prototype.key_event = function (event, name) {\n", + " // Prevent repeat events\n", + " if (name === 'key_press') {\n", + " if (event.key === this._key) {\n", + " return;\n", + " } else {\n", + " this._key = event.key;\n", + " }\n", + " }\n", + " if (name === 'key_release') {\n", + " this._key = null;\n", + " }\n", + "\n", + " var value = '';\n", + " if (event.ctrlKey && event.key !== 'Control') {\n", + " value += 'ctrl+';\n", + " }\n", + " else if (event.altKey && event.key !== 'Alt') {\n", + " value += 'alt+';\n", + " }\n", + " else if (event.shiftKey && event.key !== 'Shift') {\n", + " value += 'shift+';\n", + " }\n", + "\n", + " value += 'k' + event.key;\n", + "\n", + " this._key_event_extra(event, name);\n", + "\n", + " this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n", + " if (name === 'download') {\n", + " this.handle_save(this, null);\n", + " } else {\n", + " this.send_message('toolbar_button', { name: name });\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n", + " this.message.textContent = tooltip;\n", + "};\n", + "\n", + "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", + "// prettier-ignore\n", + "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";/* global mpl */\n", + "\n", + "var comm_websocket_adapter = function (comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.binaryType = comm.kernel.ws.binaryType;\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " function updateReadyState(_event) {\n", + " if (comm.kernel.ws) {\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " } else {\n", + " ws.readyState = 3; // Closed state.\n", + " }\n", + " }\n", + " comm.kernel.ws.addEventListener('open', updateReadyState);\n", + " comm.kernel.ws.addEventListener('close', updateReadyState);\n", + " comm.kernel.ws.addEventListener('error', updateReadyState);\n", + "\n", + " ws.close = function () {\n", + " comm.close();\n", + " };\n", + " ws.send = function (m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function (msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " var data = msg['content']['data'];\n", + " if (data['blob'] !== undefined) {\n", + " data = {\n", + " data: new Blob(msg['buffers'], { type: data['blob'] }),\n", + " };\n", + " }\n", + " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", + " ws.onmessage(data);\n", + " });\n", + " return ws;\n", + "};\n", + "\n", + "mpl.mpl_figure_comm = function (comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = document.getElementById(id);\n", + " var ws_proxy = comm_websocket_adapter(comm);\n", + "\n", + " function ondownload(figure, _format) {\n", + " window.open(figure.canvas.toDataURL());\n", + " }\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element;\n", + " fig.cell_info = mpl.find_output_cell(\"
\");\n", + " if (!fig.cell_info) {\n", + " console.error('Failed to find cell for figure', id, fig);\n", + " return;\n", + " }\n", + " fig.cell_info[0].output_area.element.on(\n", + " 'cleared',\n", + " { fig: fig },\n", + " fig._remove_fig_handler\n", + " );\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function (fig, msg) {\n", + " var width = fig.canvas.width / fig.ratio;\n", + " fig.cell_info[0].output_area.element.off(\n", + " 'cleared',\n", + " fig._remove_fig_handler\n", + " );\n", + " fig.resizeObserverInstance.unobserve(fig.canvas_div);\n", + "\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable();\n", + " fig.parent_element.innerHTML =\n", + " '';\n", + " fig.close_ws(fig, msg);\n", + "};\n", + "\n", + "mpl.figure.prototype.close_ws = function (fig, msg) {\n", + " fig.send_message('closing', msg);\n", + " // fig.ws.close()\n", + "};\n", + "\n", + "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n", + " // Turn the data on the canvas into data in the output cell.\n", + " var width = this.canvas.width / this.ratio;\n", + " var dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] =\n", + " '';\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message('ack', {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () {\n", + " fig.push_to_output();\n", + " }, 1000);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'btn-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " var button;\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " continue;\n", + " }\n", + "\n", + " button = fig.buttons[name] = document.createElement('button');\n", + " button.classList = 'btn btn-default';\n", + " button.href = '#';\n", + " button.title = name;\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message pull-right';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = document.createElement('div');\n", + " buttongrp.classList = 'btn-group inline pull-right';\n", + " button = document.createElement('button');\n", + " button.classList = 'btn btn-mini btn-primary';\n", + " button.href = '#';\n", + " button.title = 'Stop Interaction';\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', function (_evt) {\n", + " fig.handle_close(fig, {});\n", + " });\n", + " button.addEventListener(\n", + " 'mouseover',\n", + " on_mouseover_closure('Stop Interaction')\n", + " );\n", + " buttongrp.appendChild(button);\n", + " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", + " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", + "};\n", + "\n", + "mpl.figure.prototype._remove_fig_handler = function (event) {\n", + " var fig = event.data.fig;\n", + " if (event.target !== this) {\n", + " // Ignore bubbled events from children.\n", + " return;\n", + " }\n", + " fig.close_ws(fig, {});\n", + "};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (el) {\n", + " el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (el) {\n", + " // this is important to make the div 'focusable\n", + " el.setAttribute('tabindex', 0);\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " } else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which === 13) {\n", + " this.canvas_div.blur();\n", + " // select the cell after this one\n", + " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", + " IPython.notebook.select(index + 1);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " fig.ondownload(fig, null);\n", + "};\n", + "\n", + "mpl.find_output_cell = function (html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i = 0; i < ncells; i++) {\n", + " var cell = cells[i];\n", + " if (cell.cell_type === 'code') {\n", + " for (var j = 0; j < cell.output_area.outputs.length; j++) {\n", + " var data = cell.output_area.outputs[j];\n", + " if (data.data) {\n", + " // IPython >= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] === html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "};\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel !== null) {\n", + " IPython.notebook.kernel.comm_manager.register_target(\n", + " 'matplotlib',\n", + " mpl.mpl_figure_comm\n", + " );\n", + "}\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%matplotlib notebook\n", + "\n", + "# Read in results\n", + "## Params\n", + "folder = Path(\"./results/daytime_eval\")\n", + "\n", + "## Plot code\n", + "res = {}\n", + "\n", + "\n", + "def _load():\n", + " # res: dict[datetime, results]\n", + " # results: dict[tuple[system, num], bw_in_MiB]\n", + " for file in folder.iterdir():\n", + " date_str, _, _, system, num = file.stem.split(\"_\")\n", + " date = datetime.fromisoformat(date_str) + timedelta(hours=2)\n", + " file_content = json.loads(file.read_text())\n", + "\n", + " run_res = file_content[\"results\"][f\"daytime_evaluation_{system}_{num}\"]\n", + " if len(run_res) == 0:\n", + " print(\"No result for:\", date_str, system, num)\n", + " continue\n", + " if \"put_bw_in_MiB\" in run_res[0]:\n", + " value = float(run_res[0][\"put_bw_in_MiB\"])\n", + " else:\n", + " value = run_res[0][\"write_bw_mean_in_MiB\"] \n", + "\n", + " if date not in res:\n", + " res[date] = {}\n", + "\n", + " key = (system, int(num))\n", + " if key in res:\n", + " print(\"Duplication for:\", date, key)\n", + " res[date][key] = value\n", + "\n", + " labels = sorted(k for k in res.keys())\n", + " return labels, res\n", + "\n", + "def generate_tex_table(labels, res):\n", + " # Generate values for tex-table\n", + " values_netapp_1 = []\n", + " values_netapp_2 = []\n", + " values_isilon_1 = []\n", + " values_isilon_2 = []\n", + " values_s3_1 = []\n", + " values_s3_2 = []\n", + "\n", + " for key in labels:\n", + " value = res[key]\n", + " values_netapp_1.append(value.get((\"netapp\", 1), 0))\n", + " values_netapp_2.append(value.get((\"netapp\", 2), 0))\n", + " values_isilon_1.append(value.get((\"isilon\", 1), 0))\n", + " values_isilon_2.append(value.get((\"isilon\", 2), 0))\n", + " values_s3_1.append(value.get((\"s3\", 1), 0))\n", + " values_s3_2.append(value.get((\"s3\", 2), 0))\n", + "\n", + " def extract_stats(values, label=\"\"):\n", + " min_, max_ = min(values), max(values)\n", + " avg, conf = sum(values)/len(values), confidence_interval(values)\n", + " std = np.std(values)\n", + " print(f\"{label} & {min_:.0f} & {max_:.0f} & {avg:.0f} & {conf:.0f} & {std:.0f}\\\\\\\\\")\n", + "\n", + " extract_stats(values_netapp_1, \"Netapp A400\")\n", + " extract_stats(values_netapp_2)\n", + " extract_stats(values_isilon_1, \"Isilon\")\n", + " extract_stats(values_isilon_2)\n", + " extract_stats(values_s3_1, \"Netapp StorageGRID\")\n", + " extract_stats(values_s3_2)\n", + "\n", + "def generate_plot(labels, res):\n", + " # Generate plot\n", + " values_netapp_1 = []\n", + " values_netapp_2 = []\n", + " values_isilon_1 = []\n", + " values_isilon_2 = []\n", + " values_s3_1 = []\n", + " values_s3_2 = []\n", + " ## Sliding windows\n", + " sw_netapp_1 = []\n", + " sw_netapp_2 = []\n", + " sw_isilon_1 = []\n", + " sw_isilon_2 = []\n", + " sw_s3_1 = []\n", + " sw_s3_2 = []\n", + "\n", + " def calc_sliding_window(value, sw):\n", + " width = 4\n", + " sw.append(value)\n", + " if len(sw) > width:\n", + " sw.pop(0)\n", + " return sum(sw)/len(sw)\n", + "\n", + " for key in labels:\n", + " value = res[key]\n", + " values_netapp_1.append(calc_sliding_window(value.get((\"netapp\", 1), 0),sw_netapp_1))\n", + " values_netapp_2.append(calc_sliding_window(value.get((\"netapp\", 2), 0),sw_netapp_2))\n", + " values_isilon_1.append(calc_sliding_window(value.get((\"isilon\", 1), 0),sw_isilon_1))\n", + " values_isilon_2.append(calc_sliding_window(value.get((\"isilon\", 2), 0),sw_isilon_2))\n", + " values_s3_1.append(calc_sliding_window(value.get((\"s3\", 1), 0),sw_s3_1))\n", + " values_s3_2.append(calc_sliding_window(value.get((\"s3\", 2), 0),sw_s3_2))\n", + "\n", + " #plt.plot(labels, values_isilon_1, label=\"Isilon 01\")\n", + " #plt.plot(labels, values_netapp_1, label=\"Netapp 01\")\n", + " #plt.plot(labels, values_s3_1, label=\"S3 01\")\n", + " plt.plot(labels, values_isilon_2, label=\"Isilon\")\n", + " plt.plot(labels, values_netapp_2, label=\"Netapp A400\")\n", + " plt.plot(labels, values_s3_2, label=\"Netapp StorageGRID\")\n", + "\n", + " # Plot day seperation lines\n", + " for month, day in [(7,19),(7,20),(7,21),(7,22),(7,23),(7,24),(7,25)]:\n", + " x = datetime(year=2022, month=month, day=day)\n", + " plt.vlines(x=x, ymin=200, ymax=1150, linewidth=2, linestyles=\"dashed\", colors=\"k\", linewidths=0.75)\n", + " #x_8, x_17 = x.replace(hour=8), x.replace(hour=17)\n", + " #plt.axvspan(x_8, x_17, color='red', alpha=0.25)\n", + "\n", + " plt.xticks(rotation=25, ha='right')\n", + " plt.ylabel('Bandwidth (MiB/s)')\n", + "\n", + " plt.legend()\n", + " plt.title(\"Bandwidth Over Time\")\n", + " plt.savefig(\"daytime_evaluation.png\",bbox_inches='tight')\n", + " plt.show()\n", + "\n", + "labels, res = _load()\n", + "generate_tex_table(labels, res)\n", + "generate_plot(labels, res)\n" + ] + }, + { + "cell_type": "markdown", + "id": "5fb262ad", + "metadata": {}, + "source": [ + "## Experiment: Compare Storage Systems (bandwidth, iops, latency)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "47d047e4", + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/fromnumeric.py:3440: RuntimeWarning: Mean of empty slice.\n", + " return _methods._mean(a, axis=axis, dtype=dtype,\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:189: RuntimeWarning: invalid value encountered in double_scalars\n", + " ret = ret.dtype.type(ret / rcount)\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:262: RuntimeWarning: Degrees of freedom <= 0 for slice\n", + " ret = _var(a, axis=axis, dtype=dtype, out=out, ddof=ddof,\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:222: RuntimeWarning: invalid value encountered in true_divide\n", + " arrmean = um.true_divide(arrmean, div, out=arrmean, casting='unsafe',\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:254: RuntimeWarning: invalid value encountered in double_scalars\n", + " ret = ret.dtype.type(ret / rcount)\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/fromnumeric.py:3440: RuntimeWarning: Mean of empty slice.\n", + " return _methods._mean(a, axis=axis, dtype=dtype,\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:189: RuntimeWarning: invalid value encountered in double_scalars\n", + " ret = ret.dtype.type(ret / rcount)\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:262: RuntimeWarning: Degrees of freedom <= 0 for slice\n", + " ret = _var(a, axis=axis, dtype=dtype, out=out, ddof=ddof,\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:222: RuntimeWarning: invalid value encountered in true_divide\n", + " arrmean = um.true_divide(arrmean, div, out=arrmean, casting='unsafe',\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:254: RuntimeWarning: invalid value encountered in double_scalars\n", + " ret = ret.dtype.type(ret / rcount)\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/fromnumeric.py:3440: RuntimeWarning: Mean of empty slice.\n", + " return _methods._mean(a, axis=axis, dtype=dtype,\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:189: RuntimeWarning: invalid value encountered in double_scalars\n", + " ret = ret.dtype.type(ret / rcount)\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:262: RuntimeWarning: Degrees of freedom <= 0 for slice\n", + " ret = _var(a, axis=axis, dtype=dtype, out=out, ddof=ddof,\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:222: RuntimeWarning: invalid value encountered in true_divide\n", + " arrmean = um.true_divide(arrmean, div, out=arrmean, casting='unsafe',\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:254: RuntimeWarning: invalid value encountered in double_scalars\n", + " ret = ret.dtype.type(ret / rcount)\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/fromnumeric.py:3440: RuntimeWarning: Mean of empty slice.\n", + " return _methods._mean(a, axis=axis, dtype=dtype,\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:189: RuntimeWarning: invalid value encountered in double_scalars\n", + " ret = ret.dtype.type(ret / rcount)\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:262: RuntimeWarning: Degrees of freedom <= 0 for slice\n", + " ret = _var(a, axis=axis, dtype=dtype, out=out, ddof=ddof,\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:222: RuntimeWarning: invalid value encountered in true_divide\n", + " arrmean = um.true_divide(arrmean, div, out=arrmean, casting='unsafe',\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:254: RuntimeWarning: invalid value encountered in double_scalars\n", + " ret = ret.dtype.type(ret / rcount)\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/fromnumeric.py:3440: RuntimeWarning: Mean of empty slice.\n", + " return _methods._mean(a, axis=axis, dtype=dtype,\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:189: RuntimeWarning: invalid value encountered in double_scalars\n", + " ret = ret.dtype.type(ret / rcount)\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:262: RuntimeWarning: Degrees of freedom <= 0 for slice\n", + " ret = _var(a, axis=axis, dtype=dtype, out=out, ddof=ddof,\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:222: RuntimeWarning: invalid value encountered in true_divide\n", + " arrmean = um.true_divide(arrmean, div, out=arrmean, casting='unsafe',\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:254: RuntimeWarning: invalid value encountered in double_scalars\n", + " ret = ret.dtype.type(ret / rcount)\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/fromnumeric.py:3440: RuntimeWarning: Mean of empty slice.\n", + " return _methods._mean(a, axis=axis, dtype=dtype,\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:189: RuntimeWarning: invalid value encountered in double_scalars\n", + " ret = ret.dtype.type(ret / rcount)\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:262: RuntimeWarning: Degrees of freedom <= 0 for slice\n", + " ret = _var(a, axis=axis, dtype=dtype, out=out, ddof=ddof,\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:222: RuntimeWarning: invalid value encountered in true_divide\n", + " arrmean = um.true_divide(arrmean, div, out=arrmean, casting='unsafe',\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:254: RuntimeWarning: invalid value encountered in double_scalars\n", + " ret = ret.dtype.type(ret / rcount)\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/fromnumeric.py:3440: RuntimeWarning: Mean of empty slice.\n", + " return _methods._mean(a, axis=axis, dtype=dtype,\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:189: RuntimeWarning: invalid value encountered in double_scalars\n", + " ret = ret.dtype.type(ret / rcount)\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:262: RuntimeWarning: Degrees of freedom <= 0 for slice\n", + " ret = _var(a, axis=axis, dtype=dtype, out=out, ddof=ddof,\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:222: RuntimeWarning: invalid value encountered in true_divide\n", + " arrmean = um.true_divide(arrmean, div, out=arrmean, casting='unsafe',\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:254: RuntimeWarning: invalid value encountered in double_scalars\n", + " ret = ret.dtype.type(ret / rcount)\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/fromnumeric.py:3440: RuntimeWarning: Mean of empty slice.\n", + " return _methods._mean(a, axis=axis, dtype=dtype,\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:189: RuntimeWarning: invalid value encountered in double_scalars\n", + " ret = ret.dtype.type(ret / rcount)\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:262: RuntimeWarning: Degrees of freedom <= 0 for slice\n", + " ret = _var(a, axis=axis, dtype=dtype, out=out, ddof=ddof,\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:222: RuntimeWarning: invalid value encountered in true_divide\n", + " arrmean = um.true_divide(arrmean, div, out=arrmean, casting='unsafe',\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:254: RuntimeWarning: invalid value encountered in double_scalars\n", + " ret = ret.dtype.type(ret / rcount)\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/fromnumeric.py:3440: RuntimeWarning: Mean of empty slice.\n", + " return _methods._mean(a, axis=axis, dtype=dtype,\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:189: RuntimeWarning: invalid value encountered in double_scalars\n", + " ret = ret.dtype.type(ret / rcount)\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:262: RuntimeWarning: Degrees of freedom <= 0 for slice\n", + " ret = _var(a, axis=axis, dtype=dtype, out=out, ddof=ddof,\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:222: RuntimeWarning: invalid value encountered in true_divide\n", + " arrmean = um.true_divide(arrmean, div, out=arrmean, casting='unsafe',\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:254: RuntimeWarning: invalid value encountered in double_scalars\n", + " ret = ret.dtype.type(ret / rcount)\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/fromnumeric.py:3440: RuntimeWarning: Mean of empty slice.\n", + " return _methods._mean(a, axis=axis, dtype=dtype,\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:189: RuntimeWarning: invalid value encountered in double_scalars\n", + " ret = ret.dtype.type(ret / rcount)\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:262: RuntimeWarning: Degrees of freedom <= 0 for slice\n", + " ret = _var(a, axis=axis, dtype=dtype, out=out, ddof=ddof,\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:222: RuntimeWarning: invalid value encountered in true_divide\n", + " arrmean = um.true_divide(arrmean, div, out=arrmean, casting='unsafe',\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:254: RuntimeWarning: invalid value encountered in double_scalars\n", + " ret = ret.dtype.type(ret / rcount)\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/fromnumeric.py:3440: RuntimeWarning: Mean of empty slice.\n", + " return _methods._mean(a, axis=axis, dtype=dtype,\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:189: RuntimeWarning: invalid value encountered in double_scalars\n", + " ret = ret.dtype.type(ret / rcount)\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:262: RuntimeWarning: Degrees of freedom <= 0 for slice\n", + " ret = _var(a, axis=axis, dtype=dtype, out=out, ddof=ddof,\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:222: RuntimeWarning: invalid value encountered in true_divide\n", + " arrmean = um.true_divide(arrmean, div, out=arrmean, casting='unsafe',\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:254: RuntimeWarning: invalid value encountered in double_scalars\n", + " ret = ret.dtype.type(ret / rcount)\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/fromnumeric.py:3440: RuntimeWarning: Mean of empty slice.\n", + " return _methods._mean(a, axis=axis, dtype=dtype,\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:189: RuntimeWarning: invalid value encountered in double_scalars\n", + " ret = ret.dtype.type(ret / rcount)\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:262: RuntimeWarning: Degrees of freedom <= 0 for slice\n", + " ret = _var(a, axis=axis, dtype=dtype, out=out, ddof=ddof,\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:222: RuntimeWarning: invalid value encountered in true_divide\n", + " arrmean = um.true_divide(arrmean, div, out=arrmean, casting='unsafe',\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:254: RuntimeWarning: invalid value encountered in double_scalars\n", + " ret = ret.dtype.type(ret / rcount)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " mean std num\n", + "local throughput_write 128.060578 6.146713 16\n", + "nfs_isilon throughput_write 445.967968 54.696129 16\n", + "nfs_netapp throughput_write 370.237354 51.623348 16\n", + "nfs_ssdtank throughput_write 332.996586 2.512257 16\n", + "irodsfs_isilon throughput_write 145.969143 3.155809 16\n", + "irodsfs_netapp throughput_write 145.492831 4.040569 16\n", + "irodsfs_s3 throughput_write NaN NaN 0\n", + "webdav_isilon throughput_write 481.487045 24.733831 16\n", + "webdav_netapp throughput_write 495.023286 20.975037 15\n", + "webdav_s3 throughput_write NaN NaN 0\n", + " mean std num\n", + "local throughput_read 103.240813 5.642437 16\n", + "nfs_isilon throughput_read 692.785714 214.542098 16\n", + "nfs_netapp throughput_read 223.579847 27.156816 16\n", + "nfs_ssdtank throughput_read 992.937335 115.347483 16\n", + "irodsfs_isilon throughput_read 414.176076 31.938263 13\n", + "irodsfs_netapp throughput_read 306.628007 26.766749 12\n", + "irodsfs_s3 throughput_read NaN NaN 0\n", + "webdav_isilon throughput_read 1541.166369 199.253438 16\n", + "webdav_netapp throughput_read 1462.531331 383.598147 15\n", + "webdav_s3 throughput_read NaN NaN 0\n", + " mean std num\n", + "local iops_write 794.721170 3.508450 16\n", + "nfs_isilon iops_write 9639.938751 1274.151275 16\n", + "nfs_netapp iops_write 25164.049895 3030.353567 16\n", + "nfs_ssdtank iops_write 682.105620 8.961278 16\n", + "irodsfs_isilon iops_write 919.868347 24.244180 3\n", + "irodsfs_netapp iops_write 894.912605 21.943125 5\n", + "irodsfs_s3 iops_write NaN NaN 0\n", + "webdav_isilon iops_write 16290.605668 334.899233 16\n", + "webdav_netapp iops_write 16221.098345 250.879202 14\n", + "webdav_s3 iops_write NaN NaN 0\n", + " mean std num\n", + "local iops_read 801.355729 0.070015 16\n", + "nfs_isilon iops_read 40278.403887 1809.157118 16\n", + "nfs_netapp iops_read 31910.917682 3718.674746 16\n", + "nfs_ssdtank iops_read 43909.241597 2530.065501 16\n", + "irodsfs_isilon iops_read 1065.074469 51.539141 13\n", + "irodsfs_netapp iops_read 833.467536 75.039880 10\n", + "irodsfs_s3 iops_read NaN NaN 0\n", + "webdav_isilon iops_read 129855.383631 16570.959797 16\n", + "webdav_netapp iops_read 111579.243791 44543.207920 15\n", + "webdav_s3 iops_read NaN NaN 0\n", + " mean std num\n", + "local latency_write 1.332769 0.065432 16\n", + "nfs_isilon latency_write 0.610347 0.033426 16\n", + "nfs_netapp latency_write 0.843478 0.152214 16\n", + "nfs_ssdtank latency_write 1.867753 0.030613 16\n", + "irodsfs_isilon latency_write NaN NaN 0\n", + "irodsfs_netapp latency_write NaN NaN 0\n", + "irodsfs_s3 latency_write NaN NaN 0\n", + "webdav_isilon latency_write 0.074547 0.000709 16\n", + "webdav_netapp latency_write 0.074447 0.000529 15\n", + "webdav_s3 latency_write NaN NaN 0\n", + " mean std num\n", + "local latency_read 1.443774 0.169324 16\n", + "nfs_isilon latency_read 0.364842 0.021308 16\n", + "nfs_netapp latency_read 1.132750 0.239697 16\n", + "nfs_ssdtank latency_read 0.336024 0.027679 16\n", + "irodsfs_isilon latency_read 2.549647 0.101681 15\n", + "irodsfs_netapp latency_read 3.724552 0.484918 16\n", + "irodsfs_s3 latency_read NaN NaN 0\n", + "webdav_isilon latency_read 0.022976 0.002284 16\n", + "webdav_netapp latency_read 0.023815 0.002340 15\n", + "webdav_s3 latency_read NaN NaN 0\n", + "##########\n", + "read\n", + "local&103 & $\\ \\pm\\ $ & 6&801 & $\\ \\pm\\ $ & 0&1.44 & $\\ \\pm\\ $ & 0.17\\\\\n", + "nfs\\_isilon&693 & $\\ \\pm\\ $ & 215&40278 & $\\ \\pm\\ $ & 1809&0.36 & $\\ \\pm\\ $ & 0.02\\\\\n", + "nfs\\_netapp&224 & $\\ \\pm\\ $ & 27&31911 & $\\ \\pm\\ $ & 3719&1.13 & $\\ \\pm\\ $ & 0.24\\\\\n", + "nfs\\_ssdtank&993 & $\\ \\pm\\ $ & 115&43909 & $\\ \\pm\\ $ & 2530&0.34 & $\\ \\pm\\ $ & 0.03\\\\\n", + "irodsfs\\_isilon&414 & $\\ \\pm\\ $ & 32&1065 & $\\ \\pm\\ $ & 52&2.55 & $\\ \\pm\\ $ & 0.10\\\\\n", + "irodsfs\\_netapp&307 & $\\ \\pm\\ $ & 27&833 & $\\ \\pm\\ $ & 75&3.72 & $\\ \\pm\\ $ & 0.48\\\\\n", + "irodsfs\\_s3& &-& & &-& & &-& \\\\\n", + "webdav\\_isilon&1541 & $\\ \\pm\\ $ & 199&129855 & $\\ \\pm\\ $ & 16571&0.02 & $\\ \\pm\\ $ & 0.00\\\\\n", + "webdav\\_netapp&1463 & $\\ \\pm\\ $ & 384&111579 & $\\ \\pm\\ $ & 44543&0.02 & $\\ \\pm\\ $ & 0.00\\\\\n", + "webdav\\_s3& &-& & &-& & &-& \\\\\n", + "##########\n", + "write\n", + "local&128 & $\\ \\pm\\ $ & 6&795 & $\\ \\pm\\ $ & 4&1.33 & $\\ \\pm\\ $ & 0.07\\\\\n", + "nfs\\_isilon&446 & $\\ \\pm\\ $ & 55&9640 & $\\ \\pm\\ $ & 1274&0.61 & $\\ \\pm\\ $ & 0.03\\\\\n", + "nfs\\_netapp&370 & $\\ \\pm\\ $ & 52&25164 & $\\ \\pm\\ $ & 3030&0.84 & $\\ \\pm\\ $ & 0.15\\\\\n", + "nfs\\_ssdtank&333 & $\\ \\pm\\ $ & 3&682 & $\\ \\pm\\ $ & 9&1.87 & $\\ \\pm\\ $ & 0.03\\\\\n", + "irodsfs\\_isilon&146 & $\\ \\pm\\ $ & 3&920 & $\\ \\pm\\ $ & 24& &-& \\\\\n", + "irodsfs\\_netapp&145 & $\\ \\pm\\ $ & 4&895 & $\\ \\pm\\ $ & 22& &-& \\\\\n", + "irodsfs\\_s3& &-& & &-& & &-& \\\\\n", + "webdav\\_isilon&481 & $\\ \\pm\\ $ & 25&16291 & $\\ \\pm\\ $ & 335&0.07 & $\\ \\pm\\ $ & 0.00\\\\\n", + "webdav\\_netapp&495 & $\\ \\pm\\ $ & 21&16221 & $\\ \\pm\\ $ & 251&0.07 & $\\ \\pm\\ $ & 0.00\\\\\n", + "webdav\\_s3& &-& & &-& & &-& \\\\\n", + "##########\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/fromnumeric.py:3440: RuntimeWarning: Mean of empty slice.\n", + " return _methods._mean(a, axis=axis, dtype=dtype,\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:189: RuntimeWarning: invalid value encountered in double_scalars\n", + " ret = ret.dtype.type(ret / rcount)\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:262: RuntimeWarning: Degrees of freedom <= 0 for slice\n", + " ret = _var(a, axis=axis, dtype=dtype, out=out, ddof=ddof,\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:222: RuntimeWarning: invalid value encountered in true_divide\n", + " arrmean = um.true_divide(arrmean, div, out=arrmean, casting='unsafe',\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:254: RuntimeWarning: invalid value encountered in double_scalars\n", + " ret = ret.dtype.type(ret / rcount)\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/fromnumeric.py:3440: RuntimeWarning: Mean of empty slice.\n", + " return _methods._mean(a, axis=axis, dtype=dtype,\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:189: RuntimeWarning: invalid value encountered in double_scalars\n", + " ret = ret.dtype.type(ret / rcount)\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:262: RuntimeWarning: Degrees of freedom <= 0 for slice\n", + " ret = _var(a, axis=axis, dtype=dtype, out=out, ddof=ddof,\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:222: RuntimeWarning: invalid value encountered in true_divide\n", + " arrmean = um.true_divide(arrmean, div, out=arrmean, casting='unsafe',\n", + "/home/stefan/.local/lib/python3.8/site-packages/numpy/core/_methods.py:254: RuntimeWarning: invalid value encountered in double_scalars\n", + " ret = ret.dtype.type(ret / rcount)\n" + ] + } + ], + "source": [ + "# Read in results\n", + "## Params - Either specify a file prefix or a whole folder\n", + "file_prefix = None # Path(\"./results/2022-07-05T20:00:06_throughput_write_\")\n", + "folder = Path(\"./results/benchmark_posix\")\n", + "\n", + "## Plot code\n", + "path = file_prefix if file_prefix else folder\n", + "\n", + "benchmarks = [\n", + " (\"throughput_write\", \"write_bw_mean_in_MiB\"),\n", + " (\"throughput_read\", \"read_bw_mean_in_MiB\"),\n", + " (\"iops_write\", \"write_iops_mean\"),\n", + " (\"iops_read\", \"read_iops_mean\"),\n", + " (\"latency_write\", \"write_lat_mean_in_ms\"),\n", + " (\"latency_read\", \"read_lat_mean_in_ms\"),\n", + "]\n", + "dfs = []\n", + "\n", + "for label, metric in benchmarks:\n", + " dfs.append(pd.concat({\n", + " \"local\": load_data(path, metric=metric, suffix=\"posix_local.json\", x_labels=[label]),\n", + " \"nfs_isilon\": load_data(path, metric=metric, suffix=\"posix_isilon.json\", x_labels=[label]),\n", + " \"nfs_netapp\": load_data(path, metric=metric, suffix=\"posix_netapp.json\", x_labels=[label]),\n", + " \"nfs_ssdtank\": load_data(path, metric=metric, suffix=\"posix_ssd_tank.json\", x_labels=[label]),\n", + " \"irodsfs_isilon\": load_data(path, metric=metric, suffix=\"posix_irods_fuse_on_isilon.json\", x_labels=[label]),\n", + " \"irodsfs_netapp\": load_data(path, metric=metric, suffix=\"posix_irods_fuse_on_netapp.json\", x_labels=[label]),\n", + " \"irodsfs_s3\": load_data(path, metric=metric, suffix=\"posix_irods_fuse_on_s3.json\", x_labels=[label]),\n", + " \"webdav_isilon\": load_data(path, metric=metric, suffix=\"posix_irods_davrods_on_isilon.json\", x_labels=[label]),\n", + " \"webdav_netapp\": load_data(path, metric=metric, suffix=\"posix_irods_davrods_on_netapp.json\", x_labels=[label]),\n", + " \"webdav_s3\": load_data(path, metric=metric, suffix=\"posix_irods_davrods_on_s3.json\", x_labels=[label]),\n", + " }))\n", + " \n", + "for df in dfs:\n", + " print(df)\n", + "\n", + "def extract_stats(dfs, benchmark_type, label, index):\n", + " pm = \" & $\\ \\pm\\ $ & \" # Plus/minus seperator\n", + " \n", + " print(f\"{label}\", end=\"\")\n", + " for benchmark, df in zip(benchmarks, dfs):\n", + " if benchmark_type not in benchmark[0]:\n", + " continue\n", + " avg, std = df[\"mean\"][index], df[\"std\"][index]\n", + " if np.isnan(avg) or np.isnan(std):\n", + " print(\"& &-& \", end=\"\")\n", + " elif \"latency\" in benchmark[0]:\n", + " print(f\"&{avg:.2f}{pm}{std:.2f}\", end=\"\")\n", + " else:\n", + " print(f\"&{avg:.0f}{pm}{std:.0f}\", end=\"\")\n", + " print(\"\\\\\\\\\")\n", + "\n", + "print(\"#\"*10)\n", + "print(\"read\")\n", + "extract_stats(dfs, \"read\", \"local\", 0)\n", + "extract_stats(dfs, \"read\", \"nfs\\_isilon\", 1)\n", + "extract_stats(dfs, \"read\", \"nfs\\_netapp\", 2)\n", + "extract_stats(dfs, \"read\", \"nfs\\_ssdtank\", 3)\n", + "extract_stats(dfs, \"read\", \"irodsfs\\_isilon\", 4)\n", + "extract_stats(dfs, \"read\", \"irodsfs\\_netapp\", 5)\n", + "extract_stats(dfs, \"read\", \"irodsfs\\_s3\", 6)\n", + "extract_stats(dfs, \"read\", \"webdav\\_isilon\", 7)\n", + "extract_stats(dfs, \"read\", \"webdav\\_netapp\", 8)\n", + "extract_stats(dfs, \"read\", \"webdav\\_s3\", 9)\n", + "print(\"#\"*10)\n", + "print(\"write\")\n", + "extract_stats(dfs, \"write\", \"local\", 0)\n", + "extract_stats(dfs, \"write\", \"nfs\\_isilon\", 1)\n", + "extract_stats(dfs, \"write\", \"nfs\\_netapp\", 2)\n", + "extract_stats(dfs, \"write\", \"nfs\\_ssdtank\", 3)\n", + "extract_stats(dfs, \"write\", \"irodsfs\\_isilon\", 4)\n", + "extract_stats(dfs, \"write\", \"irodsfs\\_netapp\", 5)\n", + "extract_stats(dfs, \"write\", \"irodsfs\\_s3\", 6)\n", + "extract_stats(dfs, \"write\", \"webdav\\_isilon\", 7)\n", + "extract_stats(dfs, \"write\", \"webdav\\_netapp\", 8)\n", + "extract_stats(dfs, \"write\", \"webdav\\_s3\", 9)\n", + "print(\"#\"*10)\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2f425dd9", + "metadata": {}, + "source": [ + "# Experiment: Compare Storage Systems (metadata ops)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "d22b0f25", + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dir_create\n", + " mean std num\n", + "local 10 329.163769 152.234556 13\n", + " 100 2071.620385 884.264211 13\n", + " 1000 10232.780308 1272.849495 13\n", + " 10000 9032.135538 435.179116 13\n", + " 100000 9299.858923 175.215523 13\n", + "nfs\\_isilon 10 195.988000 59.939368 13\n", + " 100 793.855385 135.069714 13\n", + " 1000 1101.131231 193.412898 13\n", + " 10000 1135.692692 194.854261 13\n", + " 100000 1143.458308 180.377407 13\n", + "nfs\\_netapp 10 224.921154 67.818113 13\n", + " 100 555.779154 260.472172 13\n", + " 1000 624.946077 193.867056 13\n", + " 10000 576.150308 176.623132 13\n", + " 100000 511.110462 138.396179 13\n", + "nfs\\_ssdtank 10 164.436385 54.121114 13\n", + " 100 444.391923 53.602625 13\n", + " 1000 549.655692 17.742884 13\n", + " 10000 570.905769 12.877858 13\n", + " 100000 572.165385 7.762889 13\n", + "irodsfs\\_isilon 10 48.612846 6.384393 13\n", + " 100 66.647231 4.040323 13\n", + " 1000 67.148077 4.118740 13\n", + "irodsfs\\_netapp 10 54.868000 5.241568 13\n", + " 100 65.060385 5.534450 13\n", + " 1000 66.788231 4.543550 13\n", + "irodsfs\\_s3 10 52.199000 5.496321 13\n", + " 100 65.853769 5.133760 13\n", + " 1000 65.617769 4.313346 13\n", + "webdav\\_isilon 10 43.415077 2.727672 13\n", + " 100 52.333692 3.522601 13\n", + " 1000 51.939615 2.655561 13\n", + "webdav\\_netapp 10 43.050000 5.280436 13\n", + " 100 52.408154 3.453970 13\n", + " 1000 52.628923 2.875553 13\n", + "webdav\\_s3 10 46.547385 5.849078 13\n", + " 100 53.453077 2.703629 13\n", + " 1000 49.934077 1.998971 13\n", + "dir_stat\n", + " mean std num\n", + "local 10 4903.421769 441.547795 13\n", + " 100 45838.354923 5377.864877 13\n", + " 1000 233061.933769 55760.969888 13\n", + " 10000 375090.382385 82336.096811 13\n", + " 100000 493404.431923 31582.908479 13\n", + "nfs\\_isilon 10 5046.035308 426.992078 13\n", + " 100 47908.866154 9164.167719 13\n", + " 1000 217926.376000 18603.163358 13\n", + " 10000 347076.756538 26372.698212 13\n", + " 100000 5933.326923 723.409106 13\n", + "nfs\\_netapp 10 5032.723000 530.669961 13\n", + " 100 42446.628154 4613.618048 13\n", + " 1000 199228.459231 13750.305203 13\n", + " 10000 320719.258769 88893.118314 13\n", + " 100000 1711.970000 345.235216 13\n", + "nfs\\_ssdtank 10 4859.555385 1085.588514 13\n", + " 100 42218.569231 6822.250879 13\n", + " 1000 199143.034769 33649.752286 13\n", + " 10000 350900.231846 15439.923913 13\n", + " 100000 5953.793231 555.896984 13\n", + "irodsfs\\_isilon 10 5035.984538 921.358526 13\n", + " 100 43024.587462 6898.244423 13\n", + " 1000 237523.874769 13015.711273 13\n", + "irodsfs\\_netapp 10 5157.653615 838.771320 13\n", + " 100 45628.061769 7559.504808 13\n", + " 1000 232570.004308 21502.649131 13\n", + "irodsfs\\_s3 10 5308.842308 738.370070 13\n", + " 100 42802.546462 6221.474577 13\n", + " 1000 256695.605308 22044.428503 13\n", + "webdav\\_isilon 10 4179.347154 892.416027 13\n", + " 100 29056.400000 2752.434597 13\n", + " 1000 37438.846846 4834.634912 13\n", + "webdav\\_netapp 10 5037.517846 898.702958 13\n", + " 100 31875.486615 5067.051866 13\n", + " 1000 35221.576538 1093.067040 13\n", + "webdav\\_s3 10 4761.742462 988.414662 13\n", + " 100 27853.972462 2585.285027 13\n", + " 1000 37180.181077 6067.943252 13\n", + "dir_remove\n", + " mean std num\n", + "local 10 1821.557538 528.942466 13\n", + " 100 13189.609077 4378.672436 13\n", + " 1000 44171.995385 7241.695999 13\n", + " 10000 51278.081000 3253.314651 13\n", + " 100000 50921.158231 4063.479253 13\n", + "nfs\\_isilon 10 694.152231 148.851698 13\n", + " 100 798.781385 138.882741 13\n", + " 1000 787.212308 149.117879 13\n", + " 10000 659.417615 108.473817 13\n", + " 100000 668.133615 94.712954 13\n", + "nfs\\_netapp 10 616.598308 293.039897 13\n", + " 100 665.335231 423.913667 13\n", + " 1000 578.009846 208.307393 13\n", + " 10000 527.639000 124.240981 13\n", + " 100000 348.622231 139.202960 13\n", + "nfs\\_ssdtank 10 502.098000 30.780070 13\n", + " 100 560.564769 25.186562 13\n", + " 1000 571.138923 19.432215 13\n", + " 10000 557.507308 22.281691 13\n", + " 100000 566.146846 8.793936 13\n", + "irodsfs\\_isilon 10 79.571615 8.184698 13\n", + " 100 80.615923 6.300171 13\n", + " 1000 79.635154 5.704232 13\n", + "irodsfs\\_netapp 10 79.429538 8.798302 13\n", + " 100 78.024923 9.059523 13\n", + " 1000 76.922462 4.977721 13\n", + "irodsfs\\_s3 10 77.849769 14.291074 13\n", + " 100 79.215231 4.942006 13\n", + " 1000 76.930231 5.321220 13\n", + "webdav\\_isilon 10 25.302385 2.312536 13\n", + " 100 26.834615 1.803641 13\n", + " 1000 24.479154 1.163734 13\n", + "webdav\\_netapp 10 26.420846 1.292369 13\n", + " 100 27.683231 1.077549 13\n", + " 1000 24.944385 1.190534 13\n", + "webdav\\_s3 10 25.829538 2.388285 13\n", + " 100 27.255923 1.542436 13\n", + " 1000 24.586538 0.847337 13\n", + "file_create\n", + " mean std num\n", + "local 10 2269.084385 493.167142 13\n", + " 100 14860.881000 4475.754971 13\n", + " 1000 46389.432615 11445.400606 13\n", + " 10000 69316.555308 5957.516021 13\n", + " 100000 60108.552692 5030.659188 13\n", + "nfs\\_isilon 10 739.175923 168.284640 13\n", + " 100 914.898231 154.825232 13\n", + " 1000 883.298308 165.035079 13\n", + " 10000 888.657769 143.740313 13\n", + " 100000 885.593000 113.886786 13\n", + "nfs\\_netapp 10 712.926077 147.005125 13\n", + " 100 787.941000 174.857985 13\n", + " 1000 715.902231 133.802680 13\n", + " 10000 644.425462 70.703895 13\n", + " 100000 609.878154 111.072831 13\n", + "nfs\\_ssdtank 10 452.141615 22.782450 13\n", + " 100 489.759692 37.549920 13\n", + " 1000 519.751846 22.905900 13\n", + " 10000 531.789231 21.559985 13\n", + " 100000 533.790846 13.829183 13\n", + "irodsfs\\_isilon 10 13.763923 0.607451 13\n", + " 100 11.366462 0.350744 13\n", + " 1000 6.492231 0.117393 13\n", + "irodsfs\\_netapp 10 13.378077 1.003661 13\n", + " 100 11.015846 0.544440 13\n", + " 1000 6.435077 0.082649 13\n", + "irodsfs\\_s3 10 4.710154 0.653466 13\n", + " 100 7.469692 0.628851 13\n", + " 1000 5.761308 0.255480 13\n", + "webdav\\_isilon 10 7.918385 0.284586 13\n", + " 100 7.741154 0.232701 13\n", + " 1000 7.721000 0.215543 13\n", + "webdav\\_netapp 10 8.001923 0.530186 13\n", + " 100 7.823615 0.325362 13\n", + " 1000 7.688308 0.279878 13\n", + "webdav\\_s3 10 4.495846 0.176324 13\n", + " 100 4.470462 0.413204 13\n", + " 1000 4.514462 0.096477 13\n", + "file_stat\n", + " mean std num\n", + "local 10 6498.657846 580.412268 13\n", + " 100 61827.760385 7749.924436 13\n", + " 1000 317965.234923 30767.858842 13\n", + " 10000 441784.347923 90929.086964 13\n", + " 100000 497632.692615 33653.042218 13\n", + "nfs\\_isilon 10 6309.326462 946.283365 13\n", + " 100 49271.744231 9801.768938 13\n", + " 1000 231276.330846 28322.443228 13\n", + " 10000 5499.986538 563.309754 13\n", + " 100000 4917.837308 433.719772 13\n", + "nfs\\_netapp 10 6906.592154 1054.363347 13\n", + " 100 48707.785846 4619.834015 13\n", + " 1000 220725.584846 25204.265524 13\n", + " 10000 1830.346308 243.995131 13\n", + " 100000 1624.909000 485.601595 13\n", + "nfs\\_ssdtank 10 6567.300077 1299.470539 13\n", + " 100 45847.169000 8974.315155 13\n", + " 1000 219190.580231 23380.023372 13\n", + " 10000 6217.690077 793.742725 13\n", + " 100000 5451.145000 522.150744 13\n", + "irodsfs\\_isilon 10 4949.429308 1124.488087 13\n", + " 100 40234.515077 8195.438287 13\n", + " 1000 143.668231 25.846130 13\n", + "irodsfs\\_netapp 10 4442.225000 1123.853088 13\n", + " 100 41247.678231 4496.840883 13\n", + " 1000 153.234692 11.690668 13\n", + "irodsfs\\_s3 10 4024.333769 1004.505053 13\n", + " 100 38446.422692 5438.448783 13\n", + " 1000 171.549462 10.386184 13\n", + "webdav\\_isilon 10 214.338538 27.507937 13\n", + " 100 1956.569000 241.793420 13\n", + " 1000 13825.906077 1281.377382 13\n", + "webdav\\_netapp 10 209.670308 15.834858 13\n", + " 100 2055.081077 309.890926 13\n", + " 1000 12532.129846 1441.986798 13\n", + "webdav\\_s3 10 76.310769 12.816307 13\n", + " 100 767.701462 67.726645 13\n", + " 1000 6099.770769 716.522894 13\n", + "file_read\n", + " mean std num\n", + "local 10 6750.087846 981.965112 13\n", + " 100 54380.022615 7137.089127 13\n", + " 1000 236905.286462 22110.129076 13\n", + " 10000 339217.932615 17668.767155 13\n", + " 100000 331679.056692 17961.417736 13\n", + "nfs\\_isilon 10 1283.661692 93.185867 13\n", + " 100 1575.255615 177.752205 13\n", + " 1000 1654.904615 124.041412 13\n", + " 10000 1611.166308 88.349216 13\n", + " 100000 1593.013692 83.450422 13\n", + "nfs\\_netapp 10 823.933462 286.700545 13\n", + " 100 1007.820077 167.890270 13\n", + " 1000 879.279923 204.276480 13\n", + " 10000 889.289538 114.458868 13\n", + " 100000 817.301308 235.867273 13\n", + "nfs\\_ssdtank 10 1683.901231 213.019189 13\n", + " 100 2593.463462 328.565185 13\n", + " 1000 2769.372000 284.208925 13\n", + " 10000 2901.442615 288.648728 13\n", + " 100000 2729.638769 241.099419 13\n", + "irodsfs\\_isilon 10 33.972538 4.591990 13\n", + " 100 20.181308 1.390393 13\n", + " 1000 13.825538 0.940195 13\n", + "irodsfs\\_netapp 10 31.971154 4.411706 13\n", + " 100 19.341385 1.514505 13\n", + " 1000 13.111385 0.897127 13\n", + "irodsfs\\_s3 10 22.774846 1.847655 13\n", + " 100 28.524538 1.734236 13\n", + " 1000 18.877692 0.919563 13\n", + "webdav\\_isilon 10 7.551769 0.303996 13\n", + " 100 7.340923 0.205797 13\n", + " 1000 7.298000 0.215045 13\n", + "webdav\\_netapp 10 7.708231 0.351873 13\n", + " 100 7.233308 0.294328 13\n", + " 1000 7.261231 0.256176 13\n", + "webdav\\_s3 10 4.568000 0.161705 13\n", + " 100 4.237231 0.178763 13\n", + " 1000 4.272923 0.100173 13\n", + "file_remove\n", + " mean std num\n", + "local 10 2281.792538 325.961595 13\n", + " 100 15463.142462 5713.414167 13\n", + " 1000 63101.556846 9712.901621 13\n", + " 10000 85105.330846 13878.507486 13\n", + " 100000 73783.329462 8160.672156 13\n", + "nfs\\_isilon 10 1120.344308 213.322372 13\n", + " 100 1349.434308 226.913384 13\n", + " 1000 1314.556385 270.320106 13\n", + " 10000 1063.162615 199.936011 13\n", + " 100000 975.398000 76.462666 13\n", + "nfs\\_netapp 10 1154.976769 419.681848 13\n", + " 100 1203.067615 324.666288 13\n", + " 1000 1313.495692 138.465489 13\n", + " 10000 1285.437538 125.227800 13\n", + " 100000 1095.549231 263.683799 13\n", + "nfs\\_ssdtank 10 522.643538 17.370707 13\n", + " 100 579.388231 20.756202 13\n", + " 1000 578.652077 17.434715 13\n", + " 10000 578.331154 17.605361 13\n", + " 100000 585.016462 8.678842 13\n", + "irodsfs\\_isilon 10 37.532385 4.914748 13\n", + " 100 35.967385 6.331701 13\n", + " 1000 34.034385 4.324617 13\n", + "irodsfs\\_netapp 10 37.968308 5.544754 13\n", + " 100 39.250846 4.042518 13\n", + " 1000 32.508846 3.989500 13\n", + "irodsfs\\_s3 10 16.967154 4.362387 13\n", + " 100 25.240154 1.138464 13\n", + " 1000 25.159769 0.482726 13\n", + "webdav\\_isilon 10 15.608769 1.064103 13\n", + " 100 16.859846 0.792448 13\n", + " 1000 17.200692 0.588769 13\n", + "webdav\\_netapp 10 14.423231 0.914523 13\n", + " 100 15.214538 1.138684 13\n", + " 1000 15.700846 1.141287 13\n", + "webdav\\_s3 10 3.659462 0.539954 13\n", + " 100 4.393231 0.605544 13\n", + " 1000 4.942769 0.795328 13\n", + "##########\n", + "local & 9300 & $\\ \\pm\\ $ & 175 & 493404 & $\\ \\pm\\ $ & 31583 & 50921 & $\\ \\pm\\ $ & 4063\\\\\n", + "nfs\\_isilon & 1143 & $\\ \\pm\\ $ & 180 & 5933 & $\\ \\pm\\ $ & 723 & 668 & $\\ \\pm\\ $ & 95\\\\\n", + "nfs\\_netapp & 511 & $\\ \\pm\\ $ & 138 & 1712 & $\\ \\pm\\ $ & 345 & 349 & $\\ \\pm\\ $ & 139\\\\\n", + "nfs\\_ssdtank & 572 & $\\ \\pm\\ $ & 8 & 5954 & $\\ \\pm\\ $ & 556 & 566 & $\\ \\pm\\ $ & 9\\\\\n", + "irodsfs\\_isilon & 67 & $\\ \\pm\\ $ & 4 & 237524 & $\\ \\pm\\ $ & 13016 & 80 & $\\ \\pm\\ $ & 6\\\\\n", + "irodsfs\\_netapp & 67 & $\\ \\pm\\ $ & 5 & 232570 & $\\ \\pm\\ $ & 21503 & 77 & $\\ \\pm\\ $ & 5\\\\\n", + "irodsfs\\_s3 & 66 & $\\ \\pm\\ $ & 4 & 256696 & $\\ \\pm\\ $ & 22044 & 77 & $\\ \\pm\\ $ & 5\\\\\n", + "webdav\\_isilon & 52 & $\\ \\pm\\ $ & 3 & 37439 & $\\ \\pm\\ $ & 4835 & 24 & $\\ \\pm\\ $ & 1\\\\\n", + "webdav\\_netapp & 53 & $\\ \\pm\\ $ & 3 & 35222 & $\\ \\pm\\ $ & 1093 & 25 & $\\ \\pm\\ $ & 1\\\\\n", + "webdav\\_s3 & 50 & $\\ \\pm\\ $ & 2 & 37180 & $\\ \\pm\\ $ & 6068 & 25 & $\\ \\pm\\ $ & 1\\\\\n", + "##########\n", + "local & 60109 & $\\ \\pm\\ $ & 5031 & 497633 & $\\ \\pm\\ $ & 33653 & 331679 & $\\ \\pm\\ $ & 17961 & 73783 & $\\ \\pm\\ $ & 8161\\\\\n", + "nfs\\_isilon & 886 & $\\ \\pm\\ $ & 114 & 4918 & $\\ \\pm\\ $ & 434 & 1593 & $\\ \\pm\\ $ & 83 & 975 & $\\ \\pm\\ $ & 76\\\\\n", + "nfs\\_netapp & 610 & $\\ \\pm\\ $ & 111 & 1625 & $\\ \\pm\\ $ & 486 & 817 & $\\ \\pm\\ $ & 236 & 1096 & $\\ \\pm\\ $ & 264\\\\\n", + "nfs\\_ssdtank & 534 & $\\ \\pm\\ $ & 14 & 5451 & $\\ \\pm\\ $ & 522 & 2730 & $\\ \\pm\\ $ & 241 & 585 & $\\ \\pm\\ $ & 9\\\\\n", + "irodsfs\\_isilon & 6 & $\\ \\pm\\ $ & 0 & 144 & $\\ \\pm\\ $ & 26 & 14 & $\\ \\pm\\ $ & 1 & 34 & $\\ \\pm\\ $ & 4\\\\\n", + "irodsfs\\_netapp & 6 & $\\ \\pm\\ $ & 0 & 153 & $\\ \\pm\\ $ & 12 & 13 & $\\ \\pm\\ $ & 1 & 33 & $\\ \\pm\\ $ & 4\\\\\n", + "irodsfs\\_s3 & 6 & $\\ \\pm\\ $ & 0 & 172 & $\\ \\pm\\ $ & 10 & 19 & $\\ \\pm\\ $ & 1 & 25 & $\\ \\pm\\ $ & 0\\\\\n", + "webdav\\_isilon & 8 & $\\ \\pm\\ $ & 0 & 13826 & $\\ \\pm\\ $ & 1281 & 7 & $\\ \\pm\\ $ & 0 & 17 & $\\ \\pm\\ $ & 1\\\\\n", + "webdav\\_netapp & 8 & $\\ \\pm\\ $ & 0 & 12532 & $\\ \\pm\\ $ & 1442 & 7 & $\\ \\pm\\ $ & 0 & 16 & $\\ \\pm\\ $ & 1\\\\\n", + "webdav\\_s3 & 5 & $\\ \\pm\\ $ & 0 & 6100 & $\\ \\pm\\ $ & 717 & 4 & $\\ \\pm\\ $ & 0 & 5 & $\\ \\pm\\ $ & 1\\\\\n" + ] + } + ], + "source": [ + "# Read in results\n", + "## Params - Either specify a file prefix or a whole folder\n", + "file_prefix = None # Path(\"./results/2022-07-05T20:00:06_throughput_write_\")\n", + "folder = Path(\"./results/benchmark_metadata\")\n", + "\n", + "## Plot code\n", + "path = file_prefix if file_prefix else folder\n", + "\n", + "metrics = [\n", + " \"dir_create\",\n", + " \"dir_stat\",\n", + " \"dir_remove\",\n", + " \"file_create\",\n", + " \"file_stat\",\n", + " \"file_read\",\n", + " \"file_remove\",\n", + " #\"tree_create\",\n", + " #\"tree_remove\",\n", + "]\n", + "dfs = []\n", + "\n", + "for metric in metrics:\n", + " dfs.append(pd.concat({\n", + " \"local\": load_data(path, metric=metric, suffix=\"metadata_local.json\"),\n", + " \"nfs\\_isilon\": load_data(path, metric=metric, suffix=\"metadata_isilon.json\"),\n", + " \"nfs\\_netapp\": load_data(path, metric=metric, suffix=\"metadata_netapp.json\"),\n", + " \"nfs\\_ssdtank\": load_data(path, metric=metric, suffix=\"metadata_ssd_tank.json\"),\n", + " \"irodsfs\\_isilon\": load_data(path, metric=metric, suffix=\"metadata_irods_fuse_on_isilon.json\"),\n", + " \"irodsfs\\_netapp\": load_data(path, metric=metric, suffix=\"metadata_irods_fuse_on_netapp.json\"),\n", + " \"irodsfs\\_s3\": load_data(path, metric=metric, suffix=\"metadata_irods_fuse_on_s3.json\"),\n", + " \"webdav\\_isilon\": load_data(path, metric=metric, suffix=\"metadata_irods_davrods_on_isilon.json\"),\n", + " \"webdav\\_netapp\": load_data(path, metric=metric, suffix=\"metadata_irods_davrods_on_netapp.json\"),\n", + " \"webdav\\_s3\": load_data(path, metric=metric, suffix=\"metadata_irods_davrods_on_s3.json\"),\n", + " }))\n", + "for metric,df in zip(metrics, dfs):\n", + " print(metric)\n", + " print(df)\n", + " pass\n", + " \n", + "def extract_stats(dfs, num_files, metric_part,label):\n", + " pm = \" & $\\ \\pm\\ $ & \" # Plus/minus seperator\n", + " \n", + " print(f\"{label}\", end=\"\")\n", + " for metric, df in zip(metrics, dfs):\n", + " if metric_part not in metric:\n", + " continue\n", + " current = df.loc[(label, num_files)]\n", + " avg, std = current[\"mean\"], current[\"std\"]\n", + " print(f\" & {avg:.0f}{pm}{std:.0f}\", end=\"\")\n", + " print(\"\\\\\\\\\")\n", + "\n", + "print(\"#\"*10)\n", + "extract_stats(dfs, \"100000\", \"dir_\", \"local\")\n", + "extract_stats(dfs, \"100000\", \"dir_\", \"nfs\\_isilon\")\n", + "extract_stats(dfs, \"100000\", \"dir_\", \"nfs\\_netapp\")\n", + "extract_stats(dfs, \"100000\", \"dir_\", \"nfs\\_ssdtank\")\n", + "extract_stats(dfs, \"1000\", \"dir_\", \"irodsfs\\_isilon\")\n", + "extract_stats(dfs, \"1000\", \"dir_\", \"irodsfs\\_netapp\")\n", + "extract_stats(dfs, \"1000\", \"dir_\", \"irodsfs\\_s3\")\n", + "extract_stats(dfs, \"1000\", \"dir_\", \"webdav\\_isilon\")\n", + "extract_stats(dfs, \"1000\", \"dir_\", \"webdav\\_netapp\")\n", + "extract_stats(dfs, \"1000\", \"dir_\", \"webdav\\_s3\")\n", + "print(\"#\"*10)\n", + "extract_stats(dfs, \"100000\", \"file_\", \"local\")\n", + "extract_stats(dfs, \"100000\", \"file_\", \"nfs\\_isilon\")\n", + "extract_stats(dfs, \"100000\", \"file_\", \"nfs\\_netapp\")\n", + "extract_stats(dfs, \"100000\", \"file_\", \"nfs\\_ssdtank\")\n", + "extract_stats(dfs, \"1000\", \"file_\", \"irodsfs\\_isilon\")\n", + "extract_stats(dfs, \"1000\", \"file_\", \"irodsfs\\_netapp\")\n", + "extract_stats(dfs, \"1000\", \"file_\", \"irodsfs\\_s3\")\n", + "extract_stats(dfs, \"1000\", \"file_\", \"webdav\\_isilon\")\n", + "extract_stats(dfs, \"1000\", \"file_\", \"webdav\\_netapp\")\n", + "extract_stats(dfs, \"1000\", \"file_\", \"webdav\\_s3\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "17ac87fa", + "metadata": {}, + "source": [ + "# Experiment: Compare storage systems (S3)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "4c7b4d20", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " mean std num\n", + "bw_get 791.015000 30.901982 16\n", + "bw_put 1066.654375 60.888718 16\n", + "ops_get 4501.262500 143.819740 8\n", + "ops_put 1060.538750 104.157789 8\n", + "ops_del 2515.205000 124.952359 16\n", + "##########\n", + "Read Bandwidth\n", + "s3\\_netapp & 791 & $\\ \\pm\\ $ & 31& 4501 & $\\ \\pm\\ $ & 144\\\\\n", + "##########\n", + "Write Bandwidth\n", + "s3\\_netapp & 1067 & $\\ \\pm\\ $ & 61& 1061 & $\\ \\pm\\ $ & 104\\\\\n" + ] + } + ], + "source": [ + "# Read in results\n", + "## Params - Either specify a file prefix or a whole folder\n", + "file_prefix = None # Path(\"./results/2022-07-05T20:00:06_throughput_write_\")\n", + "folder = Path(\"./results/benchmark_s3\")\n", + "\n", + "## Plot code\n", + "path = file_prefix if file_prefix else folder\n", + "\n", + "df = pd.concat({\n", + " \"bw_get\": load_data(path, metric=\"get_bw_in_MiB\", suffix=\"s3_warp_get_put.json\"),\n", + " \"bw_put\": load_data(path, metric=\"put_bw_in_MiB\", suffix=\"s3_warp_get_put.json\"),\n", + " \"ops_get\": load_data(path, metric=\"get_ops\", suffix=\"s3_warp_get_put_ops.json\"),\n", + " \"ops_put\": load_data(path, metric=\"put_ops\", suffix=\"s3_warp_get_put_ops.json\"),\n", + " \"ops_del\": load_data(path, metric=\"delete_ops\", suffix=\"s3_warp_delete.json\"),\n", + "})\n", + "df.index = df.index.droplevel(1)\n", + "\n", + "print(df)\n", + "\n", + "df_r_bw = df.loc[(\"bw_get\")]\n", + "df_w_bw = df.loc[(\"bw_put\")]\n", + "df_r_iops = df.loc[(\"ops_get\")]\n", + "df_w_iops = df.loc[(\"ops_put\")]\n", + "\n", + "pm = \" & $\\ \\pm\\ $ & \"\n", + "print(\"#\"*10)\n", + "print(\"Read Bandwidth\")\n", + "print(\"s3\\_netapp \", end=\"\")\n", + "print(f\"& {df_r_bw['mean']:.0f}{pm}{df_r_bw['std']:.0f}\", end=\"\")\n", + "print(f\"& {df_r_iops['mean']:.0f}{pm}{df_r_iops['std']:.0f}\", end=\"\")\n", + "print(\"\\\\\\\\\")\n", + "\n", + "print(\"#\"*10)\n", + "print(\"Write Bandwidth\")\n", + "print(\"s3\\_netapp \", end=\"\")\n", + "print(f\"& {df_w_bw['mean']:.0f}{pm}{df_w_bw['std']:.0f}\", end=\"\")\n", + "print(f\"& {df_w_iops['mean']:.0f}{pm}{df_w_iops['std']:.0f}\", end=\"\")\n", + "print(\"\\\\\\\\\")" + ] + }, + { + "cell_type": "markdown", + "id": "fda89769", + "metadata": {}, + "source": [ + "# Experiment: Different access points for SSD Tank\n" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "c6bfb214", + "metadata": {}, + "outputs": [ + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "/* global mpl */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function () {\n", + " if (typeof WebSocket !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof MozWebSocket !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert(\n", + " 'Your browser does not have WebSocket support. ' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.'\n", + " );\n", + " }\n", + "};\n", + "\n", + "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = this.ws.binaryType !== undefined;\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById('mpl-warnings');\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent =\n", + " 'This browser does not support binary websocket messages. ' +\n", + " 'Performance may be slow.';\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = document.createElement('div');\n", + " this.root.setAttribute('style', 'display: inline-block');\n", + " this._root_extra_style(this.root);\n", + "\n", + " parent_element.appendChild(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message('supports_binary', { value: fig.supports_binary });\n", + " fig.send_message('send_image_mode', {});\n", + " if (fig.ratio !== 1) {\n", + " fig.send_message('set_device_pixel_ratio', {\n", + " device_pixel_ratio: fig.ratio,\n", + " });\n", + " }\n", + " fig.send_message('refresh', {});\n", + " };\n", + "\n", + " this.imageObj.onload = function () {\n", + " if (fig.image_mode === 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function () {\n", + " fig.ws.close();\n", + " };\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "};\n", + "\n", + "mpl.figure.prototype._init_header = function () {\n", + " var titlebar = document.createElement('div');\n", + " titlebar.classList =\n", + " 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n", + " var titletext = document.createElement('div');\n", + " titletext.classList = 'ui-dialog-title';\n", + " titletext.setAttribute(\n", + " 'style',\n", + " 'width: 100%; text-align: center; padding: 3px;'\n", + " );\n", + " titlebar.appendChild(titletext);\n", + " this.root.appendChild(titlebar);\n", + " this.header = titletext;\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._init_canvas = function () {\n", + " var fig = this;\n", + "\n", + " var canvas_div = (this.canvas_div = document.createElement('div'));\n", + " canvas_div.setAttribute(\n", + " 'style',\n", + " 'border: 1px solid #ddd;' +\n", + " 'box-sizing: content-box;' +\n", + " 'clear: both;' +\n", + " 'min-height: 1px;' +\n", + " 'min-width: 1px;' +\n", + " 'outline: 0;' +\n", + " 'overflow: hidden;' +\n", + " 'position: relative;' +\n", + " 'resize: both;'\n", + " );\n", + "\n", + " function on_keyboard_event_closure(name) {\n", + " return function (event) {\n", + " return fig.key_event(event, name);\n", + " };\n", + " }\n", + "\n", + " canvas_div.addEventListener(\n", + " 'keydown',\n", + " on_keyboard_event_closure('key_press')\n", + " );\n", + " canvas_div.addEventListener(\n", + " 'keyup',\n", + " on_keyboard_event_closure('key_release')\n", + " );\n", + "\n", + " this._canvas_extra_style(canvas_div);\n", + " this.root.appendChild(canvas_div);\n", + "\n", + " var canvas = (this.canvas = document.createElement('canvas'));\n", + " canvas.classList.add('mpl-canvas');\n", + " canvas.setAttribute('style', 'box-sizing: content-box;');\n", + "\n", + " this.context = canvas.getContext('2d');\n", + "\n", + " var backingStore =\n", + " this.context.backingStorePixelRatio ||\n", + " this.context.webkitBackingStorePixelRatio ||\n", + " this.context.mozBackingStorePixelRatio ||\n", + " this.context.msBackingStorePixelRatio ||\n", + " this.context.oBackingStorePixelRatio ||\n", + " this.context.backingStorePixelRatio ||\n", + " 1;\n", + "\n", + " this.ratio = (window.devicePixelRatio || 1) / backingStore;\n", + "\n", + " var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n", + " 'canvas'\n", + " ));\n", + " rubberband_canvas.setAttribute(\n", + " 'style',\n", + " 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n", + " );\n", + "\n", + " // Apply a ponyfill if ResizeObserver is not implemented by browser.\n", + " if (this.ResizeObserver === undefined) {\n", + " if (window.ResizeObserver !== undefined) {\n", + " this.ResizeObserver = window.ResizeObserver;\n", + " } else {\n", + " var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n", + " this.ResizeObserver = obs.ResizeObserver;\n", + " }\n", + " }\n", + "\n", + " this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n", + " var nentries = entries.length;\n", + " for (var i = 0; i < nentries; i++) {\n", + " var entry = entries[i];\n", + " var width, height;\n", + " if (entry.contentBoxSize) {\n", + " if (entry.contentBoxSize instanceof Array) {\n", + " // Chrome 84 implements new version of spec.\n", + " width = entry.contentBoxSize[0].inlineSize;\n", + " height = entry.contentBoxSize[0].blockSize;\n", + " } else {\n", + " // Firefox implements old version of spec.\n", + " width = entry.contentBoxSize.inlineSize;\n", + " height = entry.contentBoxSize.blockSize;\n", + " }\n", + " } else {\n", + " // Chrome <84 implements even older version of spec.\n", + " width = entry.contentRect.width;\n", + " height = entry.contentRect.height;\n", + " }\n", + "\n", + " // Keep the size of the canvas and rubber band canvas in sync with\n", + " // the canvas container.\n", + " if (entry.devicePixelContentBoxSize) {\n", + " // Chrome 84 implements new version of spec.\n", + " canvas.setAttribute(\n", + " 'width',\n", + " entry.devicePixelContentBoxSize[0].inlineSize\n", + " );\n", + " canvas.setAttribute(\n", + " 'height',\n", + " entry.devicePixelContentBoxSize[0].blockSize\n", + " );\n", + " } else {\n", + " canvas.setAttribute('width', width * fig.ratio);\n", + " canvas.setAttribute('height', height * fig.ratio);\n", + " }\n", + " canvas.setAttribute(\n", + " 'style',\n", + " 'width: ' + width + 'px; height: ' + height + 'px;'\n", + " );\n", + "\n", + " rubberband_canvas.setAttribute('width', width);\n", + " rubberband_canvas.setAttribute('height', height);\n", + "\n", + " // And update the size in Python. We ignore the initial 0/0 size\n", + " // that occurs as the element is placed into the DOM, which should\n", + " // otherwise not happen due to the minimum size styling.\n", + " if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n", + " fig.request_resize(width, height);\n", + " }\n", + " }\n", + " });\n", + " this.resizeObserverInstance.observe(canvas_div);\n", + "\n", + " function on_mouse_event_closure(name) {\n", + " return function (event) {\n", + " return fig.mouse_event(event, name);\n", + " };\n", + " }\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mousedown',\n", + " on_mouse_event_closure('button_press')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseup',\n", + " on_mouse_event_closure('button_release')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'dblclick',\n", + " on_mouse_event_closure('dblclick')\n", + " );\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband_canvas.addEventListener(\n", + " 'mousemove',\n", + " on_mouse_event_closure('motion_notify')\n", + " );\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseenter',\n", + " on_mouse_event_closure('figure_enter')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseleave',\n", + " on_mouse_event_closure('figure_leave')\n", + " );\n", + "\n", + " canvas_div.addEventListener('wheel', function (event) {\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " on_mouse_event_closure('scroll')(event);\n", + " });\n", + "\n", + " canvas_div.appendChild(canvas);\n", + " canvas_div.appendChild(rubberband_canvas);\n", + "\n", + " this.rubberband_context = rubberband_canvas.getContext('2d');\n", + " this.rubberband_context.strokeStyle = '#000000';\n", + "\n", + " this._resize_canvas = function (width, height, forward) {\n", + " if (forward) {\n", + " canvas_div.style.width = width + 'px';\n", + " canvas_div.style.height = height + 'px';\n", + " }\n", + " };\n", + "\n", + " // Disable right mouse context menu.\n", + " this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n", + " event.preventDefault();\n", + " return false;\n", + " });\n", + "\n", + " function set_focus() {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'mpl-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " continue;\n", + " }\n", + "\n", + " var button = (fig.buttons[name] = document.createElement('button'));\n", + " button.classList = 'mpl-widget';\n", + " button.setAttribute('role', 'button');\n", + " button.setAttribute('aria-disabled', 'false');\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + "\n", + " var icon_img = document.createElement('img');\n", + " icon_img.src = '_images/' + image + '.png';\n", + " icon_img.srcset = '_images/' + image + '_large.png 2x';\n", + " icon_img.alt = tooltip;\n", + " button.appendChild(icon_img);\n", + "\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " var fmt_picker = document.createElement('select');\n", + " fmt_picker.classList = 'mpl-widget';\n", + " toolbar.appendChild(fmt_picker);\n", + " this.format_dropdown = fmt_picker;\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = document.createElement('option');\n", + " option.selected = fmt === mpl.default_extension;\n", + " option.innerHTML = fmt;\n", + " fmt_picker.appendChild(option);\n", + " }\n", + "\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "};\n", + "\n", + "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', { width: x_pixels, height: y_pixels });\n", + "};\n", + "\n", + "mpl.figure.prototype.send_message = function (type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "};\n", + "\n", + "mpl.figure.prototype.send_draw_message = function () {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " var format_dropdown = fig.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " fig.ondownload(fig, format);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_resize = function (fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1], msg['forward']);\n", + " fig.send_message('refresh', {});\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n", + " var x0 = msg['x0'] / fig.ratio;\n", + " var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n", + " var x1 = msg['x1'] / fig.ratio;\n", + " var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0,\n", + " 0,\n", + " fig.canvas.width / fig.ratio,\n", + " fig.canvas.height / fig.ratio\n", + " );\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", + " fig.rubberband_canvas.style.cursor = msg['cursor'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_message = function (fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n", + " for (var key in msg) {\n", + " if (!(key in fig.buttons)) {\n", + " continue;\n", + " }\n", + " fig.buttons[key].disabled = !msg[key];\n", + " fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n", + " if (msg['mode'] === 'PAN') {\n", + " fig.buttons['Pan'].classList.add('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " } else if (msg['mode'] === 'ZOOM') {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.add('active');\n", + " } else {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message('ack', {});\n", + "};\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function (fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " var img = evt.data;\n", + " if (img.type !== 'image/png') {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " img.type = 'image/png';\n", + " }\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src\n", + " );\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " img\n", + " );\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " } else if (\n", + " typeof evt.data === 'string' &&\n", + " evt.data.slice(0, 21) === 'data:image/png;base64'\n", + " ) {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig['handle_' + msg_type];\n", + " } catch (e) {\n", + " console.log(\n", + " \"No handler for the '\" + msg_type + \"' message type: \",\n", + " msg\n", + " );\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\n", + " \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n", + " e,\n", + " e.stack,\n", + " msg\n", + " );\n", + " }\n", + " }\n", + " };\n", + "};\n", + "\n", + "// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function (e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e) {\n", + " e = window.event;\n", + " }\n", + " if (e.target) {\n", + " targ = e.target;\n", + " } else if (e.srcElement) {\n", + " targ = e.srcElement;\n", + " }\n", + " if (targ.nodeType === 3) {\n", + " // defeat Safari bug\n", + " targ = targ.parentNode;\n", + " }\n", + "\n", + " // pageX,Y are the mouse positions relative to the document\n", + " var boundingRect = targ.getBoundingClientRect();\n", + " var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n", + " var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n", + "\n", + " return { x: x, y: y };\n", + "};\n", + "\n", + "/*\n", + " * return a copy of an object with only non-object keys\n", + " * we need this to avoid circular references\n", + " * https://stackoverflow.com/a/24161582/3208463\n", + " */\n", + "function simpleKeys(original) {\n", + " return Object.keys(original).reduce(function (obj, key) {\n", + " if (typeof original[key] !== 'object') {\n", + " obj[key] = original[key];\n", + " }\n", + " return obj;\n", + " }, {});\n", + "}\n", + "\n", + "mpl.figure.prototype.mouse_event = function (event, name) {\n", + " var canvas_pos = mpl.findpos(event);\n", + "\n", + " if (name === 'button_press') {\n", + " this.canvas.focus();\n", + " this.canvas_div.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x * this.ratio;\n", + " var y = canvas_pos.y * this.ratio;\n", + "\n", + " this.send_message(name, {\n", + " x: x,\n", + " y: y,\n", + " button: event.button,\n", + " step: event.step,\n", + " guiEvent: simpleKeys(event),\n", + " });\n", + "\n", + " /* This prevents the web browser from automatically changing to\n", + " * the text insertion cursor when the button is pressed. We want\n", + " * to control all of the cursor setting manually through the\n", + " * 'cursor' event from matplotlib */\n", + " event.preventDefault();\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n", + " // Handle any extra behaviour associated with a key event\n", + "};\n", + "\n", + "mpl.figure.prototype.key_event = function (event, name) {\n", + " // Prevent repeat events\n", + " if (name === 'key_press') {\n", + " if (event.key === this._key) {\n", + " return;\n", + " } else {\n", + " this._key = event.key;\n", + " }\n", + " }\n", + " if (name === 'key_release') {\n", + " this._key = null;\n", + " }\n", + "\n", + " var value = '';\n", + " if (event.ctrlKey && event.key !== 'Control') {\n", + " value += 'ctrl+';\n", + " }\n", + " else if (event.altKey && event.key !== 'Alt') {\n", + " value += 'alt+';\n", + " }\n", + " else if (event.shiftKey && event.key !== 'Shift') {\n", + " value += 'shift+';\n", + " }\n", + "\n", + " value += 'k' + event.key;\n", + "\n", + " this._key_event_extra(event, name);\n", + "\n", + " this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n", + " if (name === 'download') {\n", + " this.handle_save(this, null);\n", + " } else {\n", + " this.send_message('toolbar_button', { name: name });\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n", + " this.message.textContent = tooltip;\n", + "};\n", + "\n", + "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", + "// prettier-ignore\n", + "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";/* global mpl */\n", + "\n", + "var comm_websocket_adapter = function (comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.binaryType = comm.kernel.ws.binaryType;\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " function updateReadyState(_event) {\n", + " if (comm.kernel.ws) {\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " } else {\n", + " ws.readyState = 3; // Closed state.\n", + " }\n", + " }\n", + " comm.kernel.ws.addEventListener('open', updateReadyState);\n", + " comm.kernel.ws.addEventListener('close', updateReadyState);\n", + " comm.kernel.ws.addEventListener('error', updateReadyState);\n", + "\n", + " ws.close = function () {\n", + " comm.close();\n", + " };\n", + " ws.send = function (m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function (msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " var data = msg['content']['data'];\n", + " if (data['blob'] !== undefined) {\n", + " data = {\n", + " data: new Blob(msg['buffers'], { type: data['blob'] }),\n", + " };\n", + " }\n", + " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", + " ws.onmessage(data);\n", + " });\n", + " return ws;\n", + "};\n", + "\n", + "mpl.mpl_figure_comm = function (comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = document.getElementById(id);\n", + " var ws_proxy = comm_websocket_adapter(comm);\n", + "\n", + " function ondownload(figure, _format) {\n", + " window.open(figure.canvas.toDataURL());\n", + " }\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element;\n", + " fig.cell_info = mpl.find_output_cell(\"
\");\n", + " if (!fig.cell_info) {\n", + " console.error('Failed to find cell for figure', id, fig);\n", + " return;\n", + " }\n", + " fig.cell_info[0].output_area.element.on(\n", + " 'cleared',\n", + " { fig: fig },\n", + " fig._remove_fig_handler\n", + " );\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function (fig, msg) {\n", + " var width = fig.canvas.width / fig.ratio;\n", + " fig.cell_info[0].output_area.element.off(\n", + " 'cleared',\n", + " fig._remove_fig_handler\n", + " );\n", + " fig.resizeObserverInstance.unobserve(fig.canvas_div);\n", + "\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable();\n", + " fig.parent_element.innerHTML =\n", + " '';\n", + " fig.close_ws(fig, msg);\n", + "};\n", + "\n", + "mpl.figure.prototype.close_ws = function (fig, msg) {\n", + " fig.send_message('closing', msg);\n", + " // fig.ws.close()\n", + "};\n", + "\n", + "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n", + " // Turn the data on the canvas into data in the output cell.\n", + " var width = this.canvas.width / this.ratio;\n", + " var dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] =\n", + " '';\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message('ack', {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () {\n", + " fig.push_to_output();\n", + " }, 1000);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'btn-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " var button;\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " continue;\n", + " }\n", + "\n", + " button = fig.buttons[name] = document.createElement('button');\n", + " button.classList = 'btn btn-default';\n", + " button.href = '#';\n", + " button.title = name;\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message pull-right';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = document.createElement('div');\n", + " buttongrp.classList = 'btn-group inline pull-right';\n", + " button = document.createElement('button');\n", + " button.classList = 'btn btn-mini btn-primary';\n", + " button.href = '#';\n", + " button.title = 'Stop Interaction';\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', function (_evt) {\n", + " fig.handle_close(fig, {});\n", + " });\n", + " button.addEventListener(\n", + " 'mouseover',\n", + " on_mouseover_closure('Stop Interaction')\n", + " );\n", + " buttongrp.appendChild(button);\n", + " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", + " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", + "};\n", + "\n", + "mpl.figure.prototype._remove_fig_handler = function (event) {\n", + " var fig = event.data.fig;\n", + " if (event.target !== this) {\n", + " // Ignore bubbled events from children.\n", + " return;\n", + " }\n", + " fig.close_ws(fig, {});\n", + "};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (el) {\n", + " el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (el) {\n", + " // this is important to make the div 'focusable\n", + " el.setAttribute('tabindex', 0);\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " } else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which === 13) {\n", + " this.canvas_div.blur();\n", + " // select the cell after this one\n", + " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", + " IPython.notebook.select(index + 1);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " fig.ondownload(fig, null);\n", + "};\n", + "\n", + "mpl.find_output_cell = function (html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i = 0; i < ncells; i++) {\n", + " var cell = cells[i];\n", + " if (cell.cell_type === 'code') {\n", + " for (var j = 0; j < cell.output_area.outputs.length; j++) {\n", + " var data = cell.output_area.outputs[j];\n", + " if (data.data) {\n", + " // IPython >= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] === html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "};\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel !== null) {\n", + " IPython.notebook.kernel.comm_manager.register_target(\n", + " 'matplotlib',\n", + " mpl.mpl_figure_comm\n", + " );\n", + "}\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "---------\n", + "Throughput write\n", + " mean std num\n", + "HDD 10Gb/s 878.803601 18.709457 5\n", + "HDD 40Gb/s 600.292803 87.651497 5\n", + "SSD 10Gb/s 606.877758 26.572561 5\n", + "SSD 40Gb/s 421.088637 9.143627 5\n", + "---------\n", + "Throughput read\n", + " mean std num\n", + "HDD 10Gb/s 492.820954 19.354635 5\n", + "HDD 40Gb/s 621.963134 65.783003 5\n", + "SSD 10Gb/s 395.877308 17.819422 5\n", + "SSD 40Gb/s 327.628254 22.662875 5\n", + "---------\n", + "IOPS write\n", + " mean std num\n", + "HDD 10Gb/s 4318.617123 940.576233 5\n", + "HDD 40Gb/s 9052.378237 1452.820299 5\n", + "SSD 10Gb/s 4953.676898 1122.344462 5\n", + "SSD 40Gb/s 9609.892948 1136.503413 5\n", + "---------\n", + "IOPS read\n", + " mean std num\n", + "HDD 10Gb/s 22353.215900 187.161457 5\n", + "HDD 40Gb/s 43927.077824 693.896096 5\n", + "SSD 10Gb/s 21739.072803 154.185243 5\n", + "SSD 40Gb/s 40184.774059 1188.841197 5\n", + "---------\n", + "Latency write\n", + " mean std num\n", + "HDD 10Gb/s 0.936706 0.027597 5\n", + "HDD 40Gb/s 0.636252 0.005675 5\n", + "SSD 10Gb/s 0.958689 0.016840 5\n", + "SSD 40Gb/s 0.745047 0.017858 5\n", + "---------\n", + "Latency read\n", + " mean std num\n", + "HDD 10Gb/s 0.515506 0.016763 5\n", + "HDD 40Gb/s 0.394226 0.007062 5\n", + "SSD 10Gb/s 0.520675 0.013789 5\n", + "SSD 40Gb/s 0.470065 0.038499 5\n" + ] + } + ], + "source": [ + "%matplotlib notebook\n", + "\n", + "## Params\n", + "result_file_prefix = \"2022-06-08T19:53:53_benchmark\"\n", + "\n", + "## Plot code\n", + "hdd_s = load_json(f\"./results/{result_file_prefix}_isilon_hdd.json\")\n", + "hdd_f = load_json(f\"./results/{result_file_prefix}_isilon_hdd_fast.json\")\n", + "ssd_s = load_json(f\"./results/{result_file_prefix}_isilon_ssd.json\")\n", + "ssd_f = load_json(f\"./results/{result_file_prefix}_isilon_ssd_fast.json\")\n", + "\n", + "benchmarks = [\n", + " (\"throughput_write\", \"write_bw_mean_in_MiB\"),\n", + " (\"throughput_read\", \"read_bw_mean_in_MiB\"),\n", + " (\"iops_write\", \"write_iops_mean\"),\n", + " (\"iops_read\", \"read_iops_mean\"),\n", + " (\"latency_write\", \"write_lat_mean_in_ms\"),\n", + " (\"latency_read\", \"read_lat_mean_in_ms\"),\n", + "]\n", + "\n", + "dfs = []\n", + "for label, metric in benchmarks:\n", + " dfs.append(pd.concat([\n", + " extract_results(hdd_s, metric, [label]).rename(index={label: \"HDD 10Gb/s\"}),\n", + " extract_results(hdd_f, metric, [label]).rename(index={label: \"HDD 40Gb/s\"}),\n", + " extract_results(ssd_s, metric, [label]).rename(index={label: \"SSD 10Gb/s\"}),\n", + " extract_results(ssd_f, metric, [label]).rename(index={label: \"SSD 40Gb/s\"}),\n", + " ]))\n", + "\n", + " \n", + "fig, axis = plt.subplots(3, 2)\n", + "fig.suptitle('Isilon SSD,HDD')\n", + "plt.subplots_adjust(wspace=0.7500, hspace=1.5)\n", + " \n", + "dfs[0][\"mean\"].plot(\n", + " ax=axis[0][0],\n", + " kind='bar', rot=0,\n", + " ylabel='Bandwidth (MiB/s)',\n", + " title='Throughput (w)',\n", + " yerr=dfs[0][\"std\"],\n", + ")\n", + "dfs[1][\"mean\"].plot(\n", + " ax=axis[0][1],\n", + " kind='bar', rot=0,\n", + " title='Throughput (r)',\n", + " yerr=dfs[1][\"std\"],\n", + ")\n", + "\n", + "dfs[2][\"mean\"].plot(\n", + " ax=axis[1][0],\n", + " kind='bar', rot=0,\n", + " ylabel='IOPS',\n", + " title='IOPS (w)',\n", + " yerr=dfs[2][\"std\"]\n", + ")\n", + "\n", + "dfs[3][\"mean\"].plot(\n", + " ax=axis[1][1],\n", + " kind='bar', rot=0,\n", + " title='IOPS (r)',\n", + " yerr=dfs[3][\"std\"],\n", + ")\n", + "dfs[4][\"mean\"].plot(\n", + " ax=axis[2][0],\n", + " kind='bar', rot=0,\n", + " ylabel='Latency (ms)',\n", + " title='Latency (w)',\n", + " yerr=dfs[4][\"std\"]\n", + ")\n", + "dfs[5][\"mean\"].plot(\n", + " ax=axis[2][1],\n", + " kind='bar', rot=0,\n", + " title='Latency (r)',\n", + " yerr=dfs[5][\"std\"],\n", + ")\n", + "\n", + "axis[0][1].sharey(axis[0][0])\n", + "axis[1][0].sharey(axis[1][1])\n", + "axis[2][1].sharey(axis[2][0])\n", + "for i,j in [(0,0),(0,1),(1,0),(1,1),(2,0),(2,1)]:\n", + " axis[i][j].tick_params(labelrotation=20)\n", + " # axis[i][j].get_legend().remove()\n", + " \n", + "#plt.tight_layout()\n", + "# plt.savefig(\"paramstudy_throughput.png\",bbox_inches='tight')\n", + "plt.show()\n", + "\n", + "print(\"---------\", \"Throughput write\", sep=\"\\n\")\n", + "print(dfs[0])\n", + "print(\"---------\", \"Throughput read\", sep=\"\\n\")\n", + "print(dfs[1])\n", + "print(\"---------\", \"IOPS write\", sep=\"\\n\")\n", + "print(dfs[2])\n", + "print(\"---------\", \"IOPS read\", sep=\"\\n\")\n", + "print(dfs[3])\n", + "print(\"---------\", \"Latency write\", sep=\"\\n\")\n", + "print(dfs[4])\n", + "print(\"---------\", \"Latency read\", sep=\"\\n\")\n", + "print(dfs[5])\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "3c1ea5a5", + "metadata": {}, + "source": [ + "# Experiment: Galaxy" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "6450ef1f", + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "No result for: 2022-09-01T15:53:08 10x1G netapp\n", + "No result for: 2022-08-31T08:43:00 1x0 irods_via_davrods_on_isilon\n", + "No result for: 2022-08-31T10:22:48 1x1K irods_via_davrods_on_netapp\n", + "No result for: 2022-09-01T15:50:52 1x1G irods_on_s3\n", + "No result for: 2022-09-01T15:50:52 1x1G irods_via_davrods_on_s3\n", + "No result for: 2022-09-01T15:50:52 1x1G isilon\n", + "No result for: 2022-08-31T11:10:13 10x1K irods_via_davrods_on_isilon\n", + "No result for: 2022-08-30T16:02:19 1x1K s3\n", + "No result for: 2022-09-04T19:23:17 1x1M irods_on_isilon\n", + "No result for: 2022-09-01T15:53:08 10x1G irods_on_isilon\n", + "No result for: 2022-08-31T08:43:00 1x0 s3\n", + "No result for: 2022-08-30T17:41:29 100x1K s3\n", + "No result for: 2022-09-01T15:14:36 1x1M irods_on_netapp\n", + "No result for: 2022-09-01T15:50:52 1x1G irods_on_isilon\n", + "No result for: 2022-09-01T15:50:52 1x1G irods_via_davrods_on_netapp\n", + "No result for: 2022-09-02T22:16:49 10x1G irods_on_s3\n", + "No result for: 2022-09-01T15:53:08 10x1G isilon\n", + "No result for: 2022-09-01T15:14:36 1x1M irods_on_isilon\n", + "No result for: 2022-09-01T15:53:08 10x1G irods_via_davrods_on_s3\n", + "No result for: 2022-09-01T15:53:08 10x1G irods_on_netapp\n", + "No result for: 2022-08-30T18:33:23 1000x1K s3\n", + "No result for: 2022-09-01T15:53:08 10x1G irods_on_s3\n", + "No result for: 2022-09-01T15:50:52 1x1G netapp\n", + "No result for: 2022-09-01T15:53:08 10x1G irods_via_davrods_on_isilon\n", + "No result for: 2022-08-30T22:51:10 1x1M s3\n", + "No result for: 2022-09-01T15:53:08 10x1G irods_via_davrods_on_netapp\n", + "No result for: 2022-09-03T16:25:08 2000x1K netapp\n", + "No result for: 2022-09-01T16:15:08 1x0 isilon\n", + "No result for: 2022-08-30T18:33:23 1000x1K netapp\n", + "No result for: 2022-09-03T00:00:04 1x0 irods_via_davrods_on_isilon\n", + "No result for: 2022-08-31T11:58:15 100x1K irods_on_netapp\n", + "No result for: 2022-09-01T15:50:52 1x1G irods_on_netapp\n", + "No result for: 2022-09-01T15:50:52 1x1G irods_via_davrods_on_isilon\n", + "No result for: 2022-09-01T15:14:36 1x1M irods_via_davrods_on_s3\n", + "No result for: 2022-09-01T15:14:36 1x1M irods_on_s3\n", + "No result for: 2022-08-30T20:18:06 2000x1K s3\n", + "No result for: 2022-08-30T23:34:17 1x1G irods_via_davrods_on_s3\n", + "No result for: 2022-08-30T18:33:23 1000x1K irods_via_davrods_on_isilon\n", + "No result for: 2022-08-30T16:50:41 10x1K s3\n", + "No result for: 2022-08-30T15:20:58 1x0 s3\n", + "['1000x1K', '100x1K', '10x1G', '10x1K', '1x0', '1x1G', '1x1K', '1x1M', '2000x1K']\n", + " mean std num\n", + "1x0 isilon 38.570352 4.155044 20\n", + " netapp 38.019479 4.514666 20\n", + " s3 30.605186 13.899926 18\n", + "1x1K isilon 61.779090 5.862412 19\n", + " netapp 61.750140 5.591803 19\n", + " s3 53.955329 13.170368 17\n", + "10x1K isilon 61.898669 5.417915 19\n", + " netapp 61.687431 5.491162 19\n", + " s3 55.906516 15.133376 17\n", + "100x1K isilon 61.409810 5.781275 20\n", + " netapp 61.627463 5.621175 20\n", + " s3 99.493895 11.511222 18\n", + "1000x1K isilon 74.438915 8.021540 19\n", + " netapp 72.967062 6.368718 18\n", + " s3 384.243165 23.929343 17\n", + "2000x1K isilon 86.488210 10.314142 19\n", + " netapp 85.885138 8.478362 18\n", + " s3 666.036083 68.643406 18\n" + ] + }, + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "/* global mpl */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function () {\n", + " if (typeof WebSocket !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof MozWebSocket !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert(\n", + " 'Your browser does not have WebSocket support. ' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.'\n", + " );\n", + " }\n", + "};\n", + "\n", + "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = this.ws.binaryType !== undefined;\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById('mpl-warnings');\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent =\n", + " 'This browser does not support binary websocket messages. ' +\n", + " 'Performance may be slow.';\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = document.createElement('div');\n", + " this.root.setAttribute('style', 'display: inline-block');\n", + " this._root_extra_style(this.root);\n", + "\n", + " parent_element.appendChild(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message('supports_binary', { value: fig.supports_binary });\n", + " fig.send_message('send_image_mode', {});\n", + " if (fig.ratio !== 1) {\n", + " fig.send_message('set_device_pixel_ratio', {\n", + " device_pixel_ratio: fig.ratio,\n", + " });\n", + " }\n", + " fig.send_message('refresh', {});\n", + " };\n", + "\n", + " this.imageObj.onload = function () {\n", + " if (fig.image_mode === 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function () {\n", + " fig.ws.close();\n", + " };\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "};\n", + "\n", + "mpl.figure.prototype._init_header = function () {\n", + " var titlebar = document.createElement('div');\n", + " titlebar.classList =\n", + " 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n", + " var titletext = document.createElement('div');\n", + " titletext.classList = 'ui-dialog-title';\n", + " titletext.setAttribute(\n", + " 'style',\n", + " 'width: 100%; text-align: center; padding: 3px;'\n", + " );\n", + " titlebar.appendChild(titletext);\n", + " this.root.appendChild(titlebar);\n", + " this.header = titletext;\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._init_canvas = function () {\n", + " var fig = this;\n", + "\n", + " var canvas_div = (this.canvas_div = document.createElement('div'));\n", + " canvas_div.setAttribute(\n", + " 'style',\n", + " 'border: 1px solid #ddd;' +\n", + " 'box-sizing: content-box;' +\n", + " 'clear: both;' +\n", + " 'min-height: 1px;' +\n", + " 'min-width: 1px;' +\n", + " 'outline: 0;' +\n", + " 'overflow: hidden;' +\n", + " 'position: relative;' +\n", + " 'resize: both;'\n", + " );\n", + "\n", + " function on_keyboard_event_closure(name) {\n", + " return function (event) {\n", + " return fig.key_event(event, name);\n", + " };\n", + " }\n", + "\n", + " canvas_div.addEventListener(\n", + " 'keydown',\n", + " on_keyboard_event_closure('key_press')\n", + " );\n", + " canvas_div.addEventListener(\n", + " 'keyup',\n", + " on_keyboard_event_closure('key_release')\n", + " );\n", + "\n", + " this._canvas_extra_style(canvas_div);\n", + " this.root.appendChild(canvas_div);\n", + "\n", + " var canvas = (this.canvas = document.createElement('canvas'));\n", + " canvas.classList.add('mpl-canvas');\n", + " canvas.setAttribute('style', 'box-sizing: content-box;');\n", + "\n", + " this.context = canvas.getContext('2d');\n", + "\n", + " var backingStore =\n", + " this.context.backingStorePixelRatio ||\n", + " this.context.webkitBackingStorePixelRatio ||\n", + " this.context.mozBackingStorePixelRatio ||\n", + " this.context.msBackingStorePixelRatio ||\n", + " this.context.oBackingStorePixelRatio ||\n", + " this.context.backingStorePixelRatio ||\n", + " 1;\n", + "\n", + " this.ratio = (window.devicePixelRatio || 1) / backingStore;\n", + "\n", + " var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n", + " 'canvas'\n", + " ));\n", + " rubberband_canvas.setAttribute(\n", + " 'style',\n", + " 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n", + " );\n", + "\n", + " // Apply a ponyfill if ResizeObserver is not implemented by browser.\n", + " if (this.ResizeObserver === undefined) {\n", + " if (window.ResizeObserver !== undefined) {\n", + " this.ResizeObserver = window.ResizeObserver;\n", + " } else {\n", + " var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n", + " this.ResizeObserver = obs.ResizeObserver;\n", + " }\n", + " }\n", + "\n", + " this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n", + " var nentries = entries.length;\n", + " for (var i = 0; i < nentries; i++) {\n", + " var entry = entries[i];\n", + " var width, height;\n", + " if (entry.contentBoxSize) {\n", + " if (entry.contentBoxSize instanceof Array) {\n", + " // Chrome 84 implements new version of spec.\n", + " width = entry.contentBoxSize[0].inlineSize;\n", + " height = entry.contentBoxSize[0].blockSize;\n", + " } else {\n", + " // Firefox implements old version of spec.\n", + " width = entry.contentBoxSize.inlineSize;\n", + " height = entry.contentBoxSize.blockSize;\n", + " }\n", + " } else {\n", + " // Chrome <84 implements even older version of spec.\n", + " width = entry.contentRect.width;\n", + " height = entry.contentRect.height;\n", + " }\n", + "\n", + " // Keep the size of the canvas and rubber band canvas in sync with\n", + " // the canvas container.\n", + " if (entry.devicePixelContentBoxSize) {\n", + " // Chrome 84 implements new version of spec.\n", + " canvas.setAttribute(\n", + " 'width',\n", + " entry.devicePixelContentBoxSize[0].inlineSize\n", + " );\n", + " canvas.setAttribute(\n", + " 'height',\n", + " entry.devicePixelContentBoxSize[0].blockSize\n", + " );\n", + " } else {\n", + " canvas.setAttribute('width', width * fig.ratio);\n", + " canvas.setAttribute('height', height * fig.ratio);\n", + " }\n", + " canvas.setAttribute(\n", + " 'style',\n", + " 'width: ' + width + 'px; height: ' + height + 'px;'\n", + " );\n", + "\n", + " rubberband_canvas.setAttribute('width', width);\n", + " rubberband_canvas.setAttribute('height', height);\n", + "\n", + " // And update the size in Python. We ignore the initial 0/0 size\n", + " // that occurs as the element is placed into the DOM, which should\n", + " // otherwise not happen due to the minimum size styling.\n", + " if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n", + " fig.request_resize(width, height);\n", + " }\n", + " }\n", + " });\n", + " this.resizeObserverInstance.observe(canvas_div);\n", + "\n", + " function on_mouse_event_closure(name) {\n", + " return function (event) {\n", + " return fig.mouse_event(event, name);\n", + " };\n", + " }\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mousedown',\n", + " on_mouse_event_closure('button_press')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseup',\n", + " on_mouse_event_closure('button_release')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'dblclick',\n", + " on_mouse_event_closure('dblclick')\n", + " );\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband_canvas.addEventListener(\n", + " 'mousemove',\n", + " on_mouse_event_closure('motion_notify')\n", + " );\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseenter',\n", + " on_mouse_event_closure('figure_enter')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseleave',\n", + " on_mouse_event_closure('figure_leave')\n", + " );\n", + "\n", + " canvas_div.addEventListener('wheel', function (event) {\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " on_mouse_event_closure('scroll')(event);\n", + " });\n", + "\n", + " canvas_div.appendChild(canvas);\n", + " canvas_div.appendChild(rubberband_canvas);\n", + "\n", + " this.rubberband_context = rubberband_canvas.getContext('2d');\n", + " this.rubberband_context.strokeStyle = '#000000';\n", + "\n", + " this._resize_canvas = function (width, height, forward) {\n", + " if (forward) {\n", + " canvas_div.style.width = width + 'px';\n", + " canvas_div.style.height = height + 'px';\n", + " }\n", + " };\n", + "\n", + " // Disable right mouse context menu.\n", + " this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n", + " event.preventDefault();\n", + " return false;\n", + " });\n", + "\n", + " function set_focus() {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'mpl-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " continue;\n", + " }\n", + "\n", + " var button = (fig.buttons[name] = document.createElement('button'));\n", + " button.classList = 'mpl-widget';\n", + " button.setAttribute('role', 'button');\n", + " button.setAttribute('aria-disabled', 'false');\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + "\n", + " var icon_img = document.createElement('img');\n", + " icon_img.src = '_images/' + image + '.png';\n", + " icon_img.srcset = '_images/' + image + '_large.png 2x';\n", + " icon_img.alt = tooltip;\n", + " button.appendChild(icon_img);\n", + "\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " var fmt_picker = document.createElement('select');\n", + " fmt_picker.classList = 'mpl-widget';\n", + " toolbar.appendChild(fmt_picker);\n", + " this.format_dropdown = fmt_picker;\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = document.createElement('option');\n", + " option.selected = fmt === mpl.default_extension;\n", + " option.innerHTML = fmt;\n", + " fmt_picker.appendChild(option);\n", + " }\n", + "\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "};\n", + "\n", + "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', { width: x_pixels, height: y_pixels });\n", + "};\n", + "\n", + "mpl.figure.prototype.send_message = function (type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "};\n", + "\n", + "mpl.figure.prototype.send_draw_message = function () {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " var format_dropdown = fig.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " fig.ondownload(fig, format);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_resize = function (fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1], msg['forward']);\n", + " fig.send_message('refresh', {});\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n", + " var x0 = msg['x0'] / fig.ratio;\n", + " var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n", + " var x1 = msg['x1'] / fig.ratio;\n", + " var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0,\n", + " 0,\n", + " fig.canvas.width / fig.ratio,\n", + " fig.canvas.height / fig.ratio\n", + " );\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", + " fig.rubberband_canvas.style.cursor = msg['cursor'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_message = function (fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n", + " for (var key in msg) {\n", + " if (!(key in fig.buttons)) {\n", + " continue;\n", + " }\n", + " fig.buttons[key].disabled = !msg[key];\n", + " fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n", + " if (msg['mode'] === 'PAN') {\n", + " fig.buttons['Pan'].classList.add('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " } else if (msg['mode'] === 'ZOOM') {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.add('active');\n", + " } else {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message('ack', {});\n", + "};\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function (fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " var img = evt.data;\n", + " if (img.type !== 'image/png') {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " img.type = 'image/png';\n", + " }\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src\n", + " );\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " img\n", + " );\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " } else if (\n", + " typeof evt.data === 'string' &&\n", + " evt.data.slice(0, 21) === 'data:image/png;base64'\n", + " ) {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig['handle_' + msg_type];\n", + " } catch (e) {\n", + " console.log(\n", + " \"No handler for the '\" + msg_type + \"' message type: \",\n", + " msg\n", + " );\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\n", + " \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n", + " e,\n", + " e.stack,\n", + " msg\n", + " );\n", + " }\n", + " }\n", + " };\n", + "};\n", + "\n", + "// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function (e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e) {\n", + " e = window.event;\n", + " }\n", + " if (e.target) {\n", + " targ = e.target;\n", + " } else if (e.srcElement) {\n", + " targ = e.srcElement;\n", + " }\n", + " if (targ.nodeType === 3) {\n", + " // defeat Safari bug\n", + " targ = targ.parentNode;\n", + " }\n", + "\n", + " // pageX,Y are the mouse positions relative to the document\n", + " var boundingRect = targ.getBoundingClientRect();\n", + " var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n", + " var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n", + "\n", + " return { x: x, y: y };\n", + "};\n", + "\n", + "/*\n", + " * return a copy of an object with only non-object keys\n", + " * we need this to avoid circular references\n", + " * https://stackoverflow.com/a/24161582/3208463\n", + " */\n", + "function simpleKeys(original) {\n", + " return Object.keys(original).reduce(function (obj, key) {\n", + " if (typeof original[key] !== 'object') {\n", + " obj[key] = original[key];\n", + " }\n", + " return obj;\n", + " }, {});\n", + "}\n", + "\n", + "mpl.figure.prototype.mouse_event = function (event, name) {\n", + " var canvas_pos = mpl.findpos(event);\n", + "\n", + " if (name === 'button_press') {\n", + " this.canvas.focus();\n", + " this.canvas_div.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x * this.ratio;\n", + " var y = canvas_pos.y * this.ratio;\n", + "\n", + " this.send_message(name, {\n", + " x: x,\n", + " y: y,\n", + " button: event.button,\n", + " step: event.step,\n", + " guiEvent: simpleKeys(event),\n", + " });\n", + "\n", + " /* This prevents the web browser from automatically changing to\n", + " * the text insertion cursor when the button is pressed. We want\n", + " * to control all of the cursor setting manually through the\n", + " * 'cursor' event from matplotlib */\n", + " event.preventDefault();\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n", + " // Handle any extra behaviour associated with a key event\n", + "};\n", + "\n", + "mpl.figure.prototype.key_event = function (event, name) {\n", + " // Prevent repeat events\n", + " if (name === 'key_press') {\n", + " if (event.key === this._key) {\n", + " return;\n", + " } else {\n", + " this._key = event.key;\n", + " }\n", + " }\n", + " if (name === 'key_release') {\n", + " this._key = null;\n", + " }\n", + "\n", + " var value = '';\n", + " if (event.ctrlKey && event.key !== 'Control') {\n", + " value += 'ctrl+';\n", + " }\n", + " else if (event.altKey && event.key !== 'Alt') {\n", + " value += 'alt+';\n", + " }\n", + " else if (event.shiftKey && event.key !== 'Shift') {\n", + " value += 'shift+';\n", + " }\n", + "\n", + " value += 'k' + event.key;\n", + "\n", + " this._key_event_extra(event, name);\n", + "\n", + " this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n", + " if (name === 'download') {\n", + " this.handle_save(this, null);\n", + " } else {\n", + " this.send_message('toolbar_button', { name: name });\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n", + " this.message.textContent = tooltip;\n", + "};\n", + "\n", + "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", + "// prettier-ignore\n", + "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";/* global mpl */\n", + "\n", + "var comm_websocket_adapter = function (comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.binaryType = comm.kernel.ws.binaryType;\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " function updateReadyState(_event) {\n", + " if (comm.kernel.ws) {\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " } else {\n", + " ws.readyState = 3; // Closed state.\n", + " }\n", + " }\n", + " comm.kernel.ws.addEventListener('open', updateReadyState);\n", + " comm.kernel.ws.addEventListener('close', updateReadyState);\n", + " comm.kernel.ws.addEventListener('error', updateReadyState);\n", + "\n", + " ws.close = function () {\n", + " comm.close();\n", + " };\n", + " ws.send = function (m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function (msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " var data = msg['content']['data'];\n", + " if (data['blob'] !== undefined) {\n", + " data = {\n", + " data: new Blob(msg['buffers'], { type: data['blob'] }),\n", + " };\n", + " }\n", + " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", + " ws.onmessage(data);\n", + " });\n", + " return ws;\n", + "};\n", + "\n", + "mpl.mpl_figure_comm = function (comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = document.getElementById(id);\n", + " var ws_proxy = comm_websocket_adapter(comm);\n", + "\n", + " function ondownload(figure, _format) {\n", + " window.open(figure.canvas.toDataURL());\n", + " }\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element;\n", + " fig.cell_info = mpl.find_output_cell(\"
\");\n", + " if (!fig.cell_info) {\n", + " console.error('Failed to find cell for figure', id, fig);\n", + " return;\n", + " }\n", + " fig.cell_info[0].output_area.element.on(\n", + " 'cleared',\n", + " { fig: fig },\n", + " fig._remove_fig_handler\n", + " );\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function (fig, msg) {\n", + " var width = fig.canvas.width / fig.ratio;\n", + " fig.cell_info[0].output_area.element.off(\n", + " 'cleared',\n", + " fig._remove_fig_handler\n", + " );\n", + " fig.resizeObserverInstance.unobserve(fig.canvas_div);\n", + "\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable();\n", + " fig.parent_element.innerHTML =\n", + " '';\n", + " fig.close_ws(fig, msg);\n", + "};\n", + "\n", + "mpl.figure.prototype.close_ws = function (fig, msg) {\n", + " fig.send_message('closing', msg);\n", + " // fig.ws.close()\n", + "};\n", + "\n", + "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n", + " // Turn the data on the canvas into data in the output cell.\n", + " var width = this.canvas.width / this.ratio;\n", + " var dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] =\n", + " '';\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message('ack', {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () {\n", + " fig.push_to_output();\n", + " }, 1000);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'btn-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " var button;\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " continue;\n", + " }\n", + "\n", + " button = fig.buttons[name] = document.createElement('button');\n", + " button.classList = 'btn btn-default';\n", + " button.href = '#';\n", + " button.title = name;\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message pull-right';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = document.createElement('div');\n", + " buttongrp.classList = 'btn-group inline pull-right';\n", + " button = document.createElement('button');\n", + " button.classList = 'btn btn-mini btn-primary';\n", + " button.href = '#';\n", + " button.title = 'Stop Interaction';\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', function (_evt) {\n", + " fig.handle_close(fig, {});\n", + " });\n", + " button.addEventListener(\n", + " 'mouseover',\n", + " on_mouseover_closure('Stop Interaction')\n", + " );\n", + " buttongrp.appendChild(button);\n", + " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", + " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", + "};\n", + "\n", + "mpl.figure.prototype._remove_fig_handler = function (event) {\n", + " var fig = event.data.fig;\n", + " if (event.target !== this) {\n", + " // Ignore bubbled events from children.\n", + " return;\n", + " }\n", + " fig.close_ws(fig, {});\n", + "};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (el) {\n", + " el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (el) {\n", + " // this is important to make the div 'focusable\n", + " el.setAttribute('tabindex', 0);\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " } else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which === 13) {\n", + " this.canvas_div.blur();\n", + " // select the cell after this one\n", + " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", + " IPython.notebook.select(index + 1);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " fig.ondownload(fig, null);\n", + "};\n", + "\n", + "mpl.find_output_cell = function (html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i = 0; i < ncells; i++) {\n", + " var cell = cells[i];\n", + " if (cell.cell_type === 'code') {\n", + " for (var j = 0; j < cell.output_area.outputs.length; j++) {\n", + " var data = cell.output_area.outputs[j];\n", + " if (data.data) {\n", + " // IPython >= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] === html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "};\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel !== null) {\n", + " IPython.notebook.kernel.comm_manager.register_target(\n", + " 'matplotlib',\n", + " mpl.mpl_figure_comm\n", + " );\n", + "}\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " mean std num\n", + "1x0 isilon 38.570352 4.155044 20\n", + " netapp 38.019479 4.514666 20\n", + " s3 30.605186 13.899926 18\n", + "1x1K isilon 61.779090 5.862412 19\n", + " netapp 61.750140 5.591803 19\n", + " s3 53.955329 13.170368 17\n", + "1x1M isilon 61.689178 5.719017 19\n", + " netapp 61.496473 5.694692 19\n", + " s3 41.793109 1.196732 17\n", + "1x1G isilon 65.067275 8.125209 18\n", + " netapp 63.104740 5.135069 18\n", + " s3 0.000000 0.000000 1\n", + "10x1G isilon 259.802910 5.809441 9\n", + " netapp 183.547638 5.057323 9\n", + " s3 0.000000 0.000000 1\n" + ] + }, + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "/* global mpl */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function () {\n", + " if (typeof WebSocket !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof MozWebSocket !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert(\n", + " 'Your browser does not have WebSocket support. ' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.'\n", + " );\n", + " }\n", + "};\n", + "\n", + "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = this.ws.binaryType !== undefined;\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById('mpl-warnings');\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent =\n", + " 'This browser does not support binary websocket messages. ' +\n", + " 'Performance may be slow.';\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = document.createElement('div');\n", + " this.root.setAttribute('style', 'display: inline-block');\n", + " this._root_extra_style(this.root);\n", + "\n", + " parent_element.appendChild(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message('supports_binary', { value: fig.supports_binary });\n", + " fig.send_message('send_image_mode', {});\n", + " if (fig.ratio !== 1) {\n", + " fig.send_message('set_device_pixel_ratio', {\n", + " device_pixel_ratio: fig.ratio,\n", + " });\n", + " }\n", + " fig.send_message('refresh', {});\n", + " };\n", + "\n", + " this.imageObj.onload = function () {\n", + " if (fig.image_mode === 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function () {\n", + " fig.ws.close();\n", + " };\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "};\n", + "\n", + "mpl.figure.prototype._init_header = function () {\n", + " var titlebar = document.createElement('div');\n", + " titlebar.classList =\n", + " 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n", + " var titletext = document.createElement('div');\n", + " titletext.classList = 'ui-dialog-title';\n", + " titletext.setAttribute(\n", + " 'style',\n", + " 'width: 100%; text-align: center; padding: 3px;'\n", + " );\n", + " titlebar.appendChild(titletext);\n", + " this.root.appendChild(titlebar);\n", + " this.header = titletext;\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._init_canvas = function () {\n", + " var fig = this;\n", + "\n", + " var canvas_div = (this.canvas_div = document.createElement('div'));\n", + " canvas_div.setAttribute(\n", + " 'style',\n", + " 'border: 1px solid #ddd;' +\n", + " 'box-sizing: content-box;' +\n", + " 'clear: both;' +\n", + " 'min-height: 1px;' +\n", + " 'min-width: 1px;' +\n", + " 'outline: 0;' +\n", + " 'overflow: hidden;' +\n", + " 'position: relative;' +\n", + " 'resize: both;'\n", + " );\n", + "\n", + " function on_keyboard_event_closure(name) {\n", + " return function (event) {\n", + " return fig.key_event(event, name);\n", + " };\n", + " }\n", + "\n", + " canvas_div.addEventListener(\n", + " 'keydown',\n", + " on_keyboard_event_closure('key_press')\n", + " );\n", + " canvas_div.addEventListener(\n", + " 'keyup',\n", + " on_keyboard_event_closure('key_release')\n", + " );\n", + "\n", + " this._canvas_extra_style(canvas_div);\n", + " this.root.appendChild(canvas_div);\n", + "\n", + " var canvas = (this.canvas = document.createElement('canvas'));\n", + " canvas.classList.add('mpl-canvas');\n", + " canvas.setAttribute('style', 'box-sizing: content-box;');\n", + "\n", + " this.context = canvas.getContext('2d');\n", + "\n", + " var backingStore =\n", + " this.context.backingStorePixelRatio ||\n", + " this.context.webkitBackingStorePixelRatio ||\n", + " this.context.mozBackingStorePixelRatio ||\n", + " this.context.msBackingStorePixelRatio ||\n", + " this.context.oBackingStorePixelRatio ||\n", + " this.context.backingStorePixelRatio ||\n", + " 1;\n", + "\n", + " this.ratio = (window.devicePixelRatio || 1) / backingStore;\n", + "\n", + " var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n", + " 'canvas'\n", + " ));\n", + " rubberband_canvas.setAttribute(\n", + " 'style',\n", + " 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n", + " );\n", + "\n", + " // Apply a ponyfill if ResizeObserver is not implemented by browser.\n", + " if (this.ResizeObserver === undefined) {\n", + " if (window.ResizeObserver !== undefined) {\n", + " this.ResizeObserver = window.ResizeObserver;\n", + " } else {\n", + " var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n", + " this.ResizeObserver = obs.ResizeObserver;\n", + " }\n", + " }\n", + "\n", + " this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n", + " var nentries = entries.length;\n", + " for (var i = 0; i < nentries; i++) {\n", + " var entry = entries[i];\n", + " var width, height;\n", + " if (entry.contentBoxSize) {\n", + " if (entry.contentBoxSize instanceof Array) {\n", + " // Chrome 84 implements new version of spec.\n", + " width = entry.contentBoxSize[0].inlineSize;\n", + " height = entry.contentBoxSize[0].blockSize;\n", + " } else {\n", + " // Firefox implements old version of spec.\n", + " width = entry.contentBoxSize.inlineSize;\n", + " height = entry.contentBoxSize.blockSize;\n", + " }\n", + " } else {\n", + " // Chrome <84 implements even older version of spec.\n", + " width = entry.contentRect.width;\n", + " height = entry.contentRect.height;\n", + " }\n", + "\n", + " // Keep the size of the canvas and rubber band canvas in sync with\n", + " // the canvas container.\n", + " if (entry.devicePixelContentBoxSize) {\n", + " // Chrome 84 implements new version of spec.\n", + " canvas.setAttribute(\n", + " 'width',\n", + " entry.devicePixelContentBoxSize[0].inlineSize\n", + " );\n", + " canvas.setAttribute(\n", + " 'height',\n", + " entry.devicePixelContentBoxSize[0].blockSize\n", + " );\n", + " } else {\n", + " canvas.setAttribute('width', width * fig.ratio);\n", + " canvas.setAttribute('height', height * fig.ratio);\n", + " }\n", + " canvas.setAttribute(\n", + " 'style',\n", + " 'width: ' + width + 'px; height: ' + height + 'px;'\n", + " );\n", + "\n", + " rubberband_canvas.setAttribute('width', width);\n", + " rubberband_canvas.setAttribute('height', height);\n", + "\n", + " // And update the size in Python. We ignore the initial 0/0 size\n", + " // that occurs as the element is placed into the DOM, which should\n", + " // otherwise not happen due to the minimum size styling.\n", + " if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n", + " fig.request_resize(width, height);\n", + " }\n", + " }\n", + " });\n", + " this.resizeObserverInstance.observe(canvas_div);\n", + "\n", + " function on_mouse_event_closure(name) {\n", + " return function (event) {\n", + " return fig.mouse_event(event, name);\n", + " };\n", + " }\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mousedown',\n", + " on_mouse_event_closure('button_press')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseup',\n", + " on_mouse_event_closure('button_release')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'dblclick',\n", + " on_mouse_event_closure('dblclick')\n", + " );\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband_canvas.addEventListener(\n", + " 'mousemove',\n", + " on_mouse_event_closure('motion_notify')\n", + " );\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseenter',\n", + " on_mouse_event_closure('figure_enter')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseleave',\n", + " on_mouse_event_closure('figure_leave')\n", + " );\n", + "\n", + " canvas_div.addEventListener('wheel', function (event) {\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " on_mouse_event_closure('scroll')(event);\n", + " });\n", + "\n", + " canvas_div.appendChild(canvas);\n", + " canvas_div.appendChild(rubberband_canvas);\n", + "\n", + " this.rubberband_context = rubberband_canvas.getContext('2d');\n", + " this.rubberband_context.strokeStyle = '#000000';\n", + "\n", + " this._resize_canvas = function (width, height, forward) {\n", + " if (forward) {\n", + " canvas_div.style.width = width + 'px';\n", + " canvas_div.style.height = height + 'px';\n", + " }\n", + " };\n", + "\n", + " // Disable right mouse context menu.\n", + " this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n", + " event.preventDefault();\n", + " return false;\n", + " });\n", + "\n", + " function set_focus() {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'mpl-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " continue;\n", + " }\n", + "\n", + " var button = (fig.buttons[name] = document.createElement('button'));\n", + " button.classList = 'mpl-widget';\n", + " button.setAttribute('role', 'button');\n", + " button.setAttribute('aria-disabled', 'false');\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + "\n", + " var icon_img = document.createElement('img');\n", + " icon_img.src = '_images/' + image + '.png';\n", + " icon_img.srcset = '_images/' + image + '_large.png 2x';\n", + " icon_img.alt = tooltip;\n", + " button.appendChild(icon_img);\n", + "\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " var fmt_picker = document.createElement('select');\n", + " fmt_picker.classList = 'mpl-widget';\n", + " toolbar.appendChild(fmt_picker);\n", + " this.format_dropdown = fmt_picker;\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = document.createElement('option');\n", + " option.selected = fmt === mpl.default_extension;\n", + " option.innerHTML = fmt;\n", + " fmt_picker.appendChild(option);\n", + " }\n", + "\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "};\n", + "\n", + "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', { width: x_pixels, height: y_pixels });\n", + "};\n", + "\n", + "mpl.figure.prototype.send_message = function (type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "};\n", + "\n", + "mpl.figure.prototype.send_draw_message = function () {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " var format_dropdown = fig.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " fig.ondownload(fig, format);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_resize = function (fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1], msg['forward']);\n", + " fig.send_message('refresh', {});\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n", + " var x0 = msg['x0'] / fig.ratio;\n", + " var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n", + " var x1 = msg['x1'] / fig.ratio;\n", + " var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0,\n", + " 0,\n", + " fig.canvas.width / fig.ratio,\n", + " fig.canvas.height / fig.ratio\n", + " );\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", + " fig.rubberband_canvas.style.cursor = msg['cursor'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_message = function (fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n", + " for (var key in msg) {\n", + " if (!(key in fig.buttons)) {\n", + " continue;\n", + " }\n", + " fig.buttons[key].disabled = !msg[key];\n", + " fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n", + " if (msg['mode'] === 'PAN') {\n", + " fig.buttons['Pan'].classList.add('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " } else if (msg['mode'] === 'ZOOM') {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.add('active');\n", + " } else {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message('ack', {});\n", + "};\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function (fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " var img = evt.data;\n", + " if (img.type !== 'image/png') {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " img.type = 'image/png';\n", + " }\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src\n", + " );\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " img\n", + " );\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " } else if (\n", + " typeof evt.data === 'string' &&\n", + " evt.data.slice(0, 21) === 'data:image/png;base64'\n", + " ) {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig['handle_' + msg_type];\n", + " } catch (e) {\n", + " console.log(\n", + " \"No handler for the '\" + msg_type + \"' message type: \",\n", + " msg\n", + " );\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\n", + " \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n", + " e,\n", + " e.stack,\n", + " msg\n", + " );\n", + " }\n", + " }\n", + " };\n", + "};\n", + "\n", + "// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function (e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e) {\n", + " e = window.event;\n", + " }\n", + " if (e.target) {\n", + " targ = e.target;\n", + " } else if (e.srcElement) {\n", + " targ = e.srcElement;\n", + " }\n", + " if (targ.nodeType === 3) {\n", + " // defeat Safari bug\n", + " targ = targ.parentNode;\n", + " }\n", + "\n", + " // pageX,Y are the mouse positions relative to the document\n", + " var boundingRect = targ.getBoundingClientRect();\n", + " var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n", + " var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n", + "\n", + " return { x: x, y: y };\n", + "};\n", + "\n", + "/*\n", + " * return a copy of an object with only non-object keys\n", + " * we need this to avoid circular references\n", + " * https://stackoverflow.com/a/24161582/3208463\n", + " */\n", + "function simpleKeys(original) {\n", + " return Object.keys(original).reduce(function (obj, key) {\n", + " if (typeof original[key] !== 'object') {\n", + " obj[key] = original[key];\n", + " }\n", + " return obj;\n", + " }, {});\n", + "}\n", + "\n", + "mpl.figure.prototype.mouse_event = function (event, name) {\n", + " var canvas_pos = mpl.findpos(event);\n", + "\n", + " if (name === 'button_press') {\n", + " this.canvas.focus();\n", + " this.canvas_div.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x * this.ratio;\n", + " var y = canvas_pos.y * this.ratio;\n", + "\n", + " this.send_message(name, {\n", + " x: x,\n", + " y: y,\n", + " button: event.button,\n", + " step: event.step,\n", + " guiEvent: simpleKeys(event),\n", + " });\n", + "\n", + " /* This prevents the web browser from automatically changing to\n", + " * the text insertion cursor when the button is pressed. We want\n", + " * to control all of the cursor setting manually through the\n", + " * 'cursor' event from matplotlib */\n", + " event.preventDefault();\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n", + " // Handle any extra behaviour associated with a key event\n", + "};\n", + "\n", + "mpl.figure.prototype.key_event = function (event, name) {\n", + " // Prevent repeat events\n", + " if (name === 'key_press') {\n", + " if (event.key === this._key) {\n", + " return;\n", + " } else {\n", + " this._key = event.key;\n", + " }\n", + " }\n", + " if (name === 'key_release') {\n", + " this._key = null;\n", + " }\n", + "\n", + " var value = '';\n", + " if (event.ctrlKey && event.key !== 'Control') {\n", + " value += 'ctrl+';\n", + " }\n", + " else if (event.altKey && event.key !== 'Alt') {\n", + " value += 'alt+';\n", + " }\n", + " else if (event.shiftKey && event.key !== 'Shift') {\n", + " value += 'shift+';\n", + " }\n", + "\n", + " value += 'k' + event.key;\n", + "\n", + " this._key_event_extra(event, name);\n", + "\n", + " this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n", + " if (name === 'download') {\n", + " this.handle_save(this, null);\n", + " } else {\n", + " this.send_message('toolbar_button', { name: name });\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n", + " this.message.textContent = tooltip;\n", + "};\n", + "\n", + "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", + "// prettier-ignore\n", + "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";/* global mpl */\n", + "\n", + "var comm_websocket_adapter = function (comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.binaryType = comm.kernel.ws.binaryType;\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " function updateReadyState(_event) {\n", + " if (comm.kernel.ws) {\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " } else {\n", + " ws.readyState = 3; // Closed state.\n", + " }\n", + " }\n", + " comm.kernel.ws.addEventListener('open', updateReadyState);\n", + " comm.kernel.ws.addEventListener('close', updateReadyState);\n", + " comm.kernel.ws.addEventListener('error', updateReadyState);\n", + "\n", + " ws.close = function () {\n", + " comm.close();\n", + " };\n", + " ws.send = function (m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function (msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " var data = msg['content']['data'];\n", + " if (data['blob'] !== undefined) {\n", + " data = {\n", + " data: new Blob(msg['buffers'], { type: data['blob'] }),\n", + " };\n", + " }\n", + " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", + " ws.onmessage(data);\n", + " });\n", + " return ws;\n", + "};\n", + "\n", + "mpl.mpl_figure_comm = function (comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = document.getElementById(id);\n", + " var ws_proxy = comm_websocket_adapter(comm);\n", + "\n", + " function ondownload(figure, _format) {\n", + " window.open(figure.canvas.toDataURL());\n", + " }\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element;\n", + " fig.cell_info = mpl.find_output_cell(\"
\");\n", + " if (!fig.cell_info) {\n", + " console.error('Failed to find cell for figure', id, fig);\n", + " return;\n", + " }\n", + " fig.cell_info[0].output_area.element.on(\n", + " 'cleared',\n", + " { fig: fig },\n", + " fig._remove_fig_handler\n", + " );\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function (fig, msg) {\n", + " var width = fig.canvas.width / fig.ratio;\n", + " fig.cell_info[0].output_area.element.off(\n", + " 'cleared',\n", + " fig._remove_fig_handler\n", + " );\n", + " fig.resizeObserverInstance.unobserve(fig.canvas_div);\n", + "\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable();\n", + " fig.parent_element.innerHTML =\n", + " '';\n", + " fig.close_ws(fig, msg);\n", + "};\n", + "\n", + "mpl.figure.prototype.close_ws = function (fig, msg) {\n", + " fig.send_message('closing', msg);\n", + " // fig.ws.close()\n", + "};\n", + "\n", + "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n", + " // Turn the data on the canvas into data in the output cell.\n", + " var width = this.canvas.width / this.ratio;\n", + " var dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] =\n", + " '';\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message('ack', {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () {\n", + " fig.push_to_output();\n", + " }, 1000);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'btn-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " var button;\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " continue;\n", + " }\n", + "\n", + " button = fig.buttons[name] = document.createElement('button');\n", + " button.classList = 'btn btn-default';\n", + " button.href = '#';\n", + " button.title = name;\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message pull-right';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = document.createElement('div');\n", + " buttongrp.classList = 'btn-group inline pull-right';\n", + " button = document.createElement('button');\n", + " button.classList = 'btn btn-mini btn-primary';\n", + " button.href = '#';\n", + " button.title = 'Stop Interaction';\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', function (_evt) {\n", + " fig.handle_close(fig, {});\n", + " });\n", + " button.addEventListener(\n", + " 'mouseover',\n", + " on_mouseover_closure('Stop Interaction')\n", + " );\n", + " buttongrp.appendChild(button);\n", + " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", + " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", + "};\n", + "\n", + "mpl.figure.prototype._remove_fig_handler = function (event) {\n", + " var fig = event.data.fig;\n", + " if (event.target !== this) {\n", + " // Ignore bubbled events from children.\n", + " return;\n", + " }\n", + " fig.close_ws(fig, {});\n", + "};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (el) {\n", + " el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (el) {\n", + " // this is important to make the div 'focusable\n", + " el.setAttribute('tabindex', 0);\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " } else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which === 13) {\n", + " this.canvas_div.blur();\n", + " // select the cell after this one\n", + " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", + " IPython.notebook.select(index + 1);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " fig.ondownload(fig, null);\n", + "};\n", + "\n", + "mpl.find_output_cell = function (html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i = 0; i < ncells; i++) {\n", + " var cell = cells[i];\n", + " if (cell.cell_type === 'code') {\n", + " for (var j = 0; j < cell.output_area.outputs.length; j++) {\n", + " var data = cell.output_area.outputs[j];\n", + " if (data.data) {\n", + " // IPython >= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] === html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "};\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel !== null) {\n", + " IPython.notebook.kernel.comm_manager.register_target(\n", + " 'matplotlib',\n", + " mpl.mpl_figure_comm\n", + " );\n", + "}\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " mean std num\n", + "1x0 irods_on_isilon 58.561781 28.682517 19\n", + " irods_on_netapp 39.473920 1.297394 10\n", + " irods_on_s3 38.528168 4.646165 19\n", + "1x1K irods_on_isilon 100.219069 1.270263 19\n", + " irods_on_netapp 100.081593 1.281383 10\n", + " irods_on_s3 61.579468 5.641403 19\n", + "10x1K irods_on_isilon 100.312221 1.484970 19\n", + " irods_on_netapp 100.054191 1.617283 10\n", + " irods_on_s3 69.618712 5.310661 19\n", + "100x1K irods_on_isilon 111.333367 6.102356 19\n", + " irods_on_netapp 107.249967 4.786830 9\n", + " irods_on_s3 142.857798 6.003477 19\n", + "1000x1K irods_on_isilon 575.954983 29.149668 19\n", + " irods_on_netapp 575.380933 13.318009 10\n", + " irods_on_s3 857.795866 17.849279 19\n", + "2000x1K irods_on_isilon 1082.106492 18.186798 19\n", + " irods_on_netapp 1088.393951 20.461509 10\n", + " irods_on_s3 1657.198189 49.299785 19\n" + ] + }, + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "/* global mpl */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function () {\n", + " if (typeof WebSocket !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof MozWebSocket !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert(\n", + " 'Your browser does not have WebSocket support. ' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.'\n", + " );\n", + " }\n", + "};\n", + "\n", + "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = this.ws.binaryType !== undefined;\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById('mpl-warnings');\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent =\n", + " 'This browser does not support binary websocket messages. ' +\n", + " 'Performance may be slow.';\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = document.createElement('div');\n", + " this.root.setAttribute('style', 'display: inline-block');\n", + " this._root_extra_style(this.root);\n", + "\n", + " parent_element.appendChild(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message('supports_binary', { value: fig.supports_binary });\n", + " fig.send_message('send_image_mode', {});\n", + " if (fig.ratio !== 1) {\n", + " fig.send_message('set_device_pixel_ratio', {\n", + " device_pixel_ratio: fig.ratio,\n", + " });\n", + " }\n", + " fig.send_message('refresh', {});\n", + " };\n", + "\n", + " this.imageObj.onload = function () {\n", + " if (fig.image_mode === 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function () {\n", + " fig.ws.close();\n", + " };\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "};\n", + "\n", + "mpl.figure.prototype._init_header = function () {\n", + " var titlebar = document.createElement('div');\n", + " titlebar.classList =\n", + " 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n", + " var titletext = document.createElement('div');\n", + " titletext.classList = 'ui-dialog-title';\n", + " titletext.setAttribute(\n", + " 'style',\n", + " 'width: 100%; text-align: center; padding: 3px;'\n", + " );\n", + " titlebar.appendChild(titletext);\n", + " this.root.appendChild(titlebar);\n", + " this.header = titletext;\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._init_canvas = function () {\n", + " var fig = this;\n", + "\n", + " var canvas_div = (this.canvas_div = document.createElement('div'));\n", + " canvas_div.setAttribute(\n", + " 'style',\n", + " 'border: 1px solid #ddd;' +\n", + " 'box-sizing: content-box;' +\n", + " 'clear: both;' +\n", + " 'min-height: 1px;' +\n", + " 'min-width: 1px;' +\n", + " 'outline: 0;' +\n", + " 'overflow: hidden;' +\n", + " 'position: relative;' +\n", + " 'resize: both;'\n", + " );\n", + "\n", + " function on_keyboard_event_closure(name) {\n", + " return function (event) {\n", + " return fig.key_event(event, name);\n", + " };\n", + " }\n", + "\n", + " canvas_div.addEventListener(\n", + " 'keydown',\n", + " on_keyboard_event_closure('key_press')\n", + " );\n", + " canvas_div.addEventListener(\n", + " 'keyup',\n", + " on_keyboard_event_closure('key_release')\n", + " );\n", + "\n", + " this._canvas_extra_style(canvas_div);\n", + " this.root.appendChild(canvas_div);\n", + "\n", + " var canvas = (this.canvas = document.createElement('canvas'));\n", + " canvas.classList.add('mpl-canvas');\n", + " canvas.setAttribute('style', 'box-sizing: content-box;');\n", + "\n", + " this.context = canvas.getContext('2d');\n", + "\n", + " var backingStore =\n", + " this.context.backingStorePixelRatio ||\n", + " this.context.webkitBackingStorePixelRatio ||\n", + " this.context.mozBackingStorePixelRatio ||\n", + " this.context.msBackingStorePixelRatio ||\n", + " this.context.oBackingStorePixelRatio ||\n", + " this.context.backingStorePixelRatio ||\n", + " 1;\n", + "\n", + " this.ratio = (window.devicePixelRatio || 1) / backingStore;\n", + "\n", + " var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n", + " 'canvas'\n", + " ));\n", + " rubberband_canvas.setAttribute(\n", + " 'style',\n", + " 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n", + " );\n", + "\n", + " // Apply a ponyfill if ResizeObserver is not implemented by browser.\n", + " if (this.ResizeObserver === undefined) {\n", + " if (window.ResizeObserver !== undefined) {\n", + " this.ResizeObserver = window.ResizeObserver;\n", + " } else {\n", + " var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n", + " this.ResizeObserver = obs.ResizeObserver;\n", + " }\n", + " }\n", + "\n", + " this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n", + " var nentries = entries.length;\n", + " for (var i = 0; i < nentries; i++) {\n", + " var entry = entries[i];\n", + " var width, height;\n", + " if (entry.contentBoxSize) {\n", + " if (entry.contentBoxSize instanceof Array) {\n", + " // Chrome 84 implements new version of spec.\n", + " width = entry.contentBoxSize[0].inlineSize;\n", + " height = entry.contentBoxSize[0].blockSize;\n", + " } else {\n", + " // Firefox implements old version of spec.\n", + " width = entry.contentBoxSize.inlineSize;\n", + " height = entry.contentBoxSize.blockSize;\n", + " }\n", + " } else {\n", + " // Chrome <84 implements even older version of spec.\n", + " width = entry.contentRect.width;\n", + " height = entry.contentRect.height;\n", + " }\n", + "\n", + " // Keep the size of the canvas and rubber band canvas in sync with\n", + " // the canvas container.\n", + " if (entry.devicePixelContentBoxSize) {\n", + " // Chrome 84 implements new version of spec.\n", + " canvas.setAttribute(\n", + " 'width',\n", + " entry.devicePixelContentBoxSize[0].inlineSize\n", + " );\n", + " canvas.setAttribute(\n", + " 'height',\n", + " entry.devicePixelContentBoxSize[0].blockSize\n", + " );\n", + " } else {\n", + " canvas.setAttribute('width', width * fig.ratio);\n", + " canvas.setAttribute('height', height * fig.ratio);\n", + " }\n", + " canvas.setAttribute(\n", + " 'style',\n", + " 'width: ' + width + 'px; height: ' + height + 'px;'\n", + " );\n", + "\n", + " rubberband_canvas.setAttribute('width', width);\n", + " rubberband_canvas.setAttribute('height', height);\n", + "\n", + " // And update the size in Python. We ignore the initial 0/0 size\n", + " // that occurs as the element is placed into the DOM, which should\n", + " // otherwise not happen due to the minimum size styling.\n", + " if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n", + " fig.request_resize(width, height);\n", + " }\n", + " }\n", + " });\n", + " this.resizeObserverInstance.observe(canvas_div);\n", + "\n", + " function on_mouse_event_closure(name) {\n", + " return function (event) {\n", + " return fig.mouse_event(event, name);\n", + " };\n", + " }\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mousedown',\n", + " on_mouse_event_closure('button_press')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseup',\n", + " on_mouse_event_closure('button_release')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'dblclick',\n", + " on_mouse_event_closure('dblclick')\n", + " );\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband_canvas.addEventListener(\n", + " 'mousemove',\n", + " on_mouse_event_closure('motion_notify')\n", + " );\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseenter',\n", + " on_mouse_event_closure('figure_enter')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseleave',\n", + " on_mouse_event_closure('figure_leave')\n", + " );\n", + "\n", + " canvas_div.addEventListener('wheel', function (event) {\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " on_mouse_event_closure('scroll')(event);\n", + " });\n", + "\n", + " canvas_div.appendChild(canvas);\n", + " canvas_div.appendChild(rubberband_canvas);\n", + "\n", + " this.rubberband_context = rubberband_canvas.getContext('2d');\n", + " this.rubberband_context.strokeStyle = '#000000';\n", + "\n", + " this._resize_canvas = function (width, height, forward) {\n", + " if (forward) {\n", + " canvas_div.style.width = width + 'px';\n", + " canvas_div.style.height = height + 'px';\n", + " }\n", + " };\n", + "\n", + " // Disable right mouse context menu.\n", + " this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n", + " event.preventDefault();\n", + " return false;\n", + " });\n", + "\n", + " function set_focus() {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'mpl-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " continue;\n", + " }\n", + "\n", + " var button = (fig.buttons[name] = document.createElement('button'));\n", + " button.classList = 'mpl-widget';\n", + " button.setAttribute('role', 'button');\n", + " button.setAttribute('aria-disabled', 'false');\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + "\n", + " var icon_img = document.createElement('img');\n", + " icon_img.src = '_images/' + image + '.png';\n", + " icon_img.srcset = '_images/' + image + '_large.png 2x';\n", + " icon_img.alt = tooltip;\n", + " button.appendChild(icon_img);\n", + "\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " var fmt_picker = document.createElement('select');\n", + " fmt_picker.classList = 'mpl-widget';\n", + " toolbar.appendChild(fmt_picker);\n", + " this.format_dropdown = fmt_picker;\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = document.createElement('option');\n", + " option.selected = fmt === mpl.default_extension;\n", + " option.innerHTML = fmt;\n", + " fmt_picker.appendChild(option);\n", + " }\n", + "\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "};\n", + "\n", + "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', { width: x_pixels, height: y_pixels });\n", + "};\n", + "\n", + "mpl.figure.prototype.send_message = function (type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "};\n", + "\n", + "mpl.figure.prototype.send_draw_message = function () {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " var format_dropdown = fig.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " fig.ondownload(fig, format);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_resize = function (fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1], msg['forward']);\n", + " fig.send_message('refresh', {});\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n", + " var x0 = msg['x0'] / fig.ratio;\n", + " var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n", + " var x1 = msg['x1'] / fig.ratio;\n", + " var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0,\n", + " 0,\n", + " fig.canvas.width / fig.ratio,\n", + " fig.canvas.height / fig.ratio\n", + " );\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", + " fig.rubberband_canvas.style.cursor = msg['cursor'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_message = function (fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n", + " for (var key in msg) {\n", + " if (!(key in fig.buttons)) {\n", + " continue;\n", + " }\n", + " fig.buttons[key].disabled = !msg[key];\n", + " fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n", + " if (msg['mode'] === 'PAN') {\n", + " fig.buttons['Pan'].classList.add('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " } else if (msg['mode'] === 'ZOOM') {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.add('active');\n", + " } else {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message('ack', {});\n", + "};\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function (fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " var img = evt.data;\n", + " if (img.type !== 'image/png') {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " img.type = 'image/png';\n", + " }\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src\n", + " );\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " img\n", + " );\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " } else if (\n", + " typeof evt.data === 'string' &&\n", + " evt.data.slice(0, 21) === 'data:image/png;base64'\n", + " ) {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig['handle_' + msg_type];\n", + " } catch (e) {\n", + " console.log(\n", + " \"No handler for the '\" + msg_type + \"' message type: \",\n", + " msg\n", + " );\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\n", + " \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n", + " e,\n", + " e.stack,\n", + " msg\n", + " );\n", + " }\n", + " }\n", + " };\n", + "};\n", + "\n", + "// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function (e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e) {\n", + " e = window.event;\n", + " }\n", + " if (e.target) {\n", + " targ = e.target;\n", + " } else if (e.srcElement) {\n", + " targ = e.srcElement;\n", + " }\n", + " if (targ.nodeType === 3) {\n", + " // defeat Safari bug\n", + " targ = targ.parentNode;\n", + " }\n", + "\n", + " // pageX,Y are the mouse positions relative to the document\n", + " var boundingRect = targ.getBoundingClientRect();\n", + " var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n", + " var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n", + "\n", + " return { x: x, y: y };\n", + "};\n", + "\n", + "/*\n", + " * return a copy of an object with only non-object keys\n", + " * we need this to avoid circular references\n", + " * https://stackoverflow.com/a/24161582/3208463\n", + " */\n", + "function simpleKeys(original) {\n", + " return Object.keys(original).reduce(function (obj, key) {\n", + " if (typeof original[key] !== 'object') {\n", + " obj[key] = original[key];\n", + " }\n", + " return obj;\n", + " }, {});\n", + "}\n", + "\n", + "mpl.figure.prototype.mouse_event = function (event, name) {\n", + " var canvas_pos = mpl.findpos(event);\n", + "\n", + " if (name === 'button_press') {\n", + " this.canvas.focus();\n", + " this.canvas_div.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x * this.ratio;\n", + " var y = canvas_pos.y * this.ratio;\n", + "\n", + " this.send_message(name, {\n", + " x: x,\n", + " y: y,\n", + " button: event.button,\n", + " step: event.step,\n", + " guiEvent: simpleKeys(event),\n", + " });\n", + "\n", + " /* This prevents the web browser from automatically changing to\n", + " * the text insertion cursor when the button is pressed. We want\n", + " * to control all of the cursor setting manually through the\n", + " * 'cursor' event from matplotlib */\n", + " event.preventDefault();\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n", + " // Handle any extra behaviour associated with a key event\n", + "};\n", + "\n", + "mpl.figure.prototype.key_event = function (event, name) {\n", + " // Prevent repeat events\n", + " if (name === 'key_press') {\n", + " if (event.key === this._key) {\n", + " return;\n", + " } else {\n", + " this._key = event.key;\n", + " }\n", + " }\n", + " if (name === 'key_release') {\n", + " this._key = null;\n", + " }\n", + "\n", + " var value = '';\n", + " if (event.ctrlKey && event.key !== 'Control') {\n", + " value += 'ctrl+';\n", + " }\n", + " else if (event.altKey && event.key !== 'Alt') {\n", + " value += 'alt+';\n", + " }\n", + " else if (event.shiftKey && event.key !== 'Shift') {\n", + " value += 'shift+';\n", + " }\n", + "\n", + " value += 'k' + event.key;\n", + "\n", + " this._key_event_extra(event, name);\n", + "\n", + " this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n", + " if (name === 'download') {\n", + " this.handle_save(this, null);\n", + " } else {\n", + " this.send_message('toolbar_button', { name: name });\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n", + " this.message.textContent = tooltip;\n", + "};\n", + "\n", + "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", + "// prettier-ignore\n", + "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";/* global mpl */\n", + "\n", + "var comm_websocket_adapter = function (comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.binaryType = comm.kernel.ws.binaryType;\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " function updateReadyState(_event) {\n", + " if (comm.kernel.ws) {\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " } else {\n", + " ws.readyState = 3; // Closed state.\n", + " }\n", + " }\n", + " comm.kernel.ws.addEventListener('open', updateReadyState);\n", + " comm.kernel.ws.addEventListener('close', updateReadyState);\n", + " comm.kernel.ws.addEventListener('error', updateReadyState);\n", + "\n", + " ws.close = function () {\n", + " comm.close();\n", + " };\n", + " ws.send = function (m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function (msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " var data = msg['content']['data'];\n", + " if (data['blob'] !== undefined) {\n", + " data = {\n", + " data: new Blob(msg['buffers'], { type: data['blob'] }),\n", + " };\n", + " }\n", + " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", + " ws.onmessage(data);\n", + " });\n", + " return ws;\n", + "};\n", + "\n", + "mpl.mpl_figure_comm = function (comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = document.getElementById(id);\n", + " var ws_proxy = comm_websocket_adapter(comm);\n", + "\n", + " function ondownload(figure, _format) {\n", + " window.open(figure.canvas.toDataURL());\n", + " }\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element;\n", + " fig.cell_info = mpl.find_output_cell(\"
\");\n", + " if (!fig.cell_info) {\n", + " console.error('Failed to find cell for figure', id, fig);\n", + " return;\n", + " }\n", + " fig.cell_info[0].output_area.element.on(\n", + " 'cleared',\n", + " { fig: fig },\n", + " fig._remove_fig_handler\n", + " );\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function (fig, msg) {\n", + " var width = fig.canvas.width / fig.ratio;\n", + " fig.cell_info[0].output_area.element.off(\n", + " 'cleared',\n", + " fig._remove_fig_handler\n", + " );\n", + " fig.resizeObserverInstance.unobserve(fig.canvas_div);\n", + "\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable();\n", + " fig.parent_element.innerHTML =\n", + " '';\n", + " fig.close_ws(fig, msg);\n", + "};\n", + "\n", + "mpl.figure.prototype.close_ws = function (fig, msg) {\n", + " fig.send_message('closing', msg);\n", + " // fig.ws.close()\n", + "};\n", + "\n", + "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n", + " // Turn the data on the canvas into data in the output cell.\n", + " var width = this.canvas.width / this.ratio;\n", + " var dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] =\n", + " '';\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message('ack', {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () {\n", + " fig.push_to_output();\n", + " }, 1000);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'btn-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " var button;\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " continue;\n", + " }\n", + "\n", + " button = fig.buttons[name] = document.createElement('button');\n", + " button.classList = 'btn btn-default';\n", + " button.href = '#';\n", + " button.title = name;\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message pull-right';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = document.createElement('div');\n", + " buttongrp.classList = 'btn-group inline pull-right';\n", + " button = document.createElement('button');\n", + " button.classList = 'btn btn-mini btn-primary';\n", + " button.href = '#';\n", + " button.title = 'Stop Interaction';\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', function (_evt) {\n", + " fig.handle_close(fig, {});\n", + " });\n", + " button.addEventListener(\n", + " 'mouseover',\n", + " on_mouseover_closure('Stop Interaction')\n", + " );\n", + " buttongrp.appendChild(button);\n", + " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", + " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", + "};\n", + "\n", + "mpl.figure.prototype._remove_fig_handler = function (event) {\n", + " var fig = event.data.fig;\n", + " if (event.target !== this) {\n", + " // Ignore bubbled events from children.\n", + " return;\n", + " }\n", + " fig.close_ws(fig, {});\n", + "};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (el) {\n", + " el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (el) {\n", + " // this is important to make the div 'focusable\n", + " el.setAttribute('tabindex', 0);\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " } else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which === 13) {\n", + " this.canvas_div.blur();\n", + " // select the cell after this one\n", + " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", + " IPython.notebook.select(index + 1);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " fig.ondownload(fig, null);\n", + "};\n", + "\n", + "mpl.find_output_cell = function (html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i = 0; i < ncells; i++) {\n", + " var cell = cells[i];\n", + " if (cell.cell_type === 'code') {\n", + " for (var j = 0; j < cell.output_area.outputs.length; j++) {\n", + " var data = cell.output_area.outputs[j];\n", + " if (data.data) {\n", + " // IPython >= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] === html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "};\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel !== null) {\n", + " IPython.notebook.kernel.comm_manager.register_target(\n", + " 'matplotlib',\n", + " mpl.mpl_figure_comm\n", + " );\n", + "}\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " mean std num\n", + "1x0 irods_on_isilon 58.561781 28.682517 19\n", + " irods_on_netapp 39.473920 1.297394 10\n", + " irods_on_s3 38.528168 4.646165 19\n", + "1x1K irods_on_isilon 100.219069 1.270263 19\n", + " irods_on_netapp 100.081593 1.281383 10\n", + " irods_on_s3 61.579468 5.641403 19\n", + "1x1M irods_on_isilon 100.629799 1.505577 17\n", + " irods_on_netapp 100.311454 1.300512 9\n", + " irods_on_s3 62.325737 5.009204 18\n", + "1x1G irods_on_isilon 130.385827 7.590377 18\n", + " irods_on_netapp 123.341776 3.075267 9\n", + " irods_on_s3 131.362494 8.960215 18\n", + "10x1G irods_on_isilon 898.671291 14.834686 9\n", + " irods_on_netapp 900.939860 12.974536 9\n", + " irods_on_s3 895.431666 14.051611 8\n" + ] + }, + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "/* global mpl */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function () {\n", + " if (typeof WebSocket !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof MozWebSocket !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert(\n", + " 'Your browser does not have WebSocket support. ' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.'\n", + " );\n", + " }\n", + "};\n", + "\n", + "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = this.ws.binaryType !== undefined;\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById('mpl-warnings');\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent =\n", + " 'This browser does not support binary websocket messages. ' +\n", + " 'Performance may be slow.';\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = document.createElement('div');\n", + " this.root.setAttribute('style', 'display: inline-block');\n", + " this._root_extra_style(this.root);\n", + "\n", + " parent_element.appendChild(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message('supports_binary', { value: fig.supports_binary });\n", + " fig.send_message('send_image_mode', {});\n", + " if (fig.ratio !== 1) {\n", + " fig.send_message('set_device_pixel_ratio', {\n", + " device_pixel_ratio: fig.ratio,\n", + " });\n", + " }\n", + " fig.send_message('refresh', {});\n", + " };\n", + "\n", + " this.imageObj.onload = function () {\n", + " if (fig.image_mode === 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function () {\n", + " fig.ws.close();\n", + " };\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "};\n", + "\n", + "mpl.figure.prototype._init_header = function () {\n", + " var titlebar = document.createElement('div');\n", + " titlebar.classList =\n", + " 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n", + " var titletext = document.createElement('div');\n", + " titletext.classList = 'ui-dialog-title';\n", + " titletext.setAttribute(\n", + " 'style',\n", + " 'width: 100%; text-align: center; padding: 3px;'\n", + " );\n", + " titlebar.appendChild(titletext);\n", + " this.root.appendChild(titlebar);\n", + " this.header = titletext;\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._init_canvas = function () {\n", + " var fig = this;\n", + "\n", + " var canvas_div = (this.canvas_div = document.createElement('div'));\n", + " canvas_div.setAttribute(\n", + " 'style',\n", + " 'border: 1px solid #ddd;' +\n", + " 'box-sizing: content-box;' +\n", + " 'clear: both;' +\n", + " 'min-height: 1px;' +\n", + " 'min-width: 1px;' +\n", + " 'outline: 0;' +\n", + " 'overflow: hidden;' +\n", + " 'position: relative;' +\n", + " 'resize: both;'\n", + " );\n", + "\n", + " function on_keyboard_event_closure(name) {\n", + " return function (event) {\n", + " return fig.key_event(event, name);\n", + " };\n", + " }\n", + "\n", + " canvas_div.addEventListener(\n", + " 'keydown',\n", + " on_keyboard_event_closure('key_press')\n", + " );\n", + " canvas_div.addEventListener(\n", + " 'keyup',\n", + " on_keyboard_event_closure('key_release')\n", + " );\n", + "\n", + " this._canvas_extra_style(canvas_div);\n", + " this.root.appendChild(canvas_div);\n", + "\n", + " var canvas = (this.canvas = document.createElement('canvas'));\n", + " canvas.classList.add('mpl-canvas');\n", + " canvas.setAttribute('style', 'box-sizing: content-box;');\n", + "\n", + " this.context = canvas.getContext('2d');\n", + "\n", + " var backingStore =\n", + " this.context.backingStorePixelRatio ||\n", + " this.context.webkitBackingStorePixelRatio ||\n", + " this.context.mozBackingStorePixelRatio ||\n", + " this.context.msBackingStorePixelRatio ||\n", + " this.context.oBackingStorePixelRatio ||\n", + " this.context.backingStorePixelRatio ||\n", + " 1;\n", + "\n", + " this.ratio = (window.devicePixelRatio || 1) / backingStore;\n", + "\n", + " var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n", + " 'canvas'\n", + " ));\n", + " rubberband_canvas.setAttribute(\n", + " 'style',\n", + " 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n", + " );\n", + "\n", + " // Apply a ponyfill if ResizeObserver is not implemented by browser.\n", + " if (this.ResizeObserver === undefined) {\n", + " if (window.ResizeObserver !== undefined) {\n", + " this.ResizeObserver = window.ResizeObserver;\n", + " } else {\n", + " var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n", + " this.ResizeObserver = obs.ResizeObserver;\n", + " }\n", + " }\n", + "\n", + " this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n", + " var nentries = entries.length;\n", + " for (var i = 0; i < nentries; i++) {\n", + " var entry = entries[i];\n", + " var width, height;\n", + " if (entry.contentBoxSize) {\n", + " if (entry.contentBoxSize instanceof Array) {\n", + " // Chrome 84 implements new version of spec.\n", + " width = entry.contentBoxSize[0].inlineSize;\n", + " height = entry.contentBoxSize[0].blockSize;\n", + " } else {\n", + " // Firefox implements old version of spec.\n", + " width = entry.contentBoxSize.inlineSize;\n", + " height = entry.contentBoxSize.blockSize;\n", + " }\n", + " } else {\n", + " // Chrome <84 implements even older version of spec.\n", + " width = entry.contentRect.width;\n", + " height = entry.contentRect.height;\n", + " }\n", + "\n", + " // Keep the size of the canvas and rubber band canvas in sync with\n", + " // the canvas container.\n", + " if (entry.devicePixelContentBoxSize) {\n", + " // Chrome 84 implements new version of spec.\n", + " canvas.setAttribute(\n", + " 'width',\n", + " entry.devicePixelContentBoxSize[0].inlineSize\n", + " );\n", + " canvas.setAttribute(\n", + " 'height',\n", + " entry.devicePixelContentBoxSize[0].blockSize\n", + " );\n", + " } else {\n", + " canvas.setAttribute('width', width * fig.ratio);\n", + " canvas.setAttribute('height', height * fig.ratio);\n", + " }\n", + " canvas.setAttribute(\n", + " 'style',\n", + " 'width: ' + width + 'px; height: ' + height + 'px;'\n", + " );\n", + "\n", + " rubberband_canvas.setAttribute('width', width);\n", + " rubberband_canvas.setAttribute('height', height);\n", + "\n", + " // And update the size in Python. We ignore the initial 0/0 size\n", + " // that occurs as the element is placed into the DOM, which should\n", + " // otherwise not happen due to the minimum size styling.\n", + " if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n", + " fig.request_resize(width, height);\n", + " }\n", + " }\n", + " });\n", + " this.resizeObserverInstance.observe(canvas_div);\n", + "\n", + " function on_mouse_event_closure(name) {\n", + " return function (event) {\n", + " return fig.mouse_event(event, name);\n", + " };\n", + " }\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mousedown',\n", + " on_mouse_event_closure('button_press')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseup',\n", + " on_mouse_event_closure('button_release')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'dblclick',\n", + " on_mouse_event_closure('dblclick')\n", + " );\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband_canvas.addEventListener(\n", + " 'mousemove',\n", + " on_mouse_event_closure('motion_notify')\n", + " );\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseenter',\n", + " on_mouse_event_closure('figure_enter')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseleave',\n", + " on_mouse_event_closure('figure_leave')\n", + " );\n", + "\n", + " canvas_div.addEventListener('wheel', function (event) {\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " on_mouse_event_closure('scroll')(event);\n", + " });\n", + "\n", + " canvas_div.appendChild(canvas);\n", + " canvas_div.appendChild(rubberband_canvas);\n", + "\n", + " this.rubberband_context = rubberband_canvas.getContext('2d');\n", + " this.rubberband_context.strokeStyle = '#000000';\n", + "\n", + " this._resize_canvas = function (width, height, forward) {\n", + " if (forward) {\n", + " canvas_div.style.width = width + 'px';\n", + " canvas_div.style.height = height + 'px';\n", + " }\n", + " };\n", + "\n", + " // Disable right mouse context menu.\n", + " this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n", + " event.preventDefault();\n", + " return false;\n", + " });\n", + "\n", + " function set_focus() {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'mpl-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " continue;\n", + " }\n", + "\n", + " var button = (fig.buttons[name] = document.createElement('button'));\n", + " button.classList = 'mpl-widget';\n", + " button.setAttribute('role', 'button');\n", + " button.setAttribute('aria-disabled', 'false');\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + "\n", + " var icon_img = document.createElement('img');\n", + " icon_img.src = '_images/' + image + '.png';\n", + " icon_img.srcset = '_images/' + image + '_large.png 2x';\n", + " icon_img.alt = tooltip;\n", + " button.appendChild(icon_img);\n", + "\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " var fmt_picker = document.createElement('select');\n", + " fmt_picker.classList = 'mpl-widget';\n", + " toolbar.appendChild(fmt_picker);\n", + " this.format_dropdown = fmt_picker;\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = document.createElement('option');\n", + " option.selected = fmt === mpl.default_extension;\n", + " option.innerHTML = fmt;\n", + " fmt_picker.appendChild(option);\n", + " }\n", + "\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "};\n", + "\n", + "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', { width: x_pixels, height: y_pixels });\n", + "};\n", + "\n", + "mpl.figure.prototype.send_message = function (type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "};\n", + "\n", + "mpl.figure.prototype.send_draw_message = function () {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " var format_dropdown = fig.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " fig.ondownload(fig, format);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_resize = function (fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1], msg['forward']);\n", + " fig.send_message('refresh', {});\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n", + " var x0 = msg['x0'] / fig.ratio;\n", + " var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n", + " var x1 = msg['x1'] / fig.ratio;\n", + " var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0,\n", + " 0,\n", + " fig.canvas.width / fig.ratio,\n", + " fig.canvas.height / fig.ratio\n", + " );\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", + " fig.rubberband_canvas.style.cursor = msg['cursor'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_message = function (fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n", + " for (var key in msg) {\n", + " if (!(key in fig.buttons)) {\n", + " continue;\n", + " }\n", + " fig.buttons[key].disabled = !msg[key];\n", + " fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n", + " if (msg['mode'] === 'PAN') {\n", + " fig.buttons['Pan'].classList.add('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " } else if (msg['mode'] === 'ZOOM') {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.add('active');\n", + " } else {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message('ack', {});\n", + "};\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function (fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " var img = evt.data;\n", + " if (img.type !== 'image/png') {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " img.type = 'image/png';\n", + " }\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src\n", + " );\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " img\n", + " );\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " } else if (\n", + " typeof evt.data === 'string' &&\n", + " evt.data.slice(0, 21) === 'data:image/png;base64'\n", + " ) {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig['handle_' + msg_type];\n", + " } catch (e) {\n", + " console.log(\n", + " \"No handler for the '\" + msg_type + \"' message type: \",\n", + " msg\n", + " );\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\n", + " \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n", + " e,\n", + " e.stack,\n", + " msg\n", + " );\n", + " }\n", + " }\n", + " };\n", + "};\n", + "\n", + "// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function (e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e) {\n", + " e = window.event;\n", + " }\n", + " if (e.target) {\n", + " targ = e.target;\n", + " } else if (e.srcElement) {\n", + " targ = e.srcElement;\n", + " }\n", + " if (targ.nodeType === 3) {\n", + " // defeat Safari bug\n", + " targ = targ.parentNode;\n", + " }\n", + "\n", + " // pageX,Y are the mouse positions relative to the document\n", + " var boundingRect = targ.getBoundingClientRect();\n", + " var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n", + " var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n", + "\n", + " return { x: x, y: y };\n", + "};\n", + "\n", + "/*\n", + " * return a copy of an object with only non-object keys\n", + " * we need this to avoid circular references\n", + " * https://stackoverflow.com/a/24161582/3208463\n", + " */\n", + "function simpleKeys(original) {\n", + " return Object.keys(original).reduce(function (obj, key) {\n", + " if (typeof original[key] !== 'object') {\n", + " obj[key] = original[key];\n", + " }\n", + " return obj;\n", + " }, {});\n", + "}\n", + "\n", + "mpl.figure.prototype.mouse_event = function (event, name) {\n", + " var canvas_pos = mpl.findpos(event);\n", + "\n", + " if (name === 'button_press') {\n", + " this.canvas.focus();\n", + " this.canvas_div.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x * this.ratio;\n", + " var y = canvas_pos.y * this.ratio;\n", + "\n", + " this.send_message(name, {\n", + " x: x,\n", + " y: y,\n", + " button: event.button,\n", + " step: event.step,\n", + " guiEvent: simpleKeys(event),\n", + " });\n", + "\n", + " /* This prevents the web browser from automatically changing to\n", + " * the text insertion cursor when the button is pressed. We want\n", + " * to control all of the cursor setting manually through the\n", + " * 'cursor' event from matplotlib */\n", + " event.preventDefault();\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n", + " // Handle any extra behaviour associated with a key event\n", + "};\n", + "\n", + "mpl.figure.prototype.key_event = function (event, name) {\n", + " // Prevent repeat events\n", + " if (name === 'key_press') {\n", + " if (event.key === this._key) {\n", + " return;\n", + " } else {\n", + " this._key = event.key;\n", + " }\n", + " }\n", + " if (name === 'key_release') {\n", + " this._key = null;\n", + " }\n", + "\n", + " var value = '';\n", + " if (event.ctrlKey && event.key !== 'Control') {\n", + " value += 'ctrl+';\n", + " }\n", + " else if (event.altKey && event.key !== 'Alt') {\n", + " value += 'alt+';\n", + " }\n", + " else if (event.shiftKey && event.key !== 'Shift') {\n", + " value += 'shift+';\n", + " }\n", + "\n", + " value += 'k' + event.key;\n", + "\n", + " this._key_event_extra(event, name);\n", + "\n", + " this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n", + " if (name === 'download') {\n", + " this.handle_save(this, null);\n", + " } else {\n", + " this.send_message('toolbar_button', { name: name });\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n", + " this.message.textContent = tooltip;\n", + "};\n", + "\n", + "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", + "// prettier-ignore\n", + "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";/* global mpl */\n", + "\n", + "var comm_websocket_adapter = function (comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.binaryType = comm.kernel.ws.binaryType;\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " function updateReadyState(_event) {\n", + " if (comm.kernel.ws) {\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " } else {\n", + " ws.readyState = 3; // Closed state.\n", + " }\n", + " }\n", + " comm.kernel.ws.addEventListener('open', updateReadyState);\n", + " comm.kernel.ws.addEventListener('close', updateReadyState);\n", + " comm.kernel.ws.addEventListener('error', updateReadyState);\n", + "\n", + " ws.close = function () {\n", + " comm.close();\n", + " };\n", + " ws.send = function (m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function (msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " var data = msg['content']['data'];\n", + " if (data['blob'] !== undefined) {\n", + " data = {\n", + " data: new Blob(msg['buffers'], { type: data['blob'] }),\n", + " };\n", + " }\n", + " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", + " ws.onmessage(data);\n", + " });\n", + " return ws;\n", + "};\n", + "\n", + "mpl.mpl_figure_comm = function (comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = document.getElementById(id);\n", + " var ws_proxy = comm_websocket_adapter(comm);\n", + "\n", + " function ondownload(figure, _format) {\n", + " window.open(figure.canvas.toDataURL());\n", + " }\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element;\n", + " fig.cell_info = mpl.find_output_cell(\"
\");\n", + " if (!fig.cell_info) {\n", + " console.error('Failed to find cell for figure', id, fig);\n", + " return;\n", + " }\n", + " fig.cell_info[0].output_area.element.on(\n", + " 'cleared',\n", + " { fig: fig },\n", + " fig._remove_fig_handler\n", + " );\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function (fig, msg) {\n", + " var width = fig.canvas.width / fig.ratio;\n", + " fig.cell_info[0].output_area.element.off(\n", + " 'cleared',\n", + " fig._remove_fig_handler\n", + " );\n", + " fig.resizeObserverInstance.unobserve(fig.canvas_div);\n", + "\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable();\n", + " fig.parent_element.innerHTML =\n", + " '';\n", + " fig.close_ws(fig, msg);\n", + "};\n", + "\n", + "mpl.figure.prototype.close_ws = function (fig, msg) {\n", + " fig.send_message('closing', msg);\n", + " // fig.ws.close()\n", + "};\n", + "\n", + "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n", + " // Turn the data on the canvas into data in the output cell.\n", + " var width = this.canvas.width / this.ratio;\n", + " var dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] =\n", + " '';\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message('ack', {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () {\n", + " fig.push_to_output();\n", + " }, 1000);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'btn-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " var button;\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " continue;\n", + " }\n", + "\n", + " button = fig.buttons[name] = document.createElement('button');\n", + " button.classList = 'btn btn-default';\n", + " button.href = '#';\n", + " button.title = name;\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message pull-right';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = document.createElement('div');\n", + " buttongrp.classList = 'btn-group inline pull-right';\n", + " button = document.createElement('button');\n", + " button.classList = 'btn btn-mini btn-primary';\n", + " button.href = '#';\n", + " button.title = 'Stop Interaction';\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', function (_evt) {\n", + " fig.handle_close(fig, {});\n", + " });\n", + " button.addEventListener(\n", + " 'mouseover',\n", + " on_mouseover_closure('Stop Interaction')\n", + " );\n", + " buttongrp.appendChild(button);\n", + " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", + " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", + "};\n", + "\n", + "mpl.figure.prototype._remove_fig_handler = function (event) {\n", + " var fig = event.data.fig;\n", + " if (event.target !== this) {\n", + " // Ignore bubbled events from children.\n", + " return;\n", + " }\n", + " fig.close_ws(fig, {});\n", + "};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (el) {\n", + " el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (el) {\n", + " // this is important to make the div 'focusable\n", + " el.setAttribute('tabindex', 0);\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " } else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which === 13) {\n", + " this.canvas_div.blur();\n", + " // select the cell after this one\n", + " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", + " IPython.notebook.select(index + 1);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " fig.ondownload(fig, null);\n", + "};\n", + "\n", + "mpl.find_output_cell = function (html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i = 0; i < ncells; i++) {\n", + " var cell = cells[i];\n", + " if (cell.cell_type === 'code') {\n", + " for (var j = 0; j < cell.output_area.outputs.length; j++) {\n", + " var data = cell.output_area.outputs[j];\n", + " if (data.data) {\n", + " // IPython >= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] === html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "};\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel !== null) {\n", + " IPython.notebook.kernel.comm_manager.register_target(\n", + " 'matplotlib',\n", + " mpl.mpl_figure_comm\n", + " );\n", + "}\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " mean std num\n", + "1x0 irods_via_davrods_on_isilon 45.970737 18.748706 18\n", + " irods_via_davrods_on_netapp 39.422446 0.920041 10\n", + " irods_via_davrods_on_s3 38.771205 4.741259 19\n", + "1x1K irods_via_davrods_on_isilon 100.057903 1.046998 19\n", + " irods_via_davrods_on_netapp 100.206922 0.858756 9\n", + " irods_via_davrods_on_s3 59.780832 5.975672 19\n", + "10x1K irods_via_davrods_on_isilon 100.749137 2.349299 18\n", + " irods_via_davrods_on_netapp 100.508246 1.126574 10\n", + " irods_via_davrods_on_s3 72.207316 5.164946 19\n", + "100x1K irods_via_davrods_on_isilon 100.584359 1.371663 20\n", + " irods_via_davrods_on_netapp 100.001887 0.612863 11\n", + " irods_via_davrods_on_s3 151.816277 6.997022 19\n", + "1000x1K irods_via_davrods_on_isilon 339.142480 9.159978 18\n", + " irods_via_davrods_on_netapp 337.333978 5.722026 10\n", + " irods_via_davrods_on_s3 890.672408 32.177313 19\n", + "2000x1K irods_via_davrods_on_isilon 617.350109 17.696956 19\n", + " irods_via_davrods_on_netapp 616.484709 14.031568 10\n", + " irods_via_davrods_on_s3 1714.515446 65.622139 19\n" + ] + }, + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "/* global mpl */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function () {\n", + " if (typeof WebSocket !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof MozWebSocket !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert(\n", + " 'Your browser does not have WebSocket support. ' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.'\n", + " );\n", + " }\n", + "};\n", + "\n", + "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = this.ws.binaryType !== undefined;\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById('mpl-warnings');\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent =\n", + " 'This browser does not support binary websocket messages. ' +\n", + " 'Performance may be slow.';\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = document.createElement('div');\n", + " this.root.setAttribute('style', 'display: inline-block');\n", + " this._root_extra_style(this.root);\n", + "\n", + " parent_element.appendChild(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message('supports_binary', { value: fig.supports_binary });\n", + " fig.send_message('send_image_mode', {});\n", + " if (fig.ratio !== 1) {\n", + " fig.send_message('set_device_pixel_ratio', {\n", + " device_pixel_ratio: fig.ratio,\n", + " });\n", + " }\n", + " fig.send_message('refresh', {});\n", + " };\n", + "\n", + " this.imageObj.onload = function () {\n", + " if (fig.image_mode === 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function () {\n", + " fig.ws.close();\n", + " };\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "};\n", + "\n", + "mpl.figure.prototype._init_header = function () {\n", + " var titlebar = document.createElement('div');\n", + " titlebar.classList =\n", + " 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n", + " var titletext = document.createElement('div');\n", + " titletext.classList = 'ui-dialog-title';\n", + " titletext.setAttribute(\n", + " 'style',\n", + " 'width: 100%; text-align: center; padding: 3px;'\n", + " );\n", + " titlebar.appendChild(titletext);\n", + " this.root.appendChild(titlebar);\n", + " this.header = titletext;\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._init_canvas = function () {\n", + " var fig = this;\n", + "\n", + " var canvas_div = (this.canvas_div = document.createElement('div'));\n", + " canvas_div.setAttribute(\n", + " 'style',\n", + " 'border: 1px solid #ddd;' +\n", + " 'box-sizing: content-box;' +\n", + " 'clear: both;' +\n", + " 'min-height: 1px;' +\n", + " 'min-width: 1px;' +\n", + " 'outline: 0;' +\n", + " 'overflow: hidden;' +\n", + " 'position: relative;' +\n", + " 'resize: both;'\n", + " );\n", + "\n", + " function on_keyboard_event_closure(name) {\n", + " return function (event) {\n", + " return fig.key_event(event, name);\n", + " };\n", + " }\n", + "\n", + " canvas_div.addEventListener(\n", + " 'keydown',\n", + " on_keyboard_event_closure('key_press')\n", + " );\n", + " canvas_div.addEventListener(\n", + " 'keyup',\n", + " on_keyboard_event_closure('key_release')\n", + " );\n", + "\n", + " this._canvas_extra_style(canvas_div);\n", + " this.root.appendChild(canvas_div);\n", + "\n", + " var canvas = (this.canvas = document.createElement('canvas'));\n", + " canvas.classList.add('mpl-canvas');\n", + " canvas.setAttribute('style', 'box-sizing: content-box;');\n", + "\n", + " this.context = canvas.getContext('2d');\n", + "\n", + " var backingStore =\n", + " this.context.backingStorePixelRatio ||\n", + " this.context.webkitBackingStorePixelRatio ||\n", + " this.context.mozBackingStorePixelRatio ||\n", + " this.context.msBackingStorePixelRatio ||\n", + " this.context.oBackingStorePixelRatio ||\n", + " this.context.backingStorePixelRatio ||\n", + " 1;\n", + "\n", + " this.ratio = (window.devicePixelRatio || 1) / backingStore;\n", + "\n", + " var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n", + " 'canvas'\n", + " ));\n", + " rubberband_canvas.setAttribute(\n", + " 'style',\n", + " 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n", + " );\n", + "\n", + " // Apply a ponyfill if ResizeObserver is not implemented by browser.\n", + " if (this.ResizeObserver === undefined) {\n", + " if (window.ResizeObserver !== undefined) {\n", + " this.ResizeObserver = window.ResizeObserver;\n", + " } else {\n", + " var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n", + " this.ResizeObserver = obs.ResizeObserver;\n", + " }\n", + " }\n", + "\n", + " this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n", + " var nentries = entries.length;\n", + " for (var i = 0; i < nentries; i++) {\n", + " var entry = entries[i];\n", + " var width, height;\n", + " if (entry.contentBoxSize) {\n", + " if (entry.contentBoxSize instanceof Array) {\n", + " // Chrome 84 implements new version of spec.\n", + " width = entry.contentBoxSize[0].inlineSize;\n", + " height = entry.contentBoxSize[0].blockSize;\n", + " } else {\n", + " // Firefox implements old version of spec.\n", + " width = entry.contentBoxSize.inlineSize;\n", + " height = entry.contentBoxSize.blockSize;\n", + " }\n", + " } else {\n", + " // Chrome <84 implements even older version of spec.\n", + " width = entry.contentRect.width;\n", + " height = entry.contentRect.height;\n", + " }\n", + "\n", + " // Keep the size of the canvas and rubber band canvas in sync with\n", + " // the canvas container.\n", + " if (entry.devicePixelContentBoxSize) {\n", + " // Chrome 84 implements new version of spec.\n", + " canvas.setAttribute(\n", + " 'width',\n", + " entry.devicePixelContentBoxSize[0].inlineSize\n", + " );\n", + " canvas.setAttribute(\n", + " 'height',\n", + " entry.devicePixelContentBoxSize[0].blockSize\n", + " );\n", + " } else {\n", + " canvas.setAttribute('width', width * fig.ratio);\n", + " canvas.setAttribute('height', height * fig.ratio);\n", + " }\n", + " canvas.setAttribute(\n", + " 'style',\n", + " 'width: ' + width + 'px; height: ' + height + 'px;'\n", + " );\n", + "\n", + " rubberband_canvas.setAttribute('width', width);\n", + " rubberband_canvas.setAttribute('height', height);\n", + "\n", + " // And update the size in Python. We ignore the initial 0/0 size\n", + " // that occurs as the element is placed into the DOM, which should\n", + " // otherwise not happen due to the minimum size styling.\n", + " if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n", + " fig.request_resize(width, height);\n", + " }\n", + " }\n", + " });\n", + " this.resizeObserverInstance.observe(canvas_div);\n", + "\n", + " function on_mouse_event_closure(name) {\n", + " return function (event) {\n", + " return fig.mouse_event(event, name);\n", + " };\n", + " }\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mousedown',\n", + " on_mouse_event_closure('button_press')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseup',\n", + " on_mouse_event_closure('button_release')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'dblclick',\n", + " on_mouse_event_closure('dblclick')\n", + " );\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband_canvas.addEventListener(\n", + " 'mousemove',\n", + " on_mouse_event_closure('motion_notify')\n", + " );\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseenter',\n", + " on_mouse_event_closure('figure_enter')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseleave',\n", + " on_mouse_event_closure('figure_leave')\n", + " );\n", + "\n", + " canvas_div.addEventListener('wheel', function (event) {\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " on_mouse_event_closure('scroll')(event);\n", + " });\n", + "\n", + " canvas_div.appendChild(canvas);\n", + " canvas_div.appendChild(rubberband_canvas);\n", + "\n", + " this.rubberband_context = rubberband_canvas.getContext('2d');\n", + " this.rubberband_context.strokeStyle = '#000000';\n", + "\n", + " this._resize_canvas = function (width, height, forward) {\n", + " if (forward) {\n", + " canvas_div.style.width = width + 'px';\n", + " canvas_div.style.height = height + 'px';\n", + " }\n", + " };\n", + "\n", + " // Disable right mouse context menu.\n", + " this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n", + " event.preventDefault();\n", + " return false;\n", + " });\n", + "\n", + " function set_focus() {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'mpl-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " continue;\n", + " }\n", + "\n", + " var button = (fig.buttons[name] = document.createElement('button'));\n", + " button.classList = 'mpl-widget';\n", + " button.setAttribute('role', 'button');\n", + " button.setAttribute('aria-disabled', 'false');\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + "\n", + " var icon_img = document.createElement('img');\n", + " icon_img.src = '_images/' + image + '.png';\n", + " icon_img.srcset = '_images/' + image + '_large.png 2x';\n", + " icon_img.alt = tooltip;\n", + " button.appendChild(icon_img);\n", + "\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " var fmt_picker = document.createElement('select');\n", + " fmt_picker.classList = 'mpl-widget';\n", + " toolbar.appendChild(fmt_picker);\n", + " this.format_dropdown = fmt_picker;\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = document.createElement('option');\n", + " option.selected = fmt === mpl.default_extension;\n", + " option.innerHTML = fmt;\n", + " fmt_picker.appendChild(option);\n", + " }\n", + "\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "};\n", + "\n", + "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', { width: x_pixels, height: y_pixels });\n", + "};\n", + "\n", + "mpl.figure.prototype.send_message = function (type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "};\n", + "\n", + "mpl.figure.prototype.send_draw_message = function () {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " var format_dropdown = fig.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " fig.ondownload(fig, format);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_resize = function (fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1], msg['forward']);\n", + " fig.send_message('refresh', {});\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n", + " var x0 = msg['x0'] / fig.ratio;\n", + " var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n", + " var x1 = msg['x1'] / fig.ratio;\n", + " var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0,\n", + " 0,\n", + " fig.canvas.width / fig.ratio,\n", + " fig.canvas.height / fig.ratio\n", + " );\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", + " fig.rubberband_canvas.style.cursor = msg['cursor'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_message = function (fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n", + " for (var key in msg) {\n", + " if (!(key in fig.buttons)) {\n", + " continue;\n", + " }\n", + " fig.buttons[key].disabled = !msg[key];\n", + " fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n", + " if (msg['mode'] === 'PAN') {\n", + " fig.buttons['Pan'].classList.add('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " } else if (msg['mode'] === 'ZOOM') {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.add('active');\n", + " } else {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message('ack', {});\n", + "};\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function (fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " var img = evt.data;\n", + " if (img.type !== 'image/png') {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " img.type = 'image/png';\n", + " }\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src\n", + " );\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " img\n", + " );\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " } else if (\n", + " typeof evt.data === 'string' &&\n", + " evt.data.slice(0, 21) === 'data:image/png;base64'\n", + " ) {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig['handle_' + msg_type];\n", + " } catch (e) {\n", + " console.log(\n", + " \"No handler for the '\" + msg_type + \"' message type: \",\n", + " msg\n", + " );\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\n", + " \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n", + " e,\n", + " e.stack,\n", + " msg\n", + " );\n", + " }\n", + " }\n", + " };\n", + "};\n", + "\n", + "// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function (e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e) {\n", + " e = window.event;\n", + " }\n", + " if (e.target) {\n", + " targ = e.target;\n", + " } else if (e.srcElement) {\n", + " targ = e.srcElement;\n", + " }\n", + " if (targ.nodeType === 3) {\n", + " // defeat Safari bug\n", + " targ = targ.parentNode;\n", + " }\n", + "\n", + " // pageX,Y are the mouse positions relative to the document\n", + " var boundingRect = targ.getBoundingClientRect();\n", + " var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n", + " var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n", + "\n", + " return { x: x, y: y };\n", + "};\n", + "\n", + "/*\n", + " * return a copy of an object with only non-object keys\n", + " * we need this to avoid circular references\n", + " * https://stackoverflow.com/a/24161582/3208463\n", + " */\n", + "function simpleKeys(original) {\n", + " return Object.keys(original).reduce(function (obj, key) {\n", + " if (typeof original[key] !== 'object') {\n", + " obj[key] = original[key];\n", + " }\n", + " return obj;\n", + " }, {});\n", + "}\n", + "\n", + "mpl.figure.prototype.mouse_event = function (event, name) {\n", + " var canvas_pos = mpl.findpos(event);\n", + "\n", + " if (name === 'button_press') {\n", + " this.canvas.focus();\n", + " this.canvas_div.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x * this.ratio;\n", + " var y = canvas_pos.y * this.ratio;\n", + "\n", + " this.send_message(name, {\n", + " x: x,\n", + " y: y,\n", + " button: event.button,\n", + " step: event.step,\n", + " guiEvent: simpleKeys(event),\n", + " });\n", + "\n", + " /* This prevents the web browser from automatically changing to\n", + " * the text insertion cursor when the button is pressed. We want\n", + " * to control all of the cursor setting manually through the\n", + " * 'cursor' event from matplotlib */\n", + " event.preventDefault();\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n", + " // Handle any extra behaviour associated with a key event\n", + "};\n", + "\n", + "mpl.figure.prototype.key_event = function (event, name) {\n", + " // Prevent repeat events\n", + " if (name === 'key_press') {\n", + " if (event.key === this._key) {\n", + " return;\n", + " } else {\n", + " this._key = event.key;\n", + " }\n", + " }\n", + " if (name === 'key_release') {\n", + " this._key = null;\n", + " }\n", + "\n", + " var value = '';\n", + " if (event.ctrlKey && event.key !== 'Control') {\n", + " value += 'ctrl+';\n", + " }\n", + " else if (event.altKey && event.key !== 'Alt') {\n", + " value += 'alt+';\n", + " }\n", + " else if (event.shiftKey && event.key !== 'Shift') {\n", + " value += 'shift+';\n", + " }\n", + "\n", + " value += 'k' + event.key;\n", + "\n", + " this._key_event_extra(event, name);\n", + "\n", + " this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n", + " if (name === 'download') {\n", + " this.handle_save(this, null);\n", + " } else {\n", + " this.send_message('toolbar_button', { name: name });\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n", + " this.message.textContent = tooltip;\n", + "};\n", + "\n", + "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", + "// prettier-ignore\n", + "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";/* global mpl */\n", + "\n", + "var comm_websocket_adapter = function (comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.binaryType = comm.kernel.ws.binaryType;\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " function updateReadyState(_event) {\n", + " if (comm.kernel.ws) {\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " } else {\n", + " ws.readyState = 3; // Closed state.\n", + " }\n", + " }\n", + " comm.kernel.ws.addEventListener('open', updateReadyState);\n", + " comm.kernel.ws.addEventListener('close', updateReadyState);\n", + " comm.kernel.ws.addEventListener('error', updateReadyState);\n", + "\n", + " ws.close = function () {\n", + " comm.close();\n", + " };\n", + " ws.send = function (m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function (msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " var data = msg['content']['data'];\n", + " if (data['blob'] !== undefined) {\n", + " data = {\n", + " data: new Blob(msg['buffers'], { type: data['blob'] }),\n", + " };\n", + " }\n", + " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", + " ws.onmessage(data);\n", + " });\n", + " return ws;\n", + "};\n", + "\n", + "mpl.mpl_figure_comm = function (comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = document.getElementById(id);\n", + " var ws_proxy = comm_websocket_adapter(comm);\n", + "\n", + " function ondownload(figure, _format) {\n", + " window.open(figure.canvas.toDataURL());\n", + " }\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element;\n", + " fig.cell_info = mpl.find_output_cell(\"
\");\n", + " if (!fig.cell_info) {\n", + " console.error('Failed to find cell for figure', id, fig);\n", + " return;\n", + " }\n", + " fig.cell_info[0].output_area.element.on(\n", + " 'cleared',\n", + " { fig: fig },\n", + " fig._remove_fig_handler\n", + " );\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function (fig, msg) {\n", + " var width = fig.canvas.width / fig.ratio;\n", + " fig.cell_info[0].output_area.element.off(\n", + " 'cleared',\n", + " fig._remove_fig_handler\n", + " );\n", + " fig.resizeObserverInstance.unobserve(fig.canvas_div);\n", + "\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable();\n", + " fig.parent_element.innerHTML =\n", + " '';\n", + " fig.close_ws(fig, msg);\n", + "};\n", + "\n", + "mpl.figure.prototype.close_ws = function (fig, msg) {\n", + " fig.send_message('closing', msg);\n", + " // fig.ws.close()\n", + "};\n", + "\n", + "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n", + " // Turn the data on the canvas into data in the output cell.\n", + " var width = this.canvas.width / this.ratio;\n", + " var dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] =\n", + " '';\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message('ack', {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () {\n", + " fig.push_to_output();\n", + " }, 1000);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'btn-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " var button;\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " continue;\n", + " }\n", + "\n", + " button = fig.buttons[name] = document.createElement('button');\n", + " button.classList = 'btn btn-default';\n", + " button.href = '#';\n", + " button.title = name;\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message pull-right';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = document.createElement('div');\n", + " buttongrp.classList = 'btn-group inline pull-right';\n", + " button = document.createElement('button');\n", + " button.classList = 'btn btn-mini btn-primary';\n", + " button.href = '#';\n", + " button.title = 'Stop Interaction';\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', function (_evt) {\n", + " fig.handle_close(fig, {});\n", + " });\n", + " button.addEventListener(\n", + " 'mouseover',\n", + " on_mouseover_closure('Stop Interaction')\n", + " );\n", + " buttongrp.appendChild(button);\n", + " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", + " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", + "};\n", + "\n", + "mpl.figure.prototype._remove_fig_handler = function (event) {\n", + " var fig = event.data.fig;\n", + " if (event.target !== this) {\n", + " // Ignore bubbled events from children.\n", + " return;\n", + " }\n", + " fig.close_ws(fig, {});\n", + "};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (el) {\n", + " el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (el) {\n", + " // this is important to make the div 'focusable\n", + " el.setAttribute('tabindex', 0);\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " } else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which === 13) {\n", + " this.canvas_div.blur();\n", + " // select the cell after this one\n", + " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", + " IPython.notebook.select(index + 1);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " fig.ondownload(fig, null);\n", + "};\n", + "\n", + "mpl.find_output_cell = function (html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i = 0; i < ncells; i++) {\n", + " var cell = cells[i];\n", + " if (cell.cell_type === 'code') {\n", + " for (var j = 0; j < cell.output_area.outputs.length; j++) {\n", + " var data = cell.output_area.outputs[j];\n", + " if (data.data) {\n", + " // IPython >= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] === html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "};\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel !== null) {\n", + " IPython.notebook.kernel.comm_manager.register_target(\n", + " 'matplotlib',\n", + " mpl.mpl_figure_comm\n", + " );\n", + "}\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " mean std num\n", + "1x0 irods_via_davrods_on_isilon 45.970737 18.748706 18\n", + " irods_via_davrods_on_netapp 39.422446 0.920041 10\n", + " irods_via_davrods_on_s3 38.771205 4.741259 19\n", + "1x1K irods_via_davrods_on_isilon 100.057903 1.046998 19\n", + " irods_via_davrods_on_netapp 100.206922 0.858756 9\n", + " irods_via_davrods_on_s3 59.780832 5.975672 19\n", + "1x1M irods_via_davrods_on_isilon 100.621282 1.388649 19\n", + " irods_via_davrods_on_netapp 100.568214 1.335967 10\n", + " irods_via_davrods_on_s3 62.377437 4.910795 18\n", + "1x1G irods_via_davrods_on_isilon 100.155441 1.003477 18\n", + " irods_via_davrods_on_netapp 100.337795 1.061627 9\n", + " irods_via_davrods_on_s3 129.621274 10.037603 16\n", + "10x1G irods_via_davrods_on_isilon 348.729399 7.039795 9\n", + " irods_via_davrods_on_netapp 352.437443 6.116535 9\n", + " irods_via_davrods_on_s3 900.107534 19.137691 9\n" + ] + }, + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "/* global mpl */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function () {\n", + " if (typeof WebSocket !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof MozWebSocket !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert(\n", + " 'Your browser does not have WebSocket support. ' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.'\n", + " );\n", + " }\n", + "};\n", + "\n", + "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = this.ws.binaryType !== undefined;\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById('mpl-warnings');\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent =\n", + " 'This browser does not support binary websocket messages. ' +\n", + " 'Performance may be slow.';\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = document.createElement('div');\n", + " this.root.setAttribute('style', 'display: inline-block');\n", + " this._root_extra_style(this.root);\n", + "\n", + " parent_element.appendChild(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message('supports_binary', { value: fig.supports_binary });\n", + " fig.send_message('send_image_mode', {});\n", + " if (fig.ratio !== 1) {\n", + " fig.send_message('set_device_pixel_ratio', {\n", + " device_pixel_ratio: fig.ratio,\n", + " });\n", + " }\n", + " fig.send_message('refresh', {});\n", + " };\n", + "\n", + " this.imageObj.onload = function () {\n", + " if (fig.image_mode === 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function () {\n", + " fig.ws.close();\n", + " };\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "};\n", + "\n", + "mpl.figure.prototype._init_header = function () {\n", + " var titlebar = document.createElement('div');\n", + " titlebar.classList =\n", + " 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n", + " var titletext = document.createElement('div');\n", + " titletext.classList = 'ui-dialog-title';\n", + " titletext.setAttribute(\n", + " 'style',\n", + " 'width: 100%; text-align: center; padding: 3px;'\n", + " );\n", + " titlebar.appendChild(titletext);\n", + " this.root.appendChild(titlebar);\n", + " this.header = titletext;\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._init_canvas = function () {\n", + " var fig = this;\n", + "\n", + " var canvas_div = (this.canvas_div = document.createElement('div'));\n", + " canvas_div.setAttribute(\n", + " 'style',\n", + " 'border: 1px solid #ddd;' +\n", + " 'box-sizing: content-box;' +\n", + " 'clear: both;' +\n", + " 'min-height: 1px;' +\n", + " 'min-width: 1px;' +\n", + " 'outline: 0;' +\n", + " 'overflow: hidden;' +\n", + " 'position: relative;' +\n", + " 'resize: both;'\n", + " );\n", + "\n", + " function on_keyboard_event_closure(name) {\n", + " return function (event) {\n", + " return fig.key_event(event, name);\n", + " };\n", + " }\n", + "\n", + " canvas_div.addEventListener(\n", + " 'keydown',\n", + " on_keyboard_event_closure('key_press')\n", + " );\n", + " canvas_div.addEventListener(\n", + " 'keyup',\n", + " on_keyboard_event_closure('key_release')\n", + " );\n", + "\n", + " this._canvas_extra_style(canvas_div);\n", + " this.root.appendChild(canvas_div);\n", + "\n", + " var canvas = (this.canvas = document.createElement('canvas'));\n", + " canvas.classList.add('mpl-canvas');\n", + " canvas.setAttribute('style', 'box-sizing: content-box;');\n", + "\n", + " this.context = canvas.getContext('2d');\n", + "\n", + " var backingStore =\n", + " this.context.backingStorePixelRatio ||\n", + " this.context.webkitBackingStorePixelRatio ||\n", + " this.context.mozBackingStorePixelRatio ||\n", + " this.context.msBackingStorePixelRatio ||\n", + " this.context.oBackingStorePixelRatio ||\n", + " this.context.backingStorePixelRatio ||\n", + " 1;\n", + "\n", + " this.ratio = (window.devicePixelRatio || 1) / backingStore;\n", + "\n", + " var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n", + " 'canvas'\n", + " ));\n", + " rubberband_canvas.setAttribute(\n", + " 'style',\n", + " 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n", + " );\n", + "\n", + " // Apply a ponyfill if ResizeObserver is not implemented by browser.\n", + " if (this.ResizeObserver === undefined) {\n", + " if (window.ResizeObserver !== undefined) {\n", + " this.ResizeObserver = window.ResizeObserver;\n", + " } else {\n", + " var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n", + " this.ResizeObserver = obs.ResizeObserver;\n", + " }\n", + " }\n", + "\n", + " this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n", + " var nentries = entries.length;\n", + " for (var i = 0; i < nentries; i++) {\n", + " var entry = entries[i];\n", + " var width, height;\n", + " if (entry.contentBoxSize) {\n", + " if (entry.contentBoxSize instanceof Array) {\n", + " // Chrome 84 implements new version of spec.\n", + " width = entry.contentBoxSize[0].inlineSize;\n", + " height = entry.contentBoxSize[0].blockSize;\n", + " } else {\n", + " // Firefox implements old version of spec.\n", + " width = entry.contentBoxSize.inlineSize;\n", + " height = entry.contentBoxSize.blockSize;\n", + " }\n", + " } else {\n", + " // Chrome <84 implements even older version of spec.\n", + " width = entry.contentRect.width;\n", + " height = entry.contentRect.height;\n", + " }\n", + "\n", + " // Keep the size of the canvas and rubber band canvas in sync with\n", + " // the canvas container.\n", + " if (entry.devicePixelContentBoxSize) {\n", + " // Chrome 84 implements new version of spec.\n", + " canvas.setAttribute(\n", + " 'width',\n", + " entry.devicePixelContentBoxSize[0].inlineSize\n", + " );\n", + " canvas.setAttribute(\n", + " 'height',\n", + " entry.devicePixelContentBoxSize[0].blockSize\n", + " );\n", + " } else {\n", + " canvas.setAttribute('width', width * fig.ratio);\n", + " canvas.setAttribute('height', height * fig.ratio);\n", + " }\n", + " canvas.setAttribute(\n", + " 'style',\n", + " 'width: ' + width + 'px; height: ' + height + 'px;'\n", + " );\n", + "\n", + " rubberband_canvas.setAttribute('width', width);\n", + " rubberband_canvas.setAttribute('height', height);\n", + "\n", + " // And update the size in Python. We ignore the initial 0/0 size\n", + " // that occurs as the element is placed into the DOM, which should\n", + " // otherwise not happen due to the minimum size styling.\n", + " if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n", + " fig.request_resize(width, height);\n", + " }\n", + " }\n", + " });\n", + " this.resizeObserverInstance.observe(canvas_div);\n", + "\n", + " function on_mouse_event_closure(name) {\n", + " return function (event) {\n", + " return fig.mouse_event(event, name);\n", + " };\n", + " }\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mousedown',\n", + " on_mouse_event_closure('button_press')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseup',\n", + " on_mouse_event_closure('button_release')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'dblclick',\n", + " on_mouse_event_closure('dblclick')\n", + " );\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband_canvas.addEventListener(\n", + " 'mousemove',\n", + " on_mouse_event_closure('motion_notify')\n", + " );\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseenter',\n", + " on_mouse_event_closure('figure_enter')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseleave',\n", + " on_mouse_event_closure('figure_leave')\n", + " );\n", + "\n", + " canvas_div.addEventListener('wheel', function (event) {\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " on_mouse_event_closure('scroll')(event);\n", + " });\n", + "\n", + " canvas_div.appendChild(canvas);\n", + " canvas_div.appendChild(rubberband_canvas);\n", + "\n", + " this.rubberband_context = rubberband_canvas.getContext('2d');\n", + " this.rubberband_context.strokeStyle = '#000000';\n", + "\n", + " this._resize_canvas = function (width, height, forward) {\n", + " if (forward) {\n", + " canvas_div.style.width = width + 'px';\n", + " canvas_div.style.height = height + 'px';\n", + " }\n", + " };\n", + "\n", + " // Disable right mouse context menu.\n", + " this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n", + " event.preventDefault();\n", + " return false;\n", + " });\n", + "\n", + " function set_focus() {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'mpl-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " continue;\n", + " }\n", + "\n", + " var button = (fig.buttons[name] = document.createElement('button'));\n", + " button.classList = 'mpl-widget';\n", + " button.setAttribute('role', 'button');\n", + " button.setAttribute('aria-disabled', 'false');\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + "\n", + " var icon_img = document.createElement('img');\n", + " icon_img.src = '_images/' + image + '.png';\n", + " icon_img.srcset = '_images/' + image + '_large.png 2x';\n", + " icon_img.alt = tooltip;\n", + " button.appendChild(icon_img);\n", + "\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " var fmt_picker = document.createElement('select');\n", + " fmt_picker.classList = 'mpl-widget';\n", + " toolbar.appendChild(fmt_picker);\n", + " this.format_dropdown = fmt_picker;\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = document.createElement('option');\n", + " option.selected = fmt === mpl.default_extension;\n", + " option.innerHTML = fmt;\n", + " fmt_picker.appendChild(option);\n", + " }\n", + "\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "};\n", + "\n", + "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', { width: x_pixels, height: y_pixels });\n", + "};\n", + "\n", + "mpl.figure.prototype.send_message = function (type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "};\n", + "\n", + "mpl.figure.prototype.send_draw_message = function () {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " var format_dropdown = fig.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " fig.ondownload(fig, format);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_resize = function (fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1], msg['forward']);\n", + " fig.send_message('refresh', {});\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n", + " var x0 = msg['x0'] / fig.ratio;\n", + " var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n", + " var x1 = msg['x1'] / fig.ratio;\n", + " var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0,\n", + " 0,\n", + " fig.canvas.width / fig.ratio,\n", + " fig.canvas.height / fig.ratio\n", + " );\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", + " fig.rubberband_canvas.style.cursor = msg['cursor'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_message = function (fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n", + " for (var key in msg) {\n", + " if (!(key in fig.buttons)) {\n", + " continue;\n", + " }\n", + " fig.buttons[key].disabled = !msg[key];\n", + " fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n", + " if (msg['mode'] === 'PAN') {\n", + " fig.buttons['Pan'].classList.add('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " } else if (msg['mode'] === 'ZOOM') {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.add('active');\n", + " } else {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message('ack', {});\n", + "};\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function (fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " var img = evt.data;\n", + " if (img.type !== 'image/png') {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " img.type = 'image/png';\n", + " }\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src\n", + " );\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " img\n", + " );\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " } else if (\n", + " typeof evt.data === 'string' &&\n", + " evt.data.slice(0, 21) === 'data:image/png;base64'\n", + " ) {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig['handle_' + msg_type];\n", + " } catch (e) {\n", + " console.log(\n", + " \"No handler for the '\" + msg_type + \"' message type: \",\n", + " msg\n", + " );\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\n", + " \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n", + " e,\n", + " e.stack,\n", + " msg\n", + " );\n", + " }\n", + " }\n", + " };\n", + "};\n", + "\n", + "// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function (e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e) {\n", + " e = window.event;\n", + " }\n", + " if (e.target) {\n", + " targ = e.target;\n", + " } else if (e.srcElement) {\n", + " targ = e.srcElement;\n", + " }\n", + " if (targ.nodeType === 3) {\n", + " // defeat Safari bug\n", + " targ = targ.parentNode;\n", + " }\n", + "\n", + " // pageX,Y are the mouse positions relative to the document\n", + " var boundingRect = targ.getBoundingClientRect();\n", + " var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n", + " var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n", + "\n", + " return { x: x, y: y };\n", + "};\n", + "\n", + "/*\n", + " * return a copy of an object with only non-object keys\n", + " * we need this to avoid circular references\n", + " * https://stackoverflow.com/a/24161582/3208463\n", + " */\n", + "function simpleKeys(original) {\n", + " return Object.keys(original).reduce(function (obj, key) {\n", + " if (typeof original[key] !== 'object') {\n", + " obj[key] = original[key];\n", + " }\n", + " return obj;\n", + " }, {});\n", + "}\n", + "\n", + "mpl.figure.prototype.mouse_event = function (event, name) {\n", + " var canvas_pos = mpl.findpos(event);\n", + "\n", + " if (name === 'button_press') {\n", + " this.canvas.focus();\n", + " this.canvas_div.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x * this.ratio;\n", + " var y = canvas_pos.y * this.ratio;\n", + "\n", + " this.send_message(name, {\n", + " x: x,\n", + " y: y,\n", + " button: event.button,\n", + " step: event.step,\n", + " guiEvent: simpleKeys(event),\n", + " });\n", + "\n", + " /* This prevents the web browser from automatically changing to\n", + " * the text insertion cursor when the button is pressed. We want\n", + " * to control all of the cursor setting manually through the\n", + " * 'cursor' event from matplotlib */\n", + " event.preventDefault();\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n", + " // Handle any extra behaviour associated with a key event\n", + "};\n", + "\n", + "mpl.figure.prototype.key_event = function (event, name) {\n", + " // Prevent repeat events\n", + " if (name === 'key_press') {\n", + " if (event.key === this._key) {\n", + " return;\n", + " } else {\n", + " this._key = event.key;\n", + " }\n", + " }\n", + " if (name === 'key_release') {\n", + " this._key = null;\n", + " }\n", + "\n", + " var value = '';\n", + " if (event.ctrlKey && event.key !== 'Control') {\n", + " value += 'ctrl+';\n", + " }\n", + " else if (event.altKey && event.key !== 'Alt') {\n", + " value += 'alt+';\n", + " }\n", + " else if (event.shiftKey && event.key !== 'Shift') {\n", + " value += 'shift+';\n", + " }\n", + "\n", + " value += 'k' + event.key;\n", + "\n", + " this._key_event_extra(event, name);\n", + "\n", + " this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n", + " if (name === 'download') {\n", + " this.handle_save(this, null);\n", + " } else {\n", + " this.send_message('toolbar_button', { name: name });\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n", + " this.message.textContent = tooltip;\n", + "};\n", + "\n", + "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", + "// prettier-ignore\n", + "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";/* global mpl */\n", + "\n", + "var comm_websocket_adapter = function (comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.binaryType = comm.kernel.ws.binaryType;\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " function updateReadyState(_event) {\n", + " if (comm.kernel.ws) {\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " } else {\n", + " ws.readyState = 3; // Closed state.\n", + " }\n", + " }\n", + " comm.kernel.ws.addEventListener('open', updateReadyState);\n", + " comm.kernel.ws.addEventListener('close', updateReadyState);\n", + " comm.kernel.ws.addEventListener('error', updateReadyState);\n", + "\n", + " ws.close = function () {\n", + " comm.close();\n", + " };\n", + " ws.send = function (m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function (msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " var data = msg['content']['data'];\n", + " if (data['blob'] !== undefined) {\n", + " data = {\n", + " data: new Blob(msg['buffers'], { type: data['blob'] }),\n", + " };\n", + " }\n", + " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", + " ws.onmessage(data);\n", + " });\n", + " return ws;\n", + "};\n", + "\n", + "mpl.mpl_figure_comm = function (comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = document.getElementById(id);\n", + " var ws_proxy = comm_websocket_adapter(comm);\n", + "\n", + " function ondownload(figure, _format) {\n", + " window.open(figure.canvas.toDataURL());\n", + " }\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element;\n", + " fig.cell_info = mpl.find_output_cell(\"
\");\n", + " if (!fig.cell_info) {\n", + " console.error('Failed to find cell for figure', id, fig);\n", + " return;\n", + " }\n", + " fig.cell_info[0].output_area.element.on(\n", + " 'cleared',\n", + " { fig: fig },\n", + " fig._remove_fig_handler\n", + " );\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function (fig, msg) {\n", + " var width = fig.canvas.width / fig.ratio;\n", + " fig.cell_info[0].output_area.element.off(\n", + " 'cleared',\n", + " fig._remove_fig_handler\n", + " );\n", + " fig.resizeObserverInstance.unobserve(fig.canvas_div);\n", + "\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable();\n", + " fig.parent_element.innerHTML =\n", + " '';\n", + " fig.close_ws(fig, msg);\n", + "};\n", + "\n", + "mpl.figure.prototype.close_ws = function (fig, msg) {\n", + " fig.send_message('closing', msg);\n", + " // fig.ws.close()\n", + "};\n", + "\n", + "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n", + " // Turn the data on the canvas into data in the output cell.\n", + " var width = this.canvas.width / this.ratio;\n", + " var dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] =\n", + " '';\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message('ack', {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () {\n", + " fig.push_to_output();\n", + " }, 1000);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'btn-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " var button;\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " continue;\n", + " }\n", + "\n", + " button = fig.buttons[name] = document.createElement('button');\n", + " button.classList = 'btn btn-default';\n", + " button.href = '#';\n", + " button.title = name;\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message pull-right';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = document.createElement('div');\n", + " buttongrp.classList = 'btn-group inline pull-right';\n", + " button = document.createElement('button');\n", + " button.classList = 'btn btn-mini btn-primary';\n", + " button.href = '#';\n", + " button.title = 'Stop Interaction';\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', function (_evt) {\n", + " fig.handle_close(fig, {});\n", + " });\n", + " button.addEventListener(\n", + " 'mouseover',\n", + " on_mouseover_closure('Stop Interaction')\n", + " );\n", + " buttongrp.appendChild(button);\n", + " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", + " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", + "};\n", + "\n", + "mpl.figure.prototype._remove_fig_handler = function (event) {\n", + " var fig = event.data.fig;\n", + " if (event.target !== this) {\n", + " // Ignore bubbled events from children.\n", + " return;\n", + " }\n", + " fig.close_ws(fig, {});\n", + "};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (el) {\n", + " el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (el) {\n", + " // this is important to make the div 'focusable\n", + " el.setAttribute('tabindex', 0);\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " } else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which === 13) {\n", + " this.canvas_div.blur();\n", + " // select the cell after this one\n", + " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", + " IPython.notebook.select(index + 1);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " fig.ondownload(fig, null);\n", + "};\n", + "\n", + "mpl.find_output_cell = function (html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i = 0; i < ncells; i++) {\n", + " var cell = cells[i];\n", + " if (cell.cell_type === 'code') {\n", + " for (var j = 0; j < cell.output_area.outputs.length; j++) {\n", + " var data = cell.output_area.outputs[j];\n", + " if (data.data) {\n", + " // IPython >= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] === html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "};\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel !== null) {\n", + " IPython.notebook.kernel.comm_manager.register_target(\n", + " 'matplotlib',\n", + " mpl.mpl_figure_comm\n", + " );\n", + "}\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " mean std num\n", + "1x0 isilon 38.570352 4.155044 20\n", + " irods_on_isilon 58.561781 28.682517 19\n", + " irods_via_davrods_on_isilon 45.970737 18.748706 18\n", + "1x1K isilon 61.779090 5.862412 19\n", + " irods_on_isilon 100.219069 1.270263 19\n", + " irods_via_davrods_on_isilon 100.057903 1.046998 19\n", + "10x1K isilon 61.898669 5.417915 19\n", + " irods_on_isilon 100.312221 1.484970 19\n", + " irods_via_davrods_on_isilon 100.749137 2.349299 18\n", + "100x1K isilon 61.409810 5.781275 20\n", + " irods_on_isilon 111.333367 6.102356 19\n", + " irods_via_davrods_on_isilon 100.584359 1.371663 20\n", + "1000x1K isilon 74.438915 8.021540 19\n", + " irods_on_isilon 575.954983 29.149668 19\n", + " irods_via_davrods_on_isilon 339.142480 9.159978 18\n", + "2000x1K isilon 86.488210 10.314142 19\n", + " irods_on_isilon 1082.106492 18.186798 19\n", + " irods_via_davrods_on_isilon 617.350109 17.696956 19\n" + ] + }, + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "/* global mpl */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function () {\n", + " if (typeof WebSocket !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof MozWebSocket !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert(\n", + " 'Your browser does not have WebSocket support. ' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.'\n", + " );\n", + " }\n", + "};\n", + "\n", + "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = this.ws.binaryType !== undefined;\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById('mpl-warnings');\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent =\n", + " 'This browser does not support binary websocket messages. ' +\n", + " 'Performance may be slow.';\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = document.createElement('div');\n", + " this.root.setAttribute('style', 'display: inline-block');\n", + " this._root_extra_style(this.root);\n", + "\n", + " parent_element.appendChild(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message('supports_binary', { value: fig.supports_binary });\n", + " fig.send_message('send_image_mode', {});\n", + " if (fig.ratio !== 1) {\n", + " fig.send_message('set_device_pixel_ratio', {\n", + " device_pixel_ratio: fig.ratio,\n", + " });\n", + " }\n", + " fig.send_message('refresh', {});\n", + " };\n", + "\n", + " this.imageObj.onload = function () {\n", + " if (fig.image_mode === 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function () {\n", + " fig.ws.close();\n", + " };\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "};\n", + "\n", + "mpl.figure.prototype._init_header = function () {\n", + " var titlebar = document.createElement('div');\n", + " titlebar.classList =\n", + " 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n", + " var titletext = document.createElement('div');\n", + " titletext.classList = 'ui-dialog-title';\n", + " titletext.setAttribute(\n", + " 'style',\n", + " 'width: 100%; text-align: center; padding: 3px;'\n", + " );\n", + " titlebar.appendChild(titletext);\n", + " this.root.appendChild(titlebar);\n", + " this.header = titletext;\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._init_canvas = function () {\n", + " var fig = this;\n", + "\n", + " var canvas_div = (this.canvas_div = document.createElement('div'));\n", + " canvas_div.setAttribute(\n", + " 'style',\n", + " 'border: 1px solid #ddd;' +\n", + " 'box-sizing: content-box;' +\n", + " 'clear: both;' +\n", + " 'min-height: 1px;' +\n", + " 'min-width: 1px;' +\n", + " 'outline: 0;' +\n", + " 'overflow: hidden;' +\n", + " 'position: relative;' +\n", + " 'resize: both;'\n", + " );\n", + "\n", + " function on_keyboard_event_closure(name) {\n", + " return function (event) {\n", + " return fig.key_event(event, name);\n", + " };\n", + " }\n", + "\n", + " canvas_div.addEventListener(\n", + " 'keydown',\n", + " on_keyboard_event_closure('key_press')\n", + " );\n", + " canvas_div.addEventListener(\n", + " 'keyup',\n", + " on_keyboard_event_closure('key_release')\n", + " );\n", + "\n", + " this._canvas_extra_style(canvas_div);\n", + " this.root.appendChild(canvas_div);\n", + "\n", + " var canvas = (this.canvas = document.createElement('canvas'));\n", + " canvas.classList.add('mpl-canvas');\n", + " canvas.setAttribute('style', 'box-sizing: content-box;');\n", + "\n", + " this.context = canvas.getContext('2d');\n", + "\n", + " var backingStore =\n", + " this.context.backingStorePixelRatio ||\n", + " this.context.webkitBackingStorePixelRatio ||\n", + " this.context.mozBackingStorePixelRatio ||\n", + " this.context.msBackingStorePixelRatio ||\n", + " this.context.oBackingStorePixelRatio ||\n", + " this.context.backingStorePixelRatio ||\n", + " 1;\n", + "\n", + " this.ratio = (window.devicePixelRatio || 1) / backingStore;\n", + "\n", + " var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n", + " 'canvas'\n", + " ));\n", + " rubberband_canvas.setAttribute(\n", + " 'style',\n", + " 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n", + " );\n", + "\n", + " // Apply a ponyfill if ResizeObserver is not implemented by browser.\n", + " if (this.ResizeObserver === undefined) {\n", + " if (window.ResizeObserver !== undefined) {\n", + " this.ResizeObserver = window.ResizeObserver;\n", + " } else {\n", + " var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n", + " this.ResizeObserver = obs.ResizeObserver;\n", + " }\n", + " }\n", + "\n", + " this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n", + " var nentries = entries.length;\n", + " for (var i = 0; i < nentries; i++) {\n", + " var entry = entries[i];\n", + " var width, height;\n", + " if (entry.contentBoxSize) {\n", + " if (entry.contentBoxSize instanceof Array) {\n", + " // Chrome 84 implements new version of spec.\n", + " width = entry.contentBoxSize[0].inlineSize;\n", + " height = entry.contentBoxSize[0].blockSize;\n", + " } else {\n", + " // Firefox implements old version of spec.\n", + " width = entry.contentBoxSize.inlineSize;\n", + " height = entry.contentBoxSize.blockSize;\n", + " }\n", + " } else {\n", + " // Chrome <84 implements even older version of spec.\n", + " width = entry.contentRect.width;\n", + " height = entry.contentRect.height;\n", + " }\n", + "\n", + " // Keep the size of the canvas and rubber band canvas in sync with\n", + " // the canvas container.\n", + " if (entry.devicePixelContentBoxSize) {\n", + " // Chrome 84 implements new version of spec.\n", + " canvas.setAttribute(\n", + " 'width',\n", + " entry.devicePixelContentBoxSize[0].inlineSize\n", + " );\n", + " canvas.setAttribute(\n", + " 'height',\n", + " entry.devicePixelContentBoxSize[0].blockSize\n", + " );\n", + " } else {\n", + " canvas.setAttribute('width', width * fig.ratio);\n", + " canvas.setAttribute('height', height * fig.ratio);\n", + " }\n", + " canvas.setAttribute(\n", + " 'style',\n", + " 'width: ' + width + 'px; height: ' + height + 'px;'\n", + " );\n", + "\n", + " rubberband_canvas.setAttribute('width', width);\n", + " rubberband_canvas.setAttribute('height', height);\n", + "\n", + " // And update the size in Python. We ignore the initial 0/0 size\n", + " // that occurs as the element is placed into the DOM, which should\n", + " // otherwise not happen due to the minimum size styling.\n", + " if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n", + " fig.request_resize(width, height);\n", + " }\n", + " }\n", + " });\n", + " this.resizeObserverInstance.observe(canvas_div);\n", + "\n", + " function on_mouse_event_closure(name) {\n", + " return function (event) {\n", + " return fig.mouse_event(event, name);\n", + " };\n", + " }\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mousedown',\n", + " on_mouse_event_closure('button_press')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseup',\n", + " on_mouse_event_closure('button_release')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'dblclick',\n", + " on_mouse_event_closure('dblclick')\n", + " );\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband_canvas.addEventListener(\n", + " 'mousemove',\n", + " on_mouse_event_closure('motion_notify')\n", + " );\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseenter',\n", + " on_mouse_event_closure('figure_enter')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseleave',\n", + " on_mouse_event_closure('figure_leave')\n", + " );\n", + "\n", + " canvas_div.addEventListener('wheel', function (event) {\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " on_mouse_event_closure('scroll')(event);\n", + " });\n", + "\n", + " canvas_div.appendChild(canvas);\n", + " canvas_div.appendChild(rubberband_canvas);\n", + "\n", + " this.rubberband_context = rubberband_canvas.getContext('2d');\n", + " this.rubberband_context.strokeStyle = '#000000';\n", + "\n", + " this._resize_canvas = function (width, height, forward) {\n", + " if (forward) {\n", + " canvas_div.style.width = width + 'px';\n", + " canvas_div.style.height = height + 'px';\n", + " }\n", + " };\n", + "\n", + " // Disable right mouse context menu.\n", + " this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n", + " event.preventDefault();\n", + " return false;\n", + " });\n", + "\n", + " function set_focus() {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'mpl-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " continue;\n", + " }\n", + "\n", + " var button = (fig.buttons[name] = document.createElement('button'));\n", + " button.classList = 'mpl-widget';\n", + " button.setAttribute('role', 'button');\n", + " button.setAttribute('aria-disabled', 'false');\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + "\n", + " var icon_img = document.createElement('img');\n", + " icon_img.src = '_images/' + image + '.png';\n", + " icon_img.srcset = '_images/' + image + '_large.png 2x';\n", + " icon_img.alt = tooltip;\n", + " button.appendChild(icon_img);\n", + "\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " var fmt_picker = document.createElement('select');\n", + " fmt_picker.classList = 'mpl-widget';\n", + " toolbar.appendChild(fmt_picker);\n", + " this.format_dropdown = fmt_picker;\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = document.createElement('option');\n", + " option.selected = fmt === mpl.default_extension;\n", + " option.innerHTML = fmt;\n", + " fmt_picker.appendChild(option);\n", + " }\n", + "\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "};\n", + "\n", + "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', { width: x_pixels, height: y_pixels });\n", + "};\n", + "\n", + "mpl.figure.prototype.send_message = function (type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "};\n", + "\n", + "mpl.figure.prototype.send_draw_message = function () {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " var format_dropdown = fig.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " fig.ondownload(fig, format);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_resize = function (fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1], msg['forward']);\n", + " fig.send_message('refresh', {});\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n", + " var x0 = msg['x0'] / fig.ratio;\n", + " var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n", + " var x1 = msg['x1'] / fig.ratio;\n", + " var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0,\n", + " 0,\n", + " fig.canvas.width / fig.ratio,\n", + " fig.canvas.height / fig.ratio\n", + " );\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", + " fig.rubberband_canvas.style.cursor = msg['cursor'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_message = function (fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n", + " for (var key in msg) {\n", + " if (!(key in fig.buttons)) {\n", + " continue;\n", + " }\n", + " fig.buttons[key].disabled = !msg[key];\n", + " fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n", + " if (msg['mode'] === 'PAN') {\n", + " fig.buttons['Pan'].classList.add('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " } else if (msg['mode'] === 'ZOOM') {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.add('active');\n", + " } else {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message('ack', {});\n", + "};\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function (fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " var img = evt.data;\n", + " if (img.type !== 'image/png') {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " img.type = 'image/png';\n", + " }\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src\n", + " );\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " img\n", + " );\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " } else if (\n", + " typeof evt.data === 'string' &&\n", + " evt.data.slice(0, 21) === 'data:image/png;base64'\n", + " ) {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig['handle_' + msg_type];\n", + " } catch (e) {\n", + " console.log(\n", + " \"No handler for the '\" + msg_type + \"' message type: \",\n", + " msg\n", + " );\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\n", + " \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n", + " e,\n", + " e.stack,\n", + " msg\n", + " );\n", + " }\n", + " }\n", + " };\n", + "};\n", + "\n", + "// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function (e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e) {\n", + " e = window.event;\n", + " }\n", + " if (e.target) {\n", + " targ = e.target;\n", + " } else if (e.srcElement) {\n", + " targ = e.srcElement;\n", + " }\n", + " if (targ.nodeType === 3) {\n", + " // defeat Safari bug\n", + " targ = targ.parentNode;\n", + " }\n", + "\n", + " // pageX,Y are the mouse positions relative to the document\n", + " var boundingRect = targ.getBoundingClientRect();\n", + " var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n", + " var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n", + "\n", + " return { x: x, y: y };\n", + "};\n", + "\n", + "/*\n", + " * return a copy of an object with only non-object keys\n", + " * we need this to avoid circular references\n", + " * https://stackoverflow.com/a/24161582/3208463\n", + " */\n", + "function simpleKeys(original) {\n", + " return Object.keys(original).reduce(function (obj, key) {\n", + " if (typeof original[key] !== 'object') {\n", + " obj[key] = original[key];\n", + " }\n", + " return obj;\n", + " }, {});\n", + "}\n", + "\n", + "mpl.figure.prototype.mouse_event = function (event, name) {\n", + " var canvas_pos = mpl.findpos(event);\n", + "\n", + " if (name === 'button_press') {\n", + " this.canvas.focus();\n", + " this.canvas_div.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x * this.ratio;\n", + " var y = canvas_pos.y * this.ratio;\n", + "\n", + " this.send_message(name, {\n", + " x: x,\n", + " y: y,\n", + " button: event.button,\n", + " step: event.step,\n", + " guiEvent: simpleKeys(event),\n", + " });\n", + "\n", + " /* This prevents the web browser from automatically changing to\n", + " * the text insertion cursor when the button is pressed. We want\n", + " * to control all of the cursor setting manually through the\n", + " * 'cursor' event from matplotlib */\n", + " event.preventDefault();\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n", + " // Handle any extra behaviour associated with a key event\n", + "};\n", + "\n", + "mpl.figure.prototype.key_event = function (event, name) {\n", + " // Prevent repeat events\n", + " if (name === 'key_press') {\n", + " if (event.key === this._key) {\n", + " return;\n", + " } else {\n", + " this._key = event.key;\n", + " }\n", + " }\n", + " if (name === 'key_release') {\n", + " this._key = null;\n", + " }\n", + "\n", + " var value = '';\n", + " if (event.ctrlKey && event.key !== 'Control') {\n", + " value += 'ctrl+';\n", + " }\n", + " else if (event.altKey && event.key !== 'Alt') {\n", + " value += 'alt+';\n", + " }\n", + " else if (event.shiftKey && event.key !== 'Shift') {\n", + " value += 'shift+';\n", + " }\n", + "\n", + " value += 'k' + event.key;\n", + "\n", + " this._key_event_extra(event, name);\n", + "\n", + " this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n", + " if (name === 'download') {\n", + " this.handle_save(this, null);\n", + " } else {\n", + " this.send_message('toolbar_button', { name: name });\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n", + " this.message.textContent = tooltip;\n", + "};\n", + "\n", + "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", + "// prettier-ignore\n", + "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";/* global mpl */\n", + "\n", + "var comm_websocket_adapter = function (comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.binaryType = comm.kernel.ws.binaryType;\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " function updateReadyState(_event) {\n", + " if (comm.kernel.ws) {\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " } else {\n", + " ws.readyState = 3; // Closed state.\n", + " }\n", + " }\n", + " comm.kernel.ws.addEventListener('open', updateReadyState);\n", + " comm.kernel.ws.addEventListener('close', updateReadyState);\n", + " comm.kernel.ws.addEventListener('error', updateReadyState);\n", + "\n", + " ws.close = function () {\n", + " comm.close();\n", + " };\n", + " ws.send = function (m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function (msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " var data = msg['content']['data'];\n", + " if (data['blob'] !== undefined) {\n", + " data = {\n", + " data: new Blob(msg['buffers'], { type: data['blob'] }),\n", + " };\n", + " }\n", + " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", + " ws.onmessage(data);\n", + " });\n", + " return ws;\n", + "};\n", + "\n", + "mpl.mpl_figure_comm = function (comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = document.getElementById(id);\n", + " var ws_proxy = comm_websocket_adapter(comm);\n", + "\n", + " function ondownload(figure, _format) {\n", + " window.open(figure.canvas.toDataURL());\n", + " }\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element;\n", + " fig.cell_info = mpl.find_output_cell(\"
\");\n", + " if (!fig.cell_info) {\n", + " console.error('Failed to find cell for figure', id, fig);\n", + " return;\n", + " }\n", + " fig.cell_info[0].output_area.element.on(\n", + " 'cleared',\n", + " { fig: fig },\n", + " fig._remove_fig_handler\n", + " );\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function (fig, msg) {\n", + " var width = fig.canvas.width / fig.ratio;\n", + " fig.cell_info[0].output_area.element.off(\n", + " 'cleared',\n", + " fig._remove_fig_handler\n", + " );\n", + " fig.resizeObserverInstance.unobserve(fig.canvas_div);\n", + "\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable();\n", + " fig.parent_element.innerHTML =\n", + " '';\n", + " fig.close_ws(fig, msg);\n", + "};\n", + "\n", + "mpl.figure.prototype.close_ws = function (fig, msg) {\n", + " fig.send_message('closing', msg);\n", + " // fig.ws.close()\n", + "};\n", + "\n", + "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n", + " // Turn the data on the canvas into data in the output cell.\n", + " var width = this.canvas.width / this.ratio;\n", + " var dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] =\n", + " '';\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message('ack', {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () {\n", + " fig.push_to_output();\n", + " }, 1000);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'btn-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " var button;\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " continue;\n", + " }\n", + "\n", + " button = fig.buttons[name] = document.createElement('button');\n", + " button.classList = 'btn btn-default';\n", + " button.href = '#';\n", + " button.title = name;\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message pull-right';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = document.createElement('div');\n", + " buttongrp.classList = 'btn-group inline pull-right';\n", + " button = document.createElement('button');\n", + " button.classList = 'btn btn-mini btn-primary';\n", + " button.href = '#';\n", + " button.title = 'Stop Interaction';\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', function (_evt) {\n", + " fig.handle_close(fig, {});\n", + " });\n", + " button.addEventListener(\n", + " 'mouseover',\n", + " on_mouseover_closure('Stop Interaction')\n", + " );\n", + " buttongrp.appendChild(button);\n", + " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", + " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", + "};\n", + "\n", + "mpl.figure.prototype._remove_fig_handler = function (event) {\n", + " var fig = event.data.fig;\n", + " if (event.target !== this) {\n", + " // Ignore bubbled events from children.\n", + " return;\n", + " }\n", + " fig.close_ws(fig, {});\n", + "};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (el) {\n", + " el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (el) {\n", + " // this is important to make the div 'focusable\n", + " el.setAttribute('tabindex', 0);\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " } else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which === 13) {\n", + " this.canvas_div.blur();\n", + " // select the cell after this one\n", + " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", + " IPython.notebook.select(index + 1);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " fig.ondownload(fig, null);\n", + "};\n", + "\n", + "mpl.find_output_cell = function (html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i = 0; i < ncells; i++) {\n", + " var cell = cells[i];\n", + " if (cell.cell_type === 'code') {\n", + " for (var j = 0; j < cell.output_area.outputs.length; j++) {\n", + " var data = cell.output_area.outputs[j];\n", + " if (data.data) {\n", + " // IPython >= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] === html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "};\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel !== null) {\n", + " IPython.notebook.kernel.comm_manager.register_target(\n", + " 'matplotlib',\n", + " mpl.mpl_figure_comm\n", + " );\n", + "}\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " mean std num\n", + "1x0 isilon 38.570352 4.155044 20\n", + " irods_on_isilon 58.561781 28.682517 19\n", + " irods_via_davrods_on_isilon 45.970737 18.748706 18\n", + "1x1K isilon 61.779090 5.862412 19\n", + " irods_on_isilon 100.219069 1.270263 19\n", + " irods_via_davrods_on_isilon 100.057903 1.046998 19\n", + "1x1M isilon 61.689178 5.719017 19\n", + " irods_on_isilon 100.629799 1.505577 17\n", + " irods_via_davrods_on_isilon 100.621282 1.388649 19\n", + "1x1G isilon 65.067275 8.125209 18\n", + " irods_on_isilon 130.385827 7.590377 18\n", + " irods_via_davrods_on_isilon 100.155441 1.003477 18\n", + "10x1G isilon 259.802910 5.809441 9\n", + " irods_on_isilon 898.671291 14.834686 9\n", + " irods_via_davrods_on_isilon 348.729399 7.039795 9\n" + ] + }, + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "/* global mpl */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function () {\n", + " if (typeof WebSocket !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof MozWebSocket !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert(\n", + " 'Your browser does not have WebSocket support. ' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.'\n", + " );\n", + " }\n", + "};\n", + "\n", + "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = this.ws.binaryType !== undefined;\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById('mpl-warnings');\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent =\n", + " 'This browser does not support binary websocket messages. ' +\n", + " 'Performance may be slow.';\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = document.createElement('div');\n", + " this.root.setAttribute('style', 'display: inline-block');\n", + " this._root_extra_style(this.root);\n", + "\n", + " parent_element.appendChild(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message('supports_binary', { value: fig.supports_binary });\n", + " fig.send_message('send_image_mode', {});\n", + " if (fig.ratio !== 1) {\n", + " fig.send_message('set_device_pixel_ratio', {\n", + " device_pixel_ratio: fig.ratio,\n", + " });\n", + " }\n", + " fig.send_message('refresh', {});\n", + " };\n", + "\n", + " this.imageObj.onload = function () {\n", + " if (fig.image_mode === 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function () {\n", + " fig.ws.close();\n", + " };\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "};\n", + "\n", + "mpl.figure.prototype._init_header = function () {\n", + " var titlebar = document.createElement('div');\n", + " titlebar.classList =\n", + " 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n", + " var titletext = document.createElement('div');\n", + " titletext.classList = 'ui-dialog-title';\n", + " titletext.setAttribute(\n", + " 'style',\n", + " 'width: 100%; text-align: center; padding: 3px;'\n", + " );\n", + " titlebar.appendChild(titletext);\n", + " this.root.appendChild(titlebar);\n", + " this.header = titletext;\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._init_canvas = function () {\n", + " var fig = this;\n", + "\n", + " var canvas_div = (this.canvas_div = document.createElement('div'));\n", + " canvas_div.setAttribute(\n", + " 'style',\n", + " 'border: 1px solid #ddd;' +\n", + " 'box-sizing: content-box;' +\n", + " 'clear: both;' +\n", + " 'min-height: 1px;' +\n", + " 'min-width: 1px;' +\n", + " 'outline: 0;' +\n", + " 'overflow: hidden;' +\n", + " 'position: relative;' +\n", + " 'resize: both;'\n", + " );\n", + "\n", + " function on_keyboard_event_closure(name) {\n", + " return function (event) {\n", + " return fig.key_event(event, name);\n", + " };\n", + " }\n", + "\n", + " canvas_div.addEventListener(\n", + " 'keydown',\n", + " on_keyboard_event_closure('key_press')\n", + " );\n", + " canvas_div.addEventListener(\n", + " 'keyup',\n", + " on_keyboard_event_closure('key_release')\n", + " );\n", + "\n", + " this._canvas_extra_style(canvas_div);\n", + " this.root.appendChild(canvas_div);\n", + "\n", + " var canvas = (this.canvas = document.createElement('canvas'));\n", + " canvas.classList.add('mpl-canvas');\n", + " canvas.setAttribute('style', 'box-sizing: content-box;');\n", + "\n", + " this.context = canvas.getContext('2d');\n", + "\n", + " var backingStore =\n", + " this.context.backingStorePixelRatio ||\n", + " this.context.webkitBackingStorePixelRatio ||\n", + " this.context.mozBackingStorePixelRatio ||\n", + " this.context.msBackingStorePixelRatio ||\n", + " this.context.oBackingStorePixelRatio ||\n", + " this.context.backingStorePixelRatio ||\n", + " 1;\n", + "\n", + " this.ratio = (window.devicePixelRatio || 1) / backingStore;\n", + "\n", + " var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n", + " 'canvas'\n", + " ));\n", + " rubberband_canvas.setAttribute(\n", + " 'style',\n", + " 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n", + " );\n", + "\n", + " // Apply a ponyfill if ResizeObserver is not implemented by browser.\n", + " if (this.ResizeObserver === undefined) {\n", + " if (window.ResizeObserver !== undefined) {\n", + " this.ResizeObserver = window.ResizeObserver;\n", + " } else {\n", + " var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n", + " this.ResizeObserver = obs.ResizeObserver;\n", + " }\n", + " }\n", + "\n", + " this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n", + " var nentries = entries.length;\n", + " for (var i = 0; i < nentries; i++) {\n", + " var entry = entries[i];\n", + " var width, height;\n", + " if (entry.contentBoxSize) {\n", + " if (entry.contentBoxSize instanceof Array) {\n", + " // Chrome 84 implements new version of spec.\n", + " width = entry.contentBoxSize[0].inlineSize;\n", + " height = entry.contentBoxSize[0].blockSize;\n", + " } else {\n", + " // Firefox implements old version of spec.\n", + " width = entry.contentBoxSize.inlineSize;\n", + " height = entry.contentBoxSize.blockSize;\n", + " }\n", + " } else {\n", + " // Chrome <84 implements even older version of spec.\n", + " width = entry.contentRect.width;\n", + " height = entry.contentRect.height;\n", + " }\n", + "\n", + " // Keep the size of the canvas and rubber band canvas in sync with\n", + " // the canvas container.\n", + " if (entry.devicePixelContentBoxSize) {\n", + " // Chrome 84 implements new version of spec.\n", + " canvas.setAttribute(\n", + " 'width',\n", + " entry.devicePixelContentBoxSize[0].inlineSize\n", + " );\n", + " canvas.setAttribute(\n", + " 'height',\n", + " entry.devicePixelContentBoxSize[0].blockSize\n", + " );\n", + " } else {\n", + " canvas.setAttribute('width', width * fig.ratio);\n", + " canvas.setAttribute('height', height * fig.ratio);\n", + " }\n", + " canvas.setAttribute(\n", + " 'style',\n", + " 'width: ' + width + 'px; height: ' + height + 'px;'\n", + " );\n", + "\n", + " rubberband_canvas.setAttribute('width', width);\n", + " rubberband_canvas.setAttribute('height', height);\n", + "\n", + " // And update the size in Python. We ignore the initial 0/0 size\n", + " // that occurs as the element is placed into the DOM, which should\n", + " // otherwise not happen due to the minimum size styling.\n", + " if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n", + " fig.request_resize(width, height);\n", + " }\n", + " }\n", + " });\n", + " this.resizeObserverInstance.observe(canvas_div);\n", + "\n", + " function on_mouse_event_closure(name) {\n", + " return function (event) {\n", + " return fig.mouse_event(event, name);\n", + " };\n", + " }\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mousedown',\n", + " on_mouse_event_closure('button_press')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseup',\n", + " on_mouse_event_closure('button_release')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'dblclick',\n", + " on_mouse_event_closure('dblclick')\n", + " );\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband_canvas.addEventListener(\n", + " 'mousemove',\n", + " on_mouse_event_closure('motion_notify')\n", + " );\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseenter',\n", + " on_mouse_event_closure('figure_enter')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseleave',\n", + " on_mouse_event_closure('figure_leave')\n", + " );\n", + "\n", + " canvas_div.addEventListener('wheel', function (event) {\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " on_mouse_event_closure('scroll')(event);\n", + " });\n", + "\n", + " canvas_div.appendChild(canvas);\n", + " canvas_div.appendChild(rubberband_canvas);\n", + "\n", + " this.rubberband_context = rubberband_canvas.getContext('2d');\n", + " this.rubberband_context.strokeStyle = '#000000';\n", + "\n", + " this._resize_canvas = function (width, height, forward) {\n", + " if (forward) {\n", + " canvas_div.style.width = width + 'px';\n", + " canvas_div.style.height = height + 'px';\n", + " }\n", + " };\n", + "\n", + " // Disable right mouse context menu.\n", + " this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n", + " event.preventDefault();\n", + " return false;\n", + " });\n", + "\n", + " function set_focus() {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'mpl-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " continue;\n", + " }\n", + "\n", + " var button = (fig.buttons[name] = document.createElement('button'));\n", + " button.classList = 'mpl-widget';\n", + " button.setAttribute('role', 'button');\n", + " button.setAttribute('aria-disabled', 'false');\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + "\n", + " var icon_img = document.createElement('img');\n", + " icon_img.src = '_images/' + image + '.png';\n", + " icon_img.srcset = '_images/' + image + '_large.png 2x';\n", + " icon_img.alt = tooltip;\n", + " button.appendChild(icon_img);\n", + "\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " var fmt_picker = document.createElement('select');\n", + " fmt_picker.classList = 'mpl-widget';\n", + " toolbar.appendChild(fmt_picker);\n", + " this.format_dropdown = fmt_picker;\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = document.createElement('option');\n", + " option.selected = fmt === mpl.default_extension;\n", + " option.innerHTML = fmt;\n", + " fmt_picker.appendChild(option);\n", + " }\n", + "\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "};\n", + "\n", + "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', { width: x_pixels, height: y_pixels });\n", + "};\n", + "\n", + "mpl.figure.prototype.send_message = function (type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "};\n", + "\n", + "mpl.figure.prototype.send_draw_message = function () {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " var format_dropdown = fig.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " fig.ondownload(fig, format);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_resize = function (fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1], msg['forward']);\n", + " fig.send_message('refresh', {});\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n", + " var x0 = msg['x0'] / fig.ratio;\n", + " var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n", + " var x1 = msg['x1'] / fig.ratio;\n", + " var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0,\n", + " 0,\n", + " fig.canvas.width / fig.ratio,\n", + " fig.canvas.height / fig.ratio\n", + " );\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", + " fig.rubberband_canvas.style.cursor = msg['cursor'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_message = function (fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n", + " for (var key in msg) {\n", + " if (!(key in fig.buttons)) {\n", + " continue;\n", + " }\n", + " fig.buttons[key].disabled = !msg[key];\n", + " fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n", + " if (msg['mode'] === 'PAN') {\n", + " fig.buttons['Pan'].classList.add('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " } else if (msg['mode'] === 'ZOOM') {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.add('active');\n", + " } else {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message('ack', {});\n", + "};\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function (fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " var img = evt.data;\n", + " if (img.type !== 'image/png') {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " img.type = 'image/png';\n", + " }\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src\n", + " );\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " img\n", + " );\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " } else if (\n", + " typeof evt.data === 'string' &&\n", + " evt.data.slice(0, 21) === 'data:image/png;base64'\n", + " ) {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig['handle_' + msg_type];\n", + " } catch (e) {\n", + " console.log(\n", + " \"No handler for the '\" + msg_type + \"' message type: \",\n", + " msg\n", + " );\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\n", + " \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n", + " e,\n", + " e.stack,\n", + " msg\n", + " );\n", + " }\n", + " }\n", + " };\n", + "};\n", + "\n", + "// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function (e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e) {\n", + " e = window.event;\n", + " }\n", + " if (e.target) {\n", + " targ = e.target;\n", + " } else if (e.srcElement) {\n", + " targ = e.srcElement;\n", + " }\n", + " if (targ.nodeType === 3) {\n", + " // defeat Safari bug\n", + " targ = targ.parentNode;\n", + " }\n", + "\n", + " // pageX,Y are the mouse positions relative to the document\n", + " var boundingRect = targ.getBoundingClientRect();\n", + " var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n", + " var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n", + "\n", + " return { x: x, y: y };\n", + "};\n", + "\n", + "/*\n", + " * return a copy of an object with only non-object keys\n", + " * we need this to avoid circular references\n", + " * https://stackoverflow.com/a/24161582/3208463\n", + " */\n", + "function simpleKeys(original) {\n", + " return Object.keys(original).reduce(function (obj, key) {\n", + " if (typeof original[key] !== 'object') {\n", + " obj[key] = original[key];\n", + " }\n", + " return obj;\n", + " }, {});\n", + "}\n", + "\n", + "mpl.figure.prototype.mouse_event = function (event, name) {\n", + " var canvas_pos = mpl.findpos(event);\n", + "\n", + " if (name === 'button_press') {\n", + " this.canvas.focus();\n", + " this.canvas_div.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x * this.ratio;\n", + " var y = canvas_pos.y * this.ratio;\n", + "\n", + " this.send_message(name, {\n", + " x: x,\n", + " y: y,\n", + " button: event.button,\n", + " step: event.step,\n", + " guiEvent: simpleKeys(event),\n", + " });\n", + "\n", + " /* This prevents the web browser from automatically changing to\n", + " * the text insertion cursor when the button is pressed. We want\n", + " * to control all of the cursor setting manually through the\n", + " * 'cursor' event from matplotlib */\n", + " event.preventDefault();\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n", + " // Handle any extra behaviour associated with a key event\n", + "};\n", + "\n", + "mpl.figure.prototype.key_event = function (event, name) {\n", + " // Prevent repeat events\n", + " if (name === 'key_press') {\n", + " if (event.key === this._key) {\n", + " return;\n", + " } else {\n", + " this._key = event.key;\n", + " }\n", + " }\n", + " if (name === 'key_release') {\n", + " this._key = null;\n", + " }\n", + "\n", + " var value = '';\n", + " if (event.ctrlKey && event.key !== 'Control') {\n", + " value += 'ctrl+';\n", + " }\n", + " else if (event.altKey && event.key !== 'Alt') {\n", + " value += 'alt+';\n", + " }\n", + " else if (event.shiftKey && event.key !== 'Shift') {\n", + " value += 'shift+';\n", + " }\n", + "\n", + " value += 'k' + event.key;\n", + "\n", + " this._key_event_extra(event, name);\n", + "\n", + " this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n", + " if (name === 'download') {\n", + " this.handle_save(this, null);\n", + " } else {\n", + " this.send_message('toolbar_button', { name: name });\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n", + " this.message.textContent = tooltip;\n", + "};\n", + "\n", + "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", + "// prettier-ignore\n", + "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";/* global mpl */\n", + "\n", + "var comm_websocket_adapter = function (comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.binaryType = comm.kernel.ws.binaryType;\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " function updateReadyState(_event) {\n", + " if (comm.kernel.ws) {\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " } else {\n", + " ws.readyState = 3; // Closed state.\n", + " }\n", + " }\n", + " comm.kernel.ws.addEventListener('open', updateReadyState);\n", + " comm.kernel.ws.addEventListener('close', updateReadyState);\n", + " comm.kernel.ws.addEventListener('error', updateReadyState);\n", + "\n", + " ws.close = function () {\n", + " comm.close();\n", + " };\n", + " ws.send = function (m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function (msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " var data = msg['content']['data'];\n", + " if (data['blob'] !== undefined) {\n", + " data = {\n", + " data: new Blob(msg['buffers'], { type: data['blob'] }),\n", + " };\n", + " }\n", + " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", + " ws.onmessage(data);\n", + " });\n", + " return ws;\n", + "};\n", + "\n", + "mpl.mpl_figure_comm = function (comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = document.getElementById(id);\n", + " var ws_proxy = comm_websocket_adapter(comm);\n", + "\n", + " function ondownload(figure, _format) {\n", + " window.open(figure.canvas.toDataURL());\n", + " }\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element;\n", + " fig.cell_info = mpl.find_output_cell(\"
\");\n", + " if (!fig.cell_info) {\n", + " console.error('Failed to find cell for figure', id, fig);\n", + " return;\n", + " }\n", + " fig.cell_info[0].output_area.element.on(\n", + " 'cleared',\n", + " { fig: fig },\n", + " fig._remove_fig_handler\n", + " );\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function (fig, msg) {\n", + " var width = fig.canvas.width / fig.ratio;\n", + " fig.cell_info[0].output_area.element.off(\n", + " 'cleared',\n", + " fig._remove_fig_handler\n", + " );\n", + " fig.resizeObserverInstance.unobserve(fig.canvas_div);\n", + "\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable();\n", + " fig.parent_element.innerHTML =\n", + " '';\n", + " fig.close_ws(fig, msg);\n", + "};\n", + "\n", + "mpl.figure.prototype.close_ws = function (fig, msg) {\n", + " fig.send_message('closing', msg);\n", + " // fig.ws.close()\n", + "};\n", + "\n", + "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n", + " // Turn the data on the canvas into data in the output cell.\n", + " var width = this.canvas.width / this.ratio;\n", + " var dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] =\n", + " '';\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message('ack', {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () {\n", + " fig.push_to_output();\n", + " }, 1000);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'btn-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " var button;\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " continue;\n", + " }\n", + "\n", + " button = fig.buttons[name] = document.createElement('button');\n", + " button.classList = 'btn btn-default';\n", + " button.href = '#';\n", + " button.title = name;\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message pull-right';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = document.createElement('div');\n", + " buttongrp.classList = 'btn-group inline pull-right';\n", + " button = document.createElement('button');\n", + " button.classList = 'btn btn-mini btn-primary';\n", + " button.href = '#';\n", + " button.title = 'Stop Interaction';\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', function (_evt) {\n", + " fig.handle_close(fig, {});\n", + " });\n", + " button.addEventListener(\n", + " 'mouseover',\n", + " on_mouseover_closure('Stop Interaction')\n", + " );\n", + " buttongrp.appendChild(button);\n", + " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", + " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", + "};\n", + "\n", + "mpl.figure.prototype._remove_fig_handler = function (event) {\n", + " var fig = event.data.fig;\n", + " if (event.target !== this) {\n", + " // Ignore bubbled events from children.\n", + " return;\n", + " }\n", + " fig.close_ws(fig, {});\n", + "};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (el) {\n", + " el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (el) {\n", + " // this is important to make the div 'focusable\n", + " el.setAttribute('tabindex', 0);\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " } else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which === 13) {\n", + " this.canvas_div.blur();\n", + " // select the cell after this one\n", + " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", + " IPython.notebook.select(index + 1);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " fig.ondownload(fig, null);\n", + "};\n", + "\n", + "mpl.find_output_cell = function (html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i = 0; i < ncells; i++) {\n", + " var cell = cells[i];\n", + " if (cell.cell_type === 'code') {\n", + " for (var j = 0; j < cell.output_area.outputs.length; j++) {\n", + " var data = cell.output_area.outputs[j];\n", + " if (data.data) {\n", + " // IPython >= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] === html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "};\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel !== null) {\n", + " IPython.notebook.kernel.comm_manager.register_target(\n", + " 'matplotlib',\n", + " mpl.mpl_figure_comm\n", + " );\n", + "}\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " mean std num\n", + "1x0 netapp 38.019479 4.514666 20\n", + " irods_on_netapp 39.473920 1.297394 10\n", + " irods_via_davrods_on_netapp 39.422446 0.920041 10\n", + "1x1K netapp 61.750140 5.591803 19\n", + " irods_on_netapp 100.081593 1.281383 10\n", + " irods_via_davrods_on_netapp 100.206922 0.858756 9\n", + "10x1K netapp 61.687431 5.491162 19\n", + " irods_on_netapp 100.054191 1.617283 10\n", + " irods_via_davrods_on_netapp 100.508246 1.126574 10\n", + "100x1K netapp 61.627463 5.621175 20\n", + " irods_on_netapp 107.249967 4.786830 9\n", + " irods_via_davrods_on_netapp 100.001887 0.612863 11\n", + "1000x1K netapp 72.967062 6.368718 18\n", + " irods_on_netapp 575.380933 13.318009 10\n", + " irods_via_davrods_on_netapp 337.333978 5.722026 10\n", + "2000x1K netapp 85.885138 8.478362 18\n", + " irods_on_netapp 1088.393951 20.461509 10\n", + " irods_via_davrods_on_netapp 616.484709 14.031568 10\n" + ] + }, + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "/* global mpl */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function () {\n", + " if (typeof WebSocket !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof MozWebSocket !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert(\n", + " 'Your browser does not have WebSocket support. ' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.'\n", + " );\n", + " }\n", + "};\n", + "\n", + "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = this.ws.binaryType !== undefined;\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById('mpl-warnings');\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent =\n", + " 'This browser does not support binary websocket messages. ' +\n", + " 'Performance may be slow.';\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = document.createElement('div');\n", + " this.root.setAttribute('style', 'display: inline-block');\n", + " this._root_extra_style(this.root);\n", + "\n", + " parent_element.appendChild(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message('supports_binary', { value: fig.supports_binary });\n", + " fig.send_message('send_image_mode', {});\n", + " if (fig.ratio !== 1) {\n", + " fig.send_message('set_device_pixel_ratio', {\n", + " device_pixel_ratio: fig.ratio,\n", + " });\n", + " }\n", + " fig.send_message('refresh', {});\n", + " };\n", + "\n", + " this.imageObj.onload = function () {\n", + " if (fig.image_mode === 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function () {\n", + " fig.ws.close();\n", + " };\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "};\n", + "\n", + "mpl.figure.prototype._init_header = function () {\n", + " var titlebar = document.createElement('div');\n", + " titlebar.classList =\n", + " 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n", + " var titletext = document.createElement('div');\n", + " titletext.classList = 'ui-dialog-title';\n", + " titletext.setAttribute(\n", + " 'style',\n", + " 'width: 100%; text-align: center; padding: 3px;'\n", + " );\n", + " titlebar.appendChild(titletext);\n", + " this.root.appendChild(titlebar);\n", + " this.header = titletext;\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._init_canvas = function () {\n", + " var fig = this;\n", + "\n", + " var canvas_div = (this.canvas_div = document.createElement('div'));\n", + " canvas_div.setAttribute(\n", + " 'style',\n", + " 'border: 1px solid #ddd;' +\n", + " 'box-sizing: content-box;' +\n", + " 'clear: both;' +\n", + " 'min-height: 1px;' +\n", + " 'min-width: 1px;' +\n", + " 'outline: 0;' +\n", + " 'overflow: hidden;' +\n", + " 'position: relative;' +\n", + " 'resize: both;'\n", + " );\n", + "\n", + " function on_keyboard_event_closure(name) {\n", + " return function (event) {\n", + " return fig.key_event(event, name);\n", + " };\n", + " }\n", + "\n", + " canvas_div.addEventListener(\n", + " 'keydown',\n", + " on_keyboard_event_closure('key_press')\n", + " );\n", + " canvas_div.addEventListener(\n", + " 'keyup',\n", + " on_keyboard_event_closure('key_release')\n", + " );\n", + "\n", + " this._canvas_extra_style(canvas_div);\n", + " this.root.appendChild(canvas_div);\n", + "\n", + " var canvas = (this.canvas = document.createElement('canvas'));\n", + " canvas.classList.add('mpl-canvas');\n", + " canvas.setAttribute('style', 'box-sizing: content-box;');\n", + "\n", + " this.context = canvas.getContext('2d');\n", + "\n", + " var backingStore =\n", + " this.context.backingStorePixelRatio ||\n", + " this.context.webkitBackingStorePixelRatio ||\n", + " this.context.mozBackingStorePixelRatio ||\n", + " this.context.msBackingStorePixelRatio ||\n", + " this.context.oBackingStorePixelRatio ||\n", + " this.context.backingStorePixelRatio ||\n", + " 1;\n", + "\n", + " this.ratio = (window.devicePixelRatio || 1) / backingStore;\n", + "\n", + " var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n", + " 'canvas'\n", + " ));\n", + " rubberband_canvas.setAttribute(\n", + " 'style',\n", + " 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n", + " );\n", + "\n", + " // Apply a ponyfill if ResizeObserver is not implemented by browser.\n", + " if (this.ResizeObserver === undefined) {\n", + " if (window.ResizeObserver !== undefined) {\n", + " this.ResizeObserver = window.ResizeObserver;\n", + " } else {\n", + " var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n", + " this.ResizeObserver = obs.ResizeObserver;\n", + " }\n", + " }\n", + "\n", + " this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n", + " var nentries = entries.length;\n", + " for (var i = 0; i < nentries; i++) {\n", + " var entry = entries[i];\n", + " var width, height;\n", + " if (entry.contentBoxSize) {\n", + " if (entry.contentBoxSize instanceof Array) {\n", + " // Chrome 84 implements new version of spec.\n", + " width = entry.contentBoxSize[0].inlineSize;\n", + " height = entry.contentBoxSize[0].blockSize;\n", + " } else {\n", + " // Firefox implements old version of spec.\n", + " width = entry.contentBoxSize.inlineSize;\n", + " height = entry.contentBoxSize.blockSize;\n", + " }\n", + " } else {\n", + " // Chrome <84 implements even older version of spec.\n", + " width = entry.contentRect.width;\n", + " height = entry.contentRect.height;\n", + " }\n", + "\n", + " // Keep the size of the canvas and rubber band canvas in sync with\n", + " // the canvas container.\n", + " if (entry.devicePixelContentBoxSize) {\n", + " // Chrome 84 implements new version of spec.\n", + " canvas.setAttribute(\n", + " 'width',\n", + " entry.devicePixelContentBoxSize[0].inlineSize\n", + " );\n", + " canvas.setAttribute(\n", + " 'height',\n", + " entry.devicePixelContentBoxSize[0].blockSize\n", + " );\n", + " } else {\n", + " canvas.setAttribute('width', width * fig.ratio);\n", + " canvas.setAttribute('height', height * fig.ratio);\n", + " }\n", + " canvas.setAttribute(\n", + " 'style',\n", + " 'width: ' + width + 'px; height: ' + height + 'px;'\n", + " );\n", + "\n", + " rubberband_canvas.setAttribute('width', width);\n", + " rubberband_canvas.setAttribute('height', height);\n", + "\n", + " // And update the size in Python. We ignore the initial 0/0 size\n", + " // that occurs as the element is placed into the DOM, which should\n", + " // otherwise not happen due to the minimum size styling.\n", + " if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n", + " fig.request_resize(width, height);\n", + " }\n", + " }\n", + " });\n", + " this.resizeObserverInstance.observe(canvas_div);\n", + "\n", + " function on_mouse_event_closure(name) {\n", + " return function (event) {\n", + " return fig.mouse_event(event, name);\n", + " };\n", + " }\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mousedown',\n", + " on_mouse_event_closure('button_press')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseup',\n", + " on_mouse_event_closure('button_release')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'dblclick',\n", + " on_mouse_event_closure('dblclick')\n", + " );\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband_canvas.addEventListener(\n", + " 'mousemove',\n", + " on_mouse_event_closure('motion_notify')\n", + " );\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseenter',\n", + " on_mouse_event_closure('figure_enter')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseleave',\n", + " on_mouse_event_closure('figure_leave')\n", + " );\n", + "\n", + " canvas_div.addEventListener('wheel', function (event) {\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " on_mouse_event_closure('scroll')(event);\n", + " });\n", + "\n", + " canvas_div.appendChild(canvas);\n", + " canvas_div.appendChild(rubberband_canvas);\n", + "\n", + " this.rubberband_context = rubberband_canvas.getContext('2d');\n", + " this.rubberband_context.strokeStyle = '#000000';\n", + "\n", + " this._resize_canvas = function (width, height, forward) {\n", + " if (forward) {\n", + " canvas_div.style.width = width + 'px';\n", + " canvas_div.style.height = height + 'px';\n", + " }\n", + " };\n", + "\n", + " // Disable right mouse context menu.\n", + " this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n", + " event.preventDefault();\n", + " return false;\n", + " });\n", + "\n", + " function set_focus() {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'mpl-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " continue;\n", + " }\n", + "\n", + " var button = (fig.buttons[name] = document.createElement('button'));\n", + " button.classList = 'mpl-widget';\n", + " button.setAttribute('role', 'button');\n", + " button.setAttribute('aria-disabled', 'false');\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + "\n", + " var icon_img = document.createElement('img');\n", + " icon_img.src = '_images/' + image + '.png';\n", + " icon_img.srcset = '_images/' + image + '_large.png 2x';\n", + " icon_img.alt = tooltip;\n", + " button.appendChild(icon_img);\n", + "\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " var fmt_picker = document.createElement('select');\n", + " fmt_picker.classList = 'mpl-widget';\n", + " toolbar.appendChild(fmt_picker);\n", + " this.format_dropdown = fmt_picker;\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = document.createElement('option');\n", + " option.selected = fmt === mpl.default_extension;\n", + " option.innerHTML = fmt;\n", + " fmt_picker.appendChild(option);\n", + " }\n", + "\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "};\n", + "\n", + "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', { width: x_pixels, height: y_pixels });\n", + "};\n", + "\n", + "mpl.figure.prototype.send_message = function (type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "};\n", + "\n", + "mpl.figure.prototype.send_draw_message = function () {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " var format_dropdown = fig.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " fig.ondownload(fig, format);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_resize = function (fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1], msg['forward']);\n", + " fig.send_message('refresh', {});\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n", + " var x0 = msg['x0'] / fig.ratio;\n", + " var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n", + " var x1 = msg['x1'] / fig.ratio;\n", + " var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0,\n", + " 0,\n", + " fig.canvas.width / fig.ratio,\n", + " fig.canvas.height / fig.ratio\n", + " );\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", + " fig.rubberband_canvas.style.cursor = msg['cursor'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_message = function (fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n", + " for (var key in msg) {\n", + " if (!(key in fig.buttons)) {\n", + " continue;\n", + " }\n", + " fig.buttons[key].disabled = !msg[key];\n", + " fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n", + " if (msg['mode'] === 'PAN') {\n", + " fig.buttons['Pan'].classList.add('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " } else if (msg['mode'] === 'ZOOM') {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.add('active');\n", + " } else {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message('ack', {});\n", + "};\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function (fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " var img = evt.data;\n", + " if (img.type !== 'image/png') {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " img.type = 'image/png';\n", + " }\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src\n", + " );\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " img\n", + " );\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " } else if (\n", + " typeof evt.data === 'string' &&\n", + " evt.data.slice(0, 21) === 'data:image/png;base64'\n", + " ) {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig['handle_' + msg_type];\n", + " } catch (e) {\n", + " console.log(\n", + " \"No handler for the '\" + msg_type + \"' message type: \",\n", + " msg\n", + " );\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\n", + " \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n", + " e,\n", + " e.stack,\n", + " msg\n", + " );\n", + " }\n", + " }\n", + " };\n", + "};\n", + "\n", + "// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function (e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e) {\n", + " e = window.event;\n", + " }\n", + " if (e.target) {\n", + " targ = e.target;\n", + " } else if (e.srcElement) {\n", + " targ = e.srcElement;\n", + " }\n", + " if (targ.nodeType === 3) {\n", + " // defeat Safari bug\n", + " targ = targ.parentNode;\n", + " }\n", + "\n", + " // pageX,Y are the mouse positions relative to the document\n", + " var boundingRect = targ.getBoundingClientRect();\n", + " var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n", + " var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n", + "\n", + " return { x: x, y: y };\n", + "};\n", + "\n", + "/*\n", + " * return a copy of an object with only non-object keys\n", + " * we need this to avoid circular references\n", + " * https://stackoverflow.com/a/24161582/3208463\n", + " */\n", + "function simpleKeys(original) {\n", + " return Object.keys(original).reduce(function (obj, key) {\n", + " if (typeof original[key] !== 'object') {\n", + " obj[key] = original[key];\n", + " }\n", + " return obj;\n", + " }, {});\n", + "}\n", + "\n", + "mpl.figure.prototype.mouse_event = function (event, name) {\n", + " var canvas_pos = mpl.findpos(event);\n", + "\n", + " if (name === 'button_press') {\n", + " this.canvas.focus();\n", + " this.canvas_div.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x * this.ratio;\n", + " var y = canvas_pos.y * this.ratio;\n", + "\n", + " this.send_message(name, {\n", + " x: x,\n", + " y: y,\n", + " button: event.button,\n", + " step: event.step,\n", + " guiEvent: simpleKeys(event),\n", + " });\n", + "\n", + " /* This prevents the web browser from automatically changing to\n", + " * the text insertion cursor when the button is pressed. We want\n", + " * to control all of the cursor setting manually through the\n", + " * 'cursor' event from matplotlib */\n", + " event.preventDefault();\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n", + " // Handle any extra behaviour associated with a key event\n", + "};\n", + "\n", + "mpl.figure.prototype.key_event = function (event, name) {\n", + " // Prevent repeat events\n", + " if (name === 'key_press') {\n", + " if (event.key === this._key) {\n", + " return;\n", + " } else {\n", + " this._key = event.key;\n", + " }\n", + " }\n", + " if (name === 'key_release') {\n", + " this._key = null;\n", + " }\n", + "\n", + " var value = '';\n", + " if (event.ctrlKey && event.key !== 'Control') {\n", + " value += 'ctrl+';\n", + " }\n", + " else if (event.altKey && event.key !== 'Alt') {\n", + " value += 'alt+';\n", + " }\n", + " else if (event.shiftKey && event.key !== 'Shift') {\n", + " value += 'shift+';\n", + " }\n", + "\n", + " value += 'k' + event.key;\n", + "\n", + " this._key_event_extra(event, name);\n", + "\n", + " this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n", + " if (name === 'download') {\n", + " this.handle_save(this, null);\n", + " } else {\n", + " this.send_message('toolbar_button', { name: name });\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n", + " this.message.textContent = tooltip;\n", + "};\n", + "\n", + "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", + "// prettier-ignore\n", + "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";/* global mpl */\n", + "\n", + "var comm_websocket_adapter = function (comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.binaryType = comm.kernel.ws.binaryType;\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " function updateReadyState(_event) {\n", + " if (comm.kernel.ws) {\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " } else {\n", + " ws.readyState = 3; // Closed state.\n", + " }\n", + " }\n", + " comm.kernel.ws.addEventListener('open', updateReadyState);\n", + " comm.kernel.ws.addEventListener('close', updateReadyState);\n", + " comm.kernel.ws.addEventListener('error', updateReadyState);\n", + "\n", + " ws.close = function () {\n", + " comm.close();\n", + " };\n", + " ws.send = function (m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function (msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " var data = msg['content']['data'];\n", + " if (data['blob'] !== undefined) {\n", + " data = {\n", + " data: new Blob(msg['buffers'], { type: data['blob'] }),\n", + " };\n", + " }\n", + " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", + " ws.onmessage(data);\n", + " });\n", + " return ws;\n", + "};\n", + "\n", + "mpl.mpl_figure_comm = function (comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = document.getElementById(id);\n", + " var ws_proxy = comm_websocket_adapter(comm);\n", + "\n", + " function ondownload(figure, _format) {\n", + " window.open(figure.canvas.toDataURL());\n", + " }\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element;\n", + " fig.cell_info = mpl.find_output_cell(\"
\");\n", + " if (!fig.cell_info) {\n", + " console.error('Failed to find cell for figure', id, fig);\n", + " return;\n", + " }\n", + " fig.cell_info[0].output_area.element.on(\n", + " 'cleared',\n", + " { fig: fig },\n", + " fig._remove_fig_handler\n", + " );\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function (fig, msg) {\n", + " var width = fig.canvas.width / fig.ratio;\n", + " fig.cell_info[0].output_area.element.off(\n", + " 'cleared',\n", + " fig._remove_fig_handler\n", + " );\n", + " fig.resizeObserverInstance.unobserve(fig.canvas_div);\n", + "\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable();\n", + " fig.parent_element.innerHTML =\n", + " '';\n", + " fig.close_ws(fig, msg);\n", + "};\n", + "\n", + "mpl.figure.prototype.close_ws = function (fig, msg) {\n", + " fig.send_message('closing', msg);\n", + " // fig.ws.close()\n", + "};\n", + "\n", + "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n", + " // Turn the data on the canvas into data in the output cell.\n", + " var width = this.canvas.width / this.ratio;\n", + " var dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] =\n", + " '';\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message('ack', {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () {\n", + " fig.push_to_output();\n", + " }, 1000);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'btn-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " var button;\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " continue;\n", + " }\n", + "\n", + " button = fig.buttons[name] = document.createElement('button');\n", + " button.classList = 'btn btn-default';\n", + " button.href = '#';\n", + " button.title = name;\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message pull-right';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = document.createElement('div');\n", + " buttongrp.classList = 'btn-group inline pull-right';\n", + " button = document.createElement('button');\n", + " button.classList = 'btn btn-mini btn-primary';\n", + " button.href = '#';\n", + " button.title = 'Stop Interaction';\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', function (_evt) {\n", + " fig.handle_close(fig, {});\n", + " });\n", + " button.addEventListener(\n", + " 'mouseover',\n", + " on_mouseover_closure('Stop Interaction')\n", + " );\n", + " buttongrp.appendChild(button);\n", + " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", + " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", + "};\n", + "\n", + "mpl.figure.prototype._remove_fig_handler = function (event) {\n", + " var fig = event.data.fig;\n", + " if (event.target !== this) {\n", + " // Ignore bubbled events from children.\n", + " return;\n", + " }\n", + " fig.close_ws(fig, {});\n", + "};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (el) {\n", + " el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (el) {\n", + " // this is important to make the div 'focusable\n", + " el.setAttribute('tabindex', 0);\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " } else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which === 13) {\n", + " this.canvas_div.blur();\n", + " // select the cell after this one\n", + " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", + " IPython.notebook.select(index + 1);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " fig.ondownload(fig, null);\n", + "};\n", + "\n", + "mpl.find_output_cell = function (html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i = 0; i < ncells; i++) {\n", + " var cell = cells[i];\n", + " if (cell.cell_type === 'code') {\n", + " for (var j = 0; j < cell.output_area.outputs.length; j++) {\n", + " var data = cell.output_area.outputs[j];\n", + " if (data.data) {\n", + " // IPython >= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] === html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "};\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel !== null) {\n", + " IPython.notebook.kernel.comm_manager.register_target(\n", + " 'matplotlib',\n", + " mpl.mpl_figure_comm\n", + " );\n", + "}\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " mean std num\n", + "1x0 netapp 38.019479 4.514666 20\n", + " irods_on_netapp 39.473920 1.297394 10\n", + " irods_via_davrods_on_netapp 39.422446 0.920041 10\n", + "1x1K netapp 61.750140 5.591803 19\n", + " irods_on_netapp 100.081593 1.281383 10\n", + " irods_via_davrods_on_netapp 100.206922 0.858756 9\n", + "1x1M netapp 61.496473 5.694692 19\n", + " irods_on_netapp 100.311454 1.300512 9\n", + " irods_via_davrods_on_netapp 100.568214 1.335967 10\n", + "1x1G netapp 63.104740 5.135069 18\n", + " irods_on_netapp 123.341776 3.075267 9\n", + " irods_via_davrods_on_netapp 100.337795 1.061627 9\n", + "10x1G netapp 183.547638 5.057323 9\n", + " irods_on_netapp 900.939860 12.974536 9\n", + " irods_via_davrods_on_netapp 352.437443 6.116535 9\n" + ] + }, + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "/* global mpl */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function () {\n", + " if (typeof WebSocket !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof MozWebSocket !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert(\n", + " 'Your browser does not have WebSocket support. ' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.'\n", + " );\n", + " }\n", + "};\n", + "\n", + "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = this.ws.binaryType !== undefined;\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById('mpl-warnings');\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent =\n", + " 'This browser does not support binary websocket messages. ' +\n", + " 'Performance may be slow.';\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = document.createElement('div');\n", + " this.root.setAttribute('style', 'display: inline-block');\n", + " this._root_extra_style(this.root);\n", + "\n", + " parent_element.appendChild(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message('supports_binary', { value: fig.supports_binary });\n", + " fig.send_message('send_image_mode', {});\n", + " if (fig.ratio !== 1) {\n", + " fig.send_message('set_device_pixel_ratio', {\n", + " device_pixel_ratio: fig.ratio,\n", + " });\n", + " }\n", + " fig.send_message('refresh', {});\n", + " };\n", + "\n", + " this.imageObj.onload = function () {\n", + " if (fig.image_mode === 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function () {\n", + " fig.ws.close();\n", + " };\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "};\n", + "\n", + "mpl.figure.prototype._init_header = function () {\n", + " var titlebar = document.createElement('div');\n", + " titlebar.classList =\n", + " 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n", + " var titletext = document.createElement('div');\n", + " titletext.classList = 'ui-dialog-title';\n", + " titletext.setAttribute(\n", + " 'style',\n", + " 'width: 100%; text-align: center; padding: 3px;'\n", + " );\n", + " titlebar.appendChild(titletext);\n", + " this.root.appendChild(titlebar);\n", + " this.header = titletext;\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._init_canvas = function () {\n", + " var fig = this;\n", + "\n", + " var canvas_div = (this.canvas_div = document.createElement('div'));\n", + " canvas_div.setAttribute(\n", + " 'style',\n", + " 'border: 1px solid #ddd;' +\n", + " 'box-sizing: content-box;' +\n", + " 'clear: both;' +\n", + " 'min-height: 1px;' +\n", + " 'min-width: 1px;' +\n", + " 'outline: 0;' +\n", + " 'overflow: hidden;' +\n", + " 'position: relative;' +\n", + " 'resize: both;'\n", + " );\n", + "\n", + " function on_keyboard_event_closure(name) {\n", + " return function (event) {\n", + " return fig.key_event(event, name);\n", + " };\n", + " }\n", + "\n", + " canvas_div.addEventListener(\n", + " 'keydown',\n", + " on_keyboard_event_closure('key_press')\n", + " );\n", + " canvas_div.addEventListener(\n", + " 'keyup',\n", + " on_keyboard_event_closure('key_release')\n", + " );\n", + "\n", + " this._canvas_extra_style(canvas_div);\n", + " this.root.appendChild(canvas_div);\n", + "\n", + " var canvas = (this.canvas = document.createElement('canvas'));\n", + " canvas.classList.add('mpl-canvas');\n", + " canvas.setAttribute('style', 'box-sizing: content-box;');\n", + "\n", + " this.context = canvas.getContext('2d');\n", + "\n", + " var backingStore =\n", + " this.context.backingStorePixelRatio ||\n", + " this.context.webkitBackingStorePixelRatio ||\n", + " this.context.mozBackingStorePixelRatio ||\n", + " this.context.msBackingStorePixelRatio ||\n", + " this.context.oBackingStorePixelRatio ||\n", + " this.context.backingStorePixelRatio ||\n", + " 1;\n", + "\n", + " this.ratio = (window.devicePixelRatio || 1) / backingStore;\n", + "\n", + " var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n", + " 'canvas'\n", + " ));\n", + " rubberband_canvas.setAttribute(\n", + " 'style',\n", + " 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n", + " );\n", + "\n", + " // Apply a ponyfill if ResizeObserver is not implemented by browser.\n", + " if (this.ResizeObserver === undefined) {\n", + " if (window.ResizeObserver !== undefined) {\n", + " this.ResizeObserver = window.ResizeObserver;\n", + " } else {\n", + " var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n", + " this.ResizeObserver = obs.ResizeObserver;\n", + " }\n", + " }\n", + "\n", + " this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n", + " var nentries = entries.length;\n", + " for (var i = 0; i < nentries; i++) {\n", + " var entry = entries[i];\n", + " var width, height;\n", + " if (entry.contentBoxSize) {\n", + " if (entry.contentBoxSize instanceof Array) {\n", + " // Chrome 84 implements new version of spec.\n", + " width = entry.contentBoxSize[0].inlineSize;\n", + " height = entry.contentBoxSize[0].blockSize;\n", + " } else {\n", + " // Firefox implements old version of spec.\n", + " width = entry.contentBoxSize.inlineSize;\n", + " height = entry.contentBoxSize.blockSize;\n", + " }\n", + " } else {\n", + " // Chrome <84 implements even older version of spec.\n", + " width = entry.contentRect.width;\n", + " height = entry.contentRect.height;\n", + " }\n", + "\n", + " // Keep the size of the canvas and rubber band canvas in sync with\n", + " // the canvas container.\n", + " if (entry.devicePixelContentBoxSize) {\n", + " // Chrome 84 implements new version of spec.\n", + " canvas.setAttribute(\n", + " 'width',\n", + " entry.devicePixelContentBoxSize[0].inlineSize\n", + " );\n", + " canvas.setAttribute(\n", + " 'height',\n", + " entry.devicePixelContentBoxSize[0].blockSize\n", + " );\n", + " } else {\n", + " canvas.setAttribute('width', width * fig.ratio);\n", + " canvas.setAttribute('height', height * fig.ratio);\n", + " }\n", + " canvas.setAttribute(\n", + " 'style',\n", + " 'width: ' + width + 'px; height: ' + height + 'px;'\n", + " );\n", + "\n", + " rubberband_canvas.setAttribute('width', width);\n", + " rubberband_canvas.setAttribute('height', height);\n", + "\n", + " // And update the size in Python. We ignore the initial 0/0 size\n", + " // that occurs as the element is placed into the DOM, which should\n", + " // otherwise not happen due to the minimum size styling.\n", + " if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n", + " fig.request_resize(width, height);\n", + " }\n", + " }\n", + " });\n", + " this.resizeObserverInstance.observe(canvas_div);\n", + "\n", + " function on_mouse_event_closure(name) {\n", + " return function (event) {\n", + " return fig.mouse_event(event, name);\n", + " };\n", + " }\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mousedown',\n", + " on_mouse_event_closure('button_press')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseup',\n", + " on_mouse_event_closure('button_release')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'dblclick',\n", + " on_mouse_event_closure('dblclick')\n", + " );\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband_canvas.addEventListener(\n", + " 'mousemove',\n", + " on_mouse_event_closure('motion_notify')\n", + " );\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseenter',\n", + " on_mouse_event_closure('figure_enter')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseleave',\n", + " on_mouse_event_closure('figure_leave')\n", + " );\n", + "\n", + " canvas_div.addEventListener('wheel', function (event) {\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " on_mouse_event_closure('scroll')(event);\n", + " });\n", + "\n", + " canvas_div.appendChild(canvas);\n", + " canvas_div.appendChild(rubberband_canvas);\n", + "\n", + " this.rubberband_context = rubberband_canvas.getContext('2d');\n", + " this.rubberband_context.strokeStyle = '#000000';\n", + "\n", + " this._resize_canvas = function (width, height, forward) {\n", + " if (forward) {\n", + " canvas_div.style.width = width + 'px';\n", + " canvas_div.style.height = height + 'px';\n", + " }\n", + " };\n", + "\n", + " // Disable right mouse context menu.\n", + " this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n", + " event.preventDefault();\n", + " return false;\n", + " });\n", + "\n", + " function set_focus() {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'mpl-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " continue;\n", + " }\n", + "\n", + " var button = (fig.buttons[name] = document.createElement('button'));\n", + " button.classList = 'mpl-widget';\n", + " button.setAttribute('role', 'button');\n", + " button.setAttribute('aria-disabled', 'false');\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + "\n", + " var icon_img = document.createElement('img');\n", + " icon_img.src = '_images/' + image + '.png';\n", + " icon_img.srcset = '_images/' + image + '_large.png 2x';\n", + " icon_img.alt = tooltip;\n", + " button.appendChild(icon_img);\n", + "\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " var fmt_picker = document.createElement('select');\n", + " fmt_picker.classList = 'mpl-widget';\n", + " toolbar.appendChild(fmt_picker);\n", + " this.format_dropdown = fmt_picker;\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = document.createElement('option');\n", + " option.selected = fmt === mpl.default_extension;\n", + " option.innerHTML = fmt;\n", + " fmt_picker.appendChild(option);\n", + " }\n", + "\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "};\n", + "\n", + "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', { width: x_pixels, height: y_pixels });\n", + "};\n", + "\n", + "mpl.figure.prototype.send_message = function (type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "};\n", + "\n", + "mpl.figure.prototype.send_draw_message = function () {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " var format_dropdown = fig.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " fig.ondownload(fig, format);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_resize = function (fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1], msg['forward']);\n", + " fig.send_message('refresh', {});\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n", + " var x0 = msg['x0'] / fig.ratio;\n", + " var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n", + " var x1 = msg['x1'] / fig.ratio;\n", + " var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0,\n", + " 0,\n", + " fig.canvas.width / fig.ratio,\n", + " fig.canvas.height / fig.ratio\n", + " );\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", + " fig.rubberband_canvas.style.cursor = msg['cursor'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_message = function (fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n", + " for (var key in msg) {\n", + " if (!(key in fig.buttons)) {\n", + " continue;\n", + " }\n", + " fig.buttons[key].disabled = !msg[key];\n", + " fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n", + " if (msg['mode'] === 'PAN') {\n", + " fig.buttons['Pan'].classList.add('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " } else if (msg['mode'] === 'ZOOM') {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.add('active');\n", + " } else {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message('ack', {});\n", + "};\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function (fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " var img = evt.data;\n", + " if (img.type !== 'image/png') {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " img.type = 'image/png';\n", + " }\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src\n", + " );\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " img\n", + " );\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " } else if (\n", + " typeof evt.data === 'string' &&\n", + " evt.data.slice(0, 21) === 'data:image/png;base64'\n", + " ) {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig['handle_' + msg_type];\n", + " } catch (e) {\n", + " console.log(\n", + " \"No handler for the '\" + msg_type + \"' message type: \",\n", + " msg\n", + " );\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\n", + " \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n", + " e,\n", + " e.stack,\n", + " msg\n", + " );\n", + " }\n", + " }\n", + " };\n", + "};\n", + "\n", + "// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function (e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e) {\n", + " e = window.event;\n", + " }\n", + " if (e.target) {\n", + " targ = e.target;\n", + " } else if (e.srcElement) {\n", + " targ = e.srcElement;\n", + " }\n", + " if (targ.nodeType === 3) {\n", + " // defeat Safari bug\n", + " targ = targ.parentNode;\n", + " }\n", + "\n", + " // pageX,Y are the mouse positions relative to the document\n", + " var boundingRect = targ.getBoundingClientRect();\n", + " var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n", + " var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n", + "\n", + " return { x: x, y: y };\n", + "};\n", + "\n", + "/*\n", + " * return a copy of an object with only non-object keys\n", + " * we need this to avoid circular references\n", + " * https://stackoverflow.com/a/24161582/3208463\n", + " */\n", + "function simpleKeys(original) {\n", + " return Object.keys(original).reduce(function (obj, key) {\n", + " if (typeof original[key] !== 'object') {\n", + " obj[key] = original[key];\n", + " }\n", + " return obj;\n", + " }, {});\n", + "}\n", + "\n", + "mpl.figure.prototype.mouse_event = function (event, name) {\n", + " var canvas_pos = mpl.findpos(event);\n", + "\n", + " if (name === 'button_press') {\n", + " this.canvas.focus();\n", + " this.canvas_div.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x * this.ratio;\n", + " var y = canvas_pos.y * this.ratio;\n", + "\n", + " this.send_message(name, {\n", + " x: x,\n", + " y: y,\n", + " button: event.button,\n", + " step: event.step,\n", + " guiEvent: simpleKeys(event),\n", + " });\n", + "\n", + " /* This prevents the web browser from automatically changing to\n", + " * the text insertion cursor when the button is pressed. We want\n", + " * to control all of the cursor setting manually through the\n", + " * 'cursor' event from matplotlib */\n", + " event.preventDefault();\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n", + " // Handle any extra behaviour associated with a key event\n", + "};\n", + "\n", + "mpl.figure.prototype.key_event = function (event, name) {\n", + " // Prevent repeat events\n", + " if (name === 'key_press') {\n", + " if (event.key === this._key) {\n", + " return;\n", + " } else {\n", + " this._key = event.key;\n", + " }\n", + " }\n", + " if (name === 'key_release') {\n", + " this._key = null;\n", + " }\n", + "\n", + " var value = '';\n", + " if (event.ctrlKey && event.key !== 'Control') {\n", + " value += 'ctrl+';\n", + " }\n", + " else if (event.altKey && event.key !== 'Alt') {\n", + " value += 'alt+';\n", + " }\n", + " else if (event.shiftKey && event.key !== 'Shift') {\n", + " value += 'shift+';\n", + " }\n", + "\n", + " value += 'k' + event.key;\n", + "\n", + " this._key_event_extra(event, name);\n", + "\n", + " this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n", + " if (name === 'download') {\n", + " this.handle_save(this, null);\n", + " } else {\n", + " this.send_message('toolbar_button', { name: name });\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n", + " this.message.textContent = tooltip;\n", + "};\n", + "\n", + "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", + "// prettier-ignore\n", + "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";/* global mpl */\n", + "\n", + "var comm_websocket_adapter = function (comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.binaryType = comm.kernel.ws.binaryType;\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " function updateReadyState(_event) {\n", + " if (comm.kernel.ws) {\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " } else {\n", + " ws.readyState = 3; // Closed state.\n", + " }\n", + " }\n", + " comm.kernel.ws.addEventListener('open', updateReadyState);\n", + " comm.kernel.ws.addEventListener('close', updateReadyState);\n", + " comm.kernel.ws.addEventListener('error', updateReadyState);\n", + "\n", + " ws.close = function () {\n", + " comm.close();\n", + " };\n", + " ws.send = function (m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function (msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " var data = msg['content']['data'];\n", + " if (data['blob'] !== undefined) {\n", + " data = {\n", + " data: new Blob(msg['buffers'], { type: data['blob'] }),\n", + " };\n", + " }\n", + " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", + " ws.onmessage(data);\n", + " });\n", + " return ws;\n", + "};\n", + "\n", + "mpl.mpl_figure_comm = function (comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = document.getElementById(id);\n", + " var ws_proxy = comm_websocket_adapter(comm);\n", + "\n", + " function ondownload(figure, _format) {\n", + " window.open(figure.canvas.toDataURL());\n", + " }\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element;\n", + " fig.cell_info = mpl.find_output_cell(\"
\");\n", + " if (!fig.cell_info) {\n", + " console.error('Failed to find cell for figure', id, fig);\n", + " return;\n", + " }\n", + " fig.cell_info[0].output_area.element.on(\n", + " 'cleared',\n", + " { fig: fig },\n", + " fig._remove_fig_handler\n", + " );\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function (fig, msg) {\n", + " var width = fig.canvas.width / fig.ratio;\n", + " fig.cell_info[0].output_area.element.off(\n", + " 'cleared',\n", + " fig._remove_fig_handler\n", + " );\n", + " fig.resizeObserverInstance.unobserve(fig.canvas_div);\n", + "\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable();\n", + " fig.parent_element.innerHTML =\n", + " '';\n", + " fig.close_ws(fig, msg);\n", + "};\n", + "\n", + "mpl.figure.prototype.close_ws = function (fig, msg) {\n", + " fig.send_message('closing', msg);\n", + " // fig.ws.close()\n", + "};\n", + "\n", + "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n", + " // Turn the data on the canvas into data in the output cell.\n", + " var width = this.canvas.width / this.ratio;\n", + " var dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] =\n", + " '';\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message('ack', {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () {\n", + " fig.push_to_output();\n", + " }, 1000);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'btn-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " var button;\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " continue;\n", + " }\n", + "\n", + " button = fig.buttons[name] = document.createElement('button');\n", + " button.classList = 'btn btn-default';\n", + " button.href = '#';\n", + " button.title = name;\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message pull-right';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = document.createElement('div');\n", + " buttongrp.classList = 'btn-group inline pull-right';\n", + " button = document.createElement('button');\n", + " button.classList = 'btn btn-mini btn-primary';\n", + " button.href = '#';\n", + " button.title = 'Stop Interaction';\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', function (_evt) {\n", + " fig.handle_close(fig, {});\n", + " });\n", + " button.addEventListener(\n", + " 'mouseover',\n", + " on_mouseover_closure('Stop Interaction')\n", + " );\n", + " buttongrp.appendChild(button);\n", + " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", + " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", + "};\n", + "\n", + "mpl.figure.prototype._remove_fig_handler = function (event) {\n", + " var fig = event.data.fig;\n", + " if (event.target !== this) {\n", + " // Ignore bubbled events from children.\n", + " return;\n", + " }\n", + " fig.close_ws(fig, {});\n", + "};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (el) {\n", + " el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (el) {\n", + " // this is important to make the div 'focusable\n", + " el.setAttribute('tabindex', 0);\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " } else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which === 13) {\n", + " this.canvas_div.blur();\n", + " // select the cell after this one\n", + " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", + " IPython.notebook.select(index + 1);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " fig.ondownload(fig, null);\n", + "};\n", + "\n", + "mpl.find_output_cell = function (html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i = 0; i < ncells; i++) {\n", + " var cell = cells[i];\n", + " if (cell.cell_type === 'code') {\n", + " for (var j = 0; j < cell.output_area.outputs.length; j++) {\n", + " var data = cell.output_area.outputs[j];\n", + " if (data.data) {\n", + " // IPython >= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] === html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "};\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel !== null) {\n", + " IPython.notebook.kernel.comm_manager.register_target(\n", + " 'matplotlib',\n", + " mpl.mpl_figure_comm\n", + " );\n", + "}\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%matplotlib notebook\n", + "\n", + "# Read in results\n", + "## Params\n", + "folder = Path(\"./results/galaxy\")\n", + "\n", + "## Plot code\n", + "res = {}\n", + "\n", + "\n", + "def _load():\n", + " # res: dict[benchmark, results]\n", + " # results: dict[target, list[total_runtime_in_s]]\n", + " for file in folder.iterdir():\n", + " date_str, _, _, benchmark, *target, _ = file.stem.split(\"_\")\n", + " target = \"_\".join(target)\n", + " file_content = json.loads(file.read_text())\n", + "\n", + " results = file_content[\"results\"]\n", + " key = f\"benchmark_galaxy_{benchmark}_{target}_backend\"\n", + " if key not in results or len(results[key]) == 0:\n", + " print(\"No result for:\", date_str, benchmark, target)\n", + " continue\n", + " value = float(results[key][0][\"total_runtime_in_s\"])\n", + "\n", + " if benchmark not in res:\n", + " res[benchmark] = {}\n", + "\n", + " if target not in res[benchmark]:\n", + " res[benchmark][target] = []\n", + " res[benchmark][target].append(value)\n", + "\n", + " labels = sorted(k for k in res.keys())\n", + " return labels, res\n", + "\n", + "def _extract_results(data, labels):\n", + " pd_data = []\n", + " for label in labels:\n", + " values = data[label] if label in data else [0]\n", + " mean = np.mean(values)\n", + " #conf = confidence_interval(values)\n", + " std = np.std(values)\n", + " num = len(values)\n", + " pd_data.append([mean, std, num])\n", + "\n", + "\n", + " df = pd.DataFrame(data=pd_data, index=labels, columns=[\"mean\", \"std\", \"num\"])\n", + " return df\n", + "\n", + "def generate_plot(res, configs, benchmarks, title):\n", + " data = {}\n", + " for b in benchmarks:\n", + " data[b] = _extract_results(res[b], configs)\n", + " df = pd.concat(data)\n", + "\n", + " def fix_df(df):\n", + " \"\"\"unstack changes the order, fix order and rename index'\"\"\"\n", + " df = df.reindex(index=configs, columns=benchmarks)\n", + " df.rename(index={\"isilon\": \"glx_nfs_isilon\", \"netapp\": \"glx_nfs_netapp\", \"s3\": \"glx_s3_netapp\"}, inplace=True)\n", + " df.rename(index={\"irods_on_isilon\": \"glx_irods_isilon\", \"irods_on_netapp\": \"glx_irods_netapp\", \"irods_on_s3\": \"glx_irods_s3\"}, inplace=True)\n", + " df.rename(index={\"irods_via_davrods_on_isilon\": \"glx_webdav_isilon\", \"irods_via_davrods_on_netapp\": \"glx_webdav_netapp\", \"irods_via_davrods_on_s3\": \"glx_webdav_s3\"}, inplace=True)\n", + " return df\n", + "\n", + " print(df)\n", + " df_mean = fix_df(df[\"mean\"].unstack(level=0))\n", + " df_std = fix_df(df[\"std\"].unstack(level=0))\n", + " df_mean.plot(\n", + " kind='bar', rot=0,\n", + " xlabel='Configuration',\n", + " ylabel='Time to disk (s)',\n", + " figsize=(8, 4),\n", + " yerr=df_std\n", + " )\n", + "\n", + " plt.legend()\n", + " plt.title(title)\n", + " plt.savefig(title.lower().replace(\" \", \"_\").replace(\":\",\"\")+\".png\",bbox_inches='tight')\n", + " plt.show()\n", + "\n", + "labels, res = _load()\n", + "print(labels)\n", + "# print(res[\"1x0\"].keys())\n", + "many_files = [\"1x0\", \"1x1K\", \"10x1K\", \"100x1K\", \"1000x1K\", \"2000x1K\"]\n", + "large_files = [\"1x0\", \"1x1K\", \"1x1M\", \"1x1G\", \"10x1G\"]\n", + "\n", + "generate_plot(res, [\"isilon\", \"netapp\", \"s3\"], many_files, \"Galaxy with direct access: Many files\")\n", + "generate_plot(res, [\"isilon\", \"netapp\", \"s3\"], large_files, \"Galaxy with direct access: Large files\")\n", + "\n", + "generate_plot(res, [\"irods_on_isilon\", \"irods_on_netapp\", \"irods_on_s3\"], many_files, \"Galaxy via iRODS: Many files\")\n", + "generate_plot(res, [\"irods_on_isilon\", \"irods_on_netapp\", \"irods_on_s3\"], large_files, \"Galaxy via iRODS: Large files\")\n", + "\n", + "generate_plot(res, [\"irods_via_davrods_on_isilon\", \"irods_via_davrods_on_netapp\", \"irods_via_davrods_on_s3\"], many_files, \"Galaxy via Davrods: Many files\")\n", + "generate_plot(res, [\"irods_via_davrods_on_isilon\", \"irods_via_davrods_on_netapp\", \"irods_via_davrods_on_s3\"], large_files, \"Galaxy via Davrods: Large files\")\n", + "\n", + "generate_plot(res, [\"isilon\", \"irods_on_isilon\", \"irods_via_davrods_on_isilon\"], many_files, \"Galaxy comparison for Isilon: Many files\")\n", + "generate_plot(res, [\"isilon\", \"irods_on_isilon\", \"irods_via_davrods_on_isilon\"], large_files, \"Galaxy comparison for Isilon: Large files\")\n", + "\n", + "generate_plot(res, [\"netapp\", \"irods_on_netapp\", \"irods_via_davrods_on_netapp\"], many_files, \"Galaxy comparison for Netapp: Many files\")\n", + "generate_plot(res, [\"netapp\", \"irods_on_netapp\", \"irods_via_davrods_on_netapp\"], large_files, \"Galaxy comparison for Netapp: Large files\")\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7a5a2bf0", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/00_available_options.yml b/examples/00_available_options.yml new file mode 100644 index 00000000..718210c7 --- /dev/null +++ b/examples/00_available_options.yml @@ -0,0 +1,38 @@ +config: + + results_path: "optional str, default: results/" + results_save_to_file: "optional bool, default: True" + results_print: "optional bool, default True" + + log_ansible_output: "optional bool, default False" + + openstack: "optional obj, see OpenStackComputeConfig" + galaxy: "optional obj, see GalaxyConfig" + +# Preconfigure some ansible tasks, can be referenced by name +tasks: + task1: + playbook: "required str" + playbook_folder: "optional str, default: ansible/" + # Extra vars used in the playbook + extra_vars: + key1: value1 + + host: "optional str, can also be a ansible host pattern" + +benchmarks: + benchmark1: + type: "required str, refers to Benchmark class" + repetitions: "required int" + + # Ansible task(s) executed before the benchmark + pre_task: "str OR obj" + # OR + pre_tasks: + - "str OR obj" + + # Ansible task(s) executed after the benchmark + post_task: "str OR obj" + # OR + post_tasks: + - "str OR obj" \ No newline at end of file diff --git a/examples/01_verify_setup.yml b/examples/01_verify_setup.yml new file mode 100644 index 00000000..8a0fe46b --- /dev/null +++ b/examples/01_verify_setup.yml @@ -0,0 +1,17 @@ +config: + log_ansible_output: true + results_print: false + results_save_to_file: false + +benchmarks: + verify_setup_benchmark: + type: SetupTimeBenchmark + repetitions: 1 + + pre_tasks: + - playbook: install_docker.yml + host: "irods_client,irods_server" + + hosts: + - irods_client + - irods_server diff --git a/examples/02_parameval_dd.yml b/examples/02_parameval_dd.yml new file mode 100644 index 00000000..e8418e09 --- /dev/null +++ b/examples/02_parameval_dd.yml @@ -0,0 +1,62 @@ +config: + log_ansible_output: false + results_print: false + results_save_raw_results: false + results_save_to_file: true + + +DestIsilon: &dest_isilon + destination: + host: irods_server + target_folder: /mnt/isilon + +DestNetapp: &dest_netapp + destination: + host: irods_server + target_folder: /mnt/netapp + +ConfigDdSingle: &conf_dd_single + repetitions: 1 + type: DdOneDimParams + dd: + blocksize: 1024k + + dim_key: blockcount + dim_values: + - 1024 + - 2048 + - 4096 + - 8192 + - 16384 + +ConfigDdParallel: &conf_dd_parallel + repetitions: 1 + type: DdOneDimParams + dd: + blocksize: 1024k + parallel: True + + dim_key: blockcount + dim_values: + - 1024 + - 2048 + - 4096 + - 8192 + - 16384 + +benchmarks: + parameval_dd_isilon: + type: BenchmarkCompare + repetitions: 1 + bench_a: + <<: [*dest_isilon, *conf_dd_single] + bench_b: + <<: [*dest_isilon, *conf_dd_parallel] + + parameval_dd_netapp: + type: BenchmarkCompare + repetitions: 1 + bench_a: + <<: [*dest_netapp, *conf_dd_single] + bench_b: + <<: [*dest_netapp, *conf_dd_parallel] diff --git a/examples/02_parameval_fio_bandwidth.yml b/examples/02_parameval_fio_bandwidth.yml new file mode 100644 index 00000000..8f9dbd7c --- /dev/null +++ b/examples/02_parameval_fio_bandwidth.yml @@ -0,0 +1,164 @@ +config: + log_ansible_output: false + results_print: false + results_save_to_file: true + + +DestIsilon: &dest_isilon + destination: + host: irods_server + target_folder: /mnt/isilon + +DestNetapp: &dest_netapp + destination: + host: irods_server + target_folder: /mnt/netapp + +ConfigFioRuntime: &conf_fio_runtime + repetitions: 1 + type: FioOneDimParams + fio: + mode: write + blocksize: 1024k + filesize: 4G + numjobs: 4 + iodepth: 32 + time_based: true + + dim_key: runtime_in_s + dim_values: + - 10 + - 20 + - 60 + - 120 + - 300 + +ConfigFioRuntime3: &conf_fio_ramptime + repetitions: 1 + type: FioOneDimParams + fio: + mode: write + blocksize: 1024k + filesize: 4G + numjobs: 4 + iodepth: 32 + time_based: true + runtime_in_s: 60 + + dim_key: ramptime_in_s + dim_values: + - 10 + - 20 + - 60 + - 120 + - 300 + +ConfigFioIoDepth: &conf_fio_iodepth + repetitions: 1 + type: FioOneDimParams + fio: + mode: write + blocksize: 1024k + filesize: 4G + numjobs: 4 + time_based: true + runtime_in_s: 60 + + dim_key: iodepth + dim_values: + - 1 + - 2 + - 4 + - 8 + - 16 + - 32 + - 64 + +ConfigFioBlocksize: &conf_fio_blocksize + repetitions: 1 + type: FioOneDimParams + fio: + mode: write + filesize: 4G + numjobs: 4 + iodepth: 32 + time_based: true + runtime_in_s: 60 + + dim_key: blocksize + dim_values: + - 4k + - 16k + - 64k + - 256k + - 1024k + - 4096k + - 16384k + +ConfigFioJobs: &conf_fio_numjobs + repetitions: 1 + type: FioOneDimParams + fio: + mode: write + blocksize: 1024k + filesize: 4G + iodepth: 32 + time_based: true + runtime_in_s: 60 + + dim_key: numjobs + dim_values: + - 1 + - 2 + - 4 + - 8 + - 16 + +ConfigFioFilesize: &conf_fio_filesize + repetitions: 1 + type: FioOneDimParams + fio: + mode: write + blocksize: 1024k + numjobs: 4 + iodepth: 32 + time_based: true + runtime_in_s: 60 + + dim_key: filesize + dim_values: + - 256M + - 1G + - 4G + - 16G + +benchmarks: + parameval_fio_bandwidth_runtime_isilon: + <<: [*dest_isilon, *conf_fio_runtime] + parameval_fio_bandwidth_runtime_netapp: + <<: [*dest_netapp, *conf_fio_runtime] + + parameval_fio_bandwidth_ramptime_isilon: + <<: [*dest_isilon, *conf_fio_ramptime] + parameval_fio_bandwidth_ramptime_netapp: + <<: [*dest_netapp, *conf_fio_ramptime] + + parameval_fio_bandwidth_iodepth_isilon: + <<: [*dest_isilon, *conf_fio_iodepth] + parameval_fio_bandwidth_iodepth_netapp: + <<: [*dest_netapp, *conf_fio_iodepth] + + parameval_fio_bandwidth_blocksize_isilon: + <<: [*dest_isilon, *conf_fio_blocksize] + parameval_fio_bandwidth_blocksize_netapp: + <<: [*dest_netapp, *conf_fio_blocksize] + + parameval_fio_bandwidth_numjobs_isilon: + <<: [*dest_isilon, *conf_fio_numjobs] + parameval_fio_bandwidth_numjobs_netapp: + <<: [*dest_netapp, *conf_fio_numjobs] + + parameval_fio_bandwidth_filesize_isilon: + <<: [*dest_isilon, *conf_fio_filesize] + parameval_fio_bandwidth_filesize_netapp: + <<: [*dest_netapp, *conf_fio_filesize] diff --git a/examples/02_parameval_fio_iops.yml b/examples/02_parameval_fio_iops.yml new file mode 100644 index 00000000..80250a1a --- /dev/null +++ b/examples/02_parameval_fio_iops.yml @@ -0,0 +1,112 @@ +config: + log_ansible_output: false + results_print: false + results_save_to_file: true + + +DestIsilon: &dest_isilon + destination: + host: irods_server + target_folder: /mnt/isilon + +DestNetapp: &dest_netapp + destination: + host: irods_server + target_folder: /mnt/netapp + +ConfigFioIoDepth: &conf_fio_iodepth + repetitions: 1 + type: FioOneDimParams + fio: + mode: randwrite + blocksize: 4k + filesize: 4G + numjobs: 4 + time_based: true + runtime_in_s: 60 + + dim_key: iodepth + dim_values: + - 1 + - 2 + - 4 + - 8 + - 16 + - 32 + - 64 + +ConfigFioBlocksize: &conf_fio_blocksize + repetitions: 1 + type: FioOneDimParams + fio: + mode: randwrite + filesize: 4G + numjobs: 4 + iodepth: 32 + time_based: true + runtime_in_s: 60 + + dim_key: blocksize + dim_values: + - 1k + - 4k + - 16k + - 64k + +ConfigFioJobs: &conf_fio_numjobs + repetitions: 1 + type: FioOneDimParams + fio: + mode: randwrite + blocksize: 4k + filesize: 4G + iodepth: 32 + time_based: true + runtime_in_s: 60 + + dim_key: numjobs + dim_values: + - 1 + - 2 + - 4 + - 8 + - 16 + +ConfigFioFilesize: &conf_fio_filesize + repetitions: 1 + type: FioOneDimParams + fio: + mode: randwrite + blocksize: 4k + numjobs: 4 + iodepth: 32 + time_based: true + runtime_in_s: 60 + + dim_key: filesize + dim_values: + - 256M + - 1G + - 4G + - 16G + +benchmarks: + parameval_fio_iops_iodepth_isilon: + <<: [*dest_isilon, *conf_fio_iodepth] + parameval_fio_iops_iodepth_netapp: + <<: [*dest_netapp, *conf_fio_iodepth] + + parameval_fio_iops_blocksize_isilon: + <<: [*dest_isilon, *conf_fio_blocksize] + parameval_fio_iops_blocksize_netapp: + <<: [*dest_netapp, *conf_fio_blocksize] + + parameval_fio_iops_numjobs_isilon: + <<: [*dest_isilon, *conf_fio_numjobs] + parameval_fio_iops_numjobs_netapp: + <<: [*dest_netapp, *conf_fio_numjobs] + + parameval_fio_iops_filesize_isilon: + <<: [*dest_isilon, *conf_fio_filesize] + parameval_fio_iops_filesize_netapp: + <<: [*dest_netapp, *conf_fio_filesize] diff --git a/examples/02_parameval_mdtest.yml b/examples/02_parameval_mdtest.yml new file mode 100644 index 00000000..e02d65e2 --- /dev/null +++ b/examples/02_parameval_mdtest.yml @@ -0,0 +1,26 @@ +config: + log_ansible_output: false + results_print: false + results_save_to_file: true + + +Default: &default + type: MdtestOneDimParams + repetitions: 1 + mdtest: + num_files: 1 + + dim_key: num_files + dim_values: + - 10 + - 100 + - 1000 + - 10000 + - 100000 + +benchmarks: + parameval_mdtest_isilon: + <<: *default + destination: + host: irods_server + target_folder: /mnt/isilon diff --git a/examples/02_parameval_s3benchmark.yml b/examples/02_parameval_s3benchmark.yml new file mode 100644 index 00000000..27dcd7b3 --- /dev/null +++ b/examples/02_parameval_s3benchmark.yml @@ -0,0 +1,50 @@ +config: + log_ansible_output: false + results_print: false + results_save_raw_results: false + results_save_to_file: true + +benchmarks: + parameval_s3benchmark_threads: + type: S3BenchmarkOneDimParams + repetitions: 1 + destination: + host: irods_server + s3benchmark: + base_url: https://s3.bwsfs.uni-freiburg.de/ + bucket_name: frct-smoe-bench-ec61-01 + region: fr-repl + filesize: 10M + + dim_key: threads + dim_values: + - 1 + - 2 + - 4 + - 8 + - 16 + - 32 + - 64 + - 65 + - 66 + - 67 + - 128 + + parameval_s3benchmark_runtime: + type: S3BenchmarkOneDimParams + repetitions: 1 + destination: + host: irods_server + s3benchmark: + base_url: https://s3.bwsfs.uni-freiburg.de/ + bucket_name: frct-smoe-bench-ec61-01 + region: fr-repl + filesize: 10M + + dim_key: runtime_in_s + dim_values: + - 10 + - 20 + - 60 + - 120 + - 300 diff --git a/examples/02_parameval_warp.yml b/examples/02_parameval_warp.yml new file mode 100644 index 00000000..b9008354 --- /dev/null +++ b/examples/02_parameval_warp.yml @@ -0,0 +1,51 @@ +config: + log_ansible_output: false + results_print: false + results_save_raw_results: false + results_save_to_file: true + +benchmarks: + parameval_warp_concurrent: + type: WarpOneDimParams + repetitions: 1 + destination: + host: irods_server + warp: + mode: put + base_url: s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + region: fr-repl + filesize: 10MiB + + dim_key: concurrent_ops + dim_values: + - 1 + - 2 + - 4 + - 8 + - 16 + - 32 + - 33 + - 34 + - 35 + - 64 + + parameval_warp_runtime: + type: WarpOneDimParams + repetitions: 1 + destination: + host: irods_server + warp: + mode: put + base_url: s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + region: fr-repl + filesize: 10MiB + + dim_key: runtime + dim_values: + - 10s + - 20s + - 1m0s + - 2m0s + - 5m0s diff --git a/examples/03_container_vs_native.yml b/examples/03_container_vs_native.yml new file mode 100644 index 00000000..7ab1b613 --- /dev/null +++ b/examples/03_container_vs_native.yml @@ -0,0 +1,121 @@ +config: + log_ansible_output: false + results_print: false + results_save_to_file: true + + +ThroughputWrite: &throughputWrite + repetitions: 1 + fio: + mode: write + blocksize: 1024k + numjobs: 4 + iodepth: 32 + destination: + host: irods_server + target_folder: /mnt/isilon + +ThroughputRead: &throughputRead + repetitions: 1 + fio: + mode: read + blocksize: 1024k + numjobs: 4 + iodepth: 32 + destination: + host: irods_server + target_folder: /mnt/isilon + +IopsWrite: &iopsWrite + repetitions: 1 + fio: + mode: randwrite + blocksize: 4k + numjobs: 4 + iodepth: 32 + destination: + host: irods_server + target_folder: /mnt/isilon + +IopsRead: &iopsRead + repetitions: 1 + fio: + mode: randread + blocksize: 4k + numjobs: 4 + iodepth: 32 + destination: + host: irods_server + target_folder: /mnt/isilon + +LatencyWrite: &latencyWrite + repetitions: 1 + fio: + mode: randwrite + blocksize: 4k + numjobs: 1 + iodepth: 1 + destination: + host: irods_server + target_folder: /mnt/isilon + +LatencyRead: &latencyRead + repetitions: 1 + fio: + mode: randread + blocksize: 4k + numjobs: 1 + iodepth: 1 + destination: + host: irods_server + target_folder: /mnt/isilon + + +benchmarks: + con_vs_nativ_throughput_write_con: + <<: *throughputWrite + type: FioFixedParams + + con_vs_nativ_throughput_write_nativ: + <<: *throughputWrite + type: FioNotContainerized + + con_vs_nativ_throughput_read_con: + <<: *throughputRead + type: FioFixedParams + + con_vs_nativ_throughput_read_nativ: + <<: *throughputRead + type: FioNotContainerized + + con_vs_nativ_iops_write_con: + <<: *iopsWrite + type: FioFixedParams + + con_vs_nativ_iops_write_nativ: + <<: *iopsWrite + type: FioNotContainerized + + con_vs_nativ_iops_read_con: + <<: *iopsRead + type: FioFixedParams + + con_vs_nativ_iops_read_nativ: + <<: *iopsRead + type: FioNotContainerized + + con_vs_nativ_latency_write_con: + <<: *latencyWrite + type: FioFixedParams + + con_vs_nativ_latency_write_nativ: + <<: *latencyWrite + type: FioNotContainerized + + con_vs_nativ_latency_read_con: + <<: *latencyRead + type: FioFixedParams + + con_vs_nativ_latency_read_nativ: + <<: *latencyRead + type: FioNotContainerized diff --git a/examples/03_dd_vs_fio.yml b/examples/03_dd_vs_fio.yml new file mode 100644 index 00000000..32694415 --- /dev/null +++ b/examples/03_dd_vs_fio.yml @@ -0,0 +1,66 @@ +config: + log_ansible_output: false + results_print: false + results_save_raw_results: false + results_save_to_file: true + + +DestIsilon: &dest_isilon + destination: + host: irods_server + target_folder: /mnt/isilon + +DestNetapp: &dest_netapp + destination: + host: irods_server + target_folder: /mnt/netapp + +ConfigFio: &conf_fio + repetitions: 1 + type: FioOneDimParams + fio: + mode: write + blocksize: 1024k + numjobs: 4 + iodepth: 32 + time_based: false + + dim_key: filesize + dim_values: + - 1G + - 2G + - 4G + - 8G + - 16G + +ConfigDd: &conf_dd + repetitions: 1 + type: DdOneDimParams + dd: + blocksize: 1024k + parallel: True + + dim_key: blockcount + dim_values: + - 1024 + - 2048 + - 4096 + - 8192 + - 16384 + +benchmarks: + dd_vs_fio_isilon: + type: BenchmarkCompare + repetitions: 1 + bench_a: + <<: [*dest_isilon, *conf_fio] + bench_b: + <<: [*dest_isilon, *conf_dd] + + dd_vs_fio_netapp: + type: BenchmarkCompare + repetitions: 1 + bench_a: + <<: [*dest_netapp, *conf_fio] + bench_b: + <<: [*dest_netapp, *conf_dd] diff --git a/examples/03_s3benchmark_vs_warp.yml b/examples/03_s3benchmark_vs_warp.yml new file mode 100644 index 00000000..322f1ab1 --- /dev/null +++ b/examples/03_s3benchmark_vs_warp.yml @@ -0,0 +1,80 @@ +config: + log_ansible_output: false + results_print: false + results_save_raw_results: false + results_save_to_file: true + +Dest: &dest + destination: + host: irods_server + +ConfigS3Benchmark: &conf_s3bench + repetitions: 1 + type: S3BenchmarkFixedParams + s3benchmark: &conf_s3bench_defaults + base_url: https://s3.bwsfs.uni-freiburg.de/ + bucket_name: frct-smoe-bench-ec61-01 + filesize: 10M + region: fr-repl + runtime_in_s: 60 + threads: 64 + +ConfigS3BenchmarkOps: &conf_s3bench_ops + repetitions: 1 + type: S3BenchmarkFixedParams + s3benchmark: + <<: *conf_s3bench_defaults + filesize: 1K + +ConfigWarpGet: &conf_warp_get_put + type: WarpFixedParams + repetitions: 1 + warp: &conf_warp_defaults + mode: get + base_url: s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + filesize: 10MiB + region: fr-repl + runtime: 60s + concurrent_ops: 32 + +ConfigWarpDelOps: &conf_warp_del_ops + type: WarpFixedParams + repetitions: 1 + warp: + <<: *conf_warp_defaults + mode: delete + filesize: 1KiB + +ConfigWarpGetOps: &conf_warp_get_ops + type: WarpFixedParams + repetitions: 1 + warp: + <<: *conf_warp_defaults + mode: get + filesize: 1KiB + +benchmarks: + s3benchmark_vs_warp_get_put: + type: BenchmarkCompare + repetitions: 1 + bench_a: + <<: [*dest, *conf_s3bench] + bench_b: + <<: [*dest, *conf_warp_get_put] + + s3benchmark_vs_warp_get_put_ops: + type: BenchmarkCompare + repetitions: 1 + bench_a: + <<: [*dest, *conf_s3bench_ops] + bench_b: + <<: [*dest, *conf_warp_get_ops] + + s3benchmark_vs_warp_delete: + type: BenchmarkCompare + repetitions: 1 + bench_a: + <<: [*dest, *conf_s3bench_ops] + bench_b: + <<: [*dest, *conf_warp_del_ops] diff --git a/examples/04_daytime_evaluation.yml b/examples/04_daytime_evaluation.yml new file mode 100644 index 00000000..b2473b43 --- /dev/null +++ b/examples/04_daytime_evaluation.yml @@ -0,0 +1,72 @@ +config: + log_ansible_output: false + results_print: false + results_save_to_file: true + +ConfigFio: &conf_fio + repetitions: 1 + type: FioFixedParams + fio: + mode: write + blocksize: 1024k + numjobs: 4 + iodepth: 32 + +ConfigWarp: &conf_warp + repetitions: 1 + type: WarpFixedParams + warp: + mode: put + base_url: s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + concurrent_ops: 32 + filesize: 10MiB + region: fr-repl + runtime: 60s + +benchmarks: + daytime_evaluation_isilon_01: + <<: *conf_fio + destination: + host: irods_server + target_folder: /mnt/isilon + + daytime_evaluation_netapp_01: + <<: *conf_fio + destination: + host: irods_server + target_folder: /mnt/netapp + + daytime_evaluation_ssdtank_01: + <<: *conf_fio + destination: + host: irods_server + target_folder: /mnt/ssd_tank/test_moehrles + + daytime_evaluation_s3_01: + <<: *conf_warp + destination: + host: irods_server + + daytime_evaluation_isilon_02: + <<: *conf_fio + destination: + host: irods_server + target_folder: /mnt/isilon + + daytime_evaluation_netapp_02: + <<: *conf_fio + destination: + host: irods_server + target_folder: /mnt/netapp + + daytime_evaluation_ssdtank_02: + <<: *conf_fio + destination: + host: irods_server + target_folder: /mnt/ssd_tank/test_moehrles + + daytime_evaluation_s3_02: + <<: *conf_warp + destination: + host: irods_server diff --git a/examples/05_benchmark_metadata.yml b/examples/05_benchmark_metadata.yml new file mode 100644 index 00000000..fd98e9b7 --- /dev/null +++ b/examples/05_benchmark_metadata.yml @@ -0,0 +1,226 @@ +config: + log_ansible_output: false + results_print: false + results_save_to_file: true + +Default_100k: &default_100k + type: MdtestOneDimParams + repetitions: 1 + mdtest: + num_files: 1 + + dim_key: num_files + dim_values: + - 10 + - 100 + - 1000 + - 10000 + - 100000 + +Default_1k: &default_1k + type: MdtestOneDimParams + repetitions: 1 + mdtest: + num_files: 1 + + dim_key: num_files + dim_values: + - 10 + - 100 + - 1000 + + +benchmarks: + benchmark_metadata_isilon: + <<: *default_100k + destination: + host: irods_server + target_folder: /mnt/isilon + + benchmark_metadata_netapp: + <<: *default_100k + destination: + host: irods_server + target_folder: /mnt/netapp + + benchmark_metadata_ssd_tank: + <<: *default_100k + destination: + host: irods_server + target_folder: /mnt/ssd_tank/test_moehrles + + benchmark_metadata_local: + <<: *default_100k + destination: + host: irods_server + target_folder: /mnt/local/ubuntu + + benchmark_metadata_irods_fuse_on_isilon: + <<: *default_1k + destination: + host: irods_client + target_folder: /mnt/volume_under_test/irods-fuse + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + - playbook: setup_irods_fuse_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + irods_host_volume_owner_uid: "1000" + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + - playbook: cleanup_irods_fuse_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + + benchmark_metadata_irods_fuse_on_netapp: + <<: *default_1k + destination: + host: irods_client + target_folder: /mnt/volume_under_test/irods-fuse + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + - playbook: setup_irods_fuse_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + irods_host_volume_owner_uid: "1000" + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + - playbook: cleanup_irods_fuse_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + + benchmark_metadata_irods_fuse_on_s3: + <<: *default_1k + destination: + host: irods_client + target_folder: /mnt/volume_under_test/irods-fuse + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_use_s3: true + - playbook: setup_irods_fuse_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + irods_host_volume_owner_uid: "1000" + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server + - playbook: cleanup_irods_fuse_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + + benchmark_metadata_irods_davrods_on_isilon: + <<: *default_1k + destination: + host: irods_client + target_folder: /mnt/volume_under_test/irods-webdav + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + irods_use_davrods: true + davrods_owner_uid: "1000" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1000" + + post_tasks: + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + irods_use_davrods: true + + benchmark_metadata_irods_davrods_on_netapp: + <<: *default_1k + destination: + host: irods_client + target_folder: /mnt/volume_under_test/irods-webdav + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + irods_use_davrods: true + davrods_owner_uid: "1000" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1000" + + post_tasks: + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + irods_use_davrods: true + + benchmark_metadata_irods_davrods_on_s3: + <<: *default_1k + destination: + host: irods_client + target_folder: /mnt/volume_under_test/irods-webdav + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_use_s3: true + irods_use_davrods: true + davrods_owner_uid: "1000" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1000" + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_use_davrods: true + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test diff --git a/examples/05_benchmark_posix.yml b/examples/05_benchmark_posix.yml new file mode 100644 index 00000000..1e23962c --- /dev/null +++ b/examples/05_benchmark_posix.yml @@ -0,0 +1,213 @@ +config: + log_ansible_output: false + results_print: false + results_save_to_file: true + +Default: &default + type: FioFullPosix + repetitions: 1 + fio: + runtime_in_s: 60 + +benchmarks: + benchmark_posix_isilon: + <<: *default + destination: + host: irods_server + target_folder: /mnt/isilon + + benchmark_posix_netapp: + <<: *default + destination: + host: irods_server + target_folder: /mnt/netapp + + benchmark_posix_ssd_tank: + <<: *default + destination: + host: irods_server + target_folder: /mnt/ssd_tank/test_moehrles + + benchmark_posix_local: + <<: *default + destination: + host: irods_server + target_folder: /mnt/local/ubuntu + + benchmark_posix_irods_fuse_on_isilon: + <<: *default + destination: + host: irods_client + target_folder: /mnt/volume_under_test/irods-fuse + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + - playbook: setup_irods_fuse_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + irods_host_volume_owner_uid: "1000" + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + - playbook: cleanup_irods_fuse_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + + benchmark_posix_irods_fuse_on_netapp: + <<: *default + destination: + host: irods_client + target_folder: /mnt/volume_under_test/irods-fuse + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + - playbook: setup_irods_fuse_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + irods_host_volume_owner_uid: "1000" + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + - playbook: cleanup_irods_fuse_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + + benchmark_posix_irods_fuse_on_s3: + <<: *default + destination: + host: irods_client + target_folder: /mnt/volume_under_test/irods-fuse + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_use_s3: true + - playbook: setup_irods_fuse_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + irods_host_volume_owner_uid: "1000" + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server + - playbook: cleanup_irods_fuse_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + + benchmark_posix_irods_davrods_on_isilon: + <<: *default + destination: + host: irods_client + target_folder: /mnt/volume_under_test/irods-webdav + + fio: + runtime_in_s: 60 + prepare_read_benchmark_in_tmp: true + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + irods_use_davrods: true + davrods_owner_uid: "1000" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1000" + + post_tasks: + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + irods_use_davrods: true + + benchmark_posix_irods_davrods_on_netapp: + <<: *default + destination: + host: irods_client + target_folder: /mnt/volume_under_test/irods-webdav + + fio: + runtime_in_s: 60 + prepare_read_benchmark_in_tmp: true + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + irods_use_davrods: true + davrods_owner_uid: "1000" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1000" + + post_tasks: + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + irods_use_davrods: true + + benchmark_posix_irods_davrods_on_s3: + <<: *default + destination: + host: irods_client + target_folder: /mnt/volume_under_test/irods-webdav + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_use_s3: true + irods_use_davrods: true + davrods_owner_uid: "1000" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1000" + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_use_davrods: true + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test diff --git a/examples/05_benchmark_s3.yml b/examples/05_benchmark_s3.yml new file mode 100644 index 00000000..f311e420 --- /dev/null +++ b/examples/05_benchmark_s3.yml @@ -0,0 +1,48 @@ +config: + log_ansible_output: false + results_print: false + results_save_raw_results: false + results_save_to_file: true + +benchmarks: + benchmark_s3_warp_get_put: + type: WarpFixedParams + repetitions: 1 + destination: + host: irods_server + warp: + mode: get + base_url: s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + concurrent_ops: 32 + filesize: 10MiB + region: fr-repl + runtime: 5m0s + + benchmark_s3_warp_get_put_ops: + type: WarpFixedParams + repetitions: 1 + destination: + host: irods_server + warp: + mode: get + base_url: s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + concurrent_ops: 32 + filesize: 1KiB + region: fr-repl + runtime: 5m0s + + benchmark_s3_warp_delete: + type: WarpFixedParams + repetitions: 1 + destination: + host: irods_server + warp: + mode: delete + base_url: s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + concurrent_ops: 32 + filesize: 1KiB + region: fr-repl + runtime: 5m0s diff --git a/examples/06_benchmark_galaxy_10000x1K.yml b/examples/06_benchmark_galaxy_10000x1K.yml new file mode 100644 index 00000000..fb45fbc7 --- /dev/null +++ b/examples/06_benchmark_galaxy_10000x1K.yml @@ -0,0 +1,242 @@ +config: + log_ansible_output: false + results_print: false + results_save_raw_results: false + results_save_to_file: true + +Default: &default + input: + num_files: 10000 + file_size_in_bytes: 1024 + verification_timeout_in_s: 9000 + export_volume: /mnt/local + + +benchmarks: + benchmark_galaxy_10000x1K_isilon_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/isilon + galaxy_job: + <<: *default + path_to_files: /mnt/isilon/galaxy_in_progress/files + + benchmark_galaxy_10000x1K_netapp_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/netapp + galaxy_job: + <<: *default + path_to_files: /mnt/netapp/galaxy_in_progress/files + + benchmark_galaxy_10000x1K_s3_backend: + type: GalaxyFileGenOnS3Job + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + base_url: https://s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + + # benchmark_galaxy_10000x1K_irods_via_fuse_on_isilon_backend: + # type: GalaxyFileGenOnMountVolumeJob + # repetitions: 1 + # destination: + # host: irods_client + # target_folder: /mnt/volume_under_test/irods-fuse + # galaxy_job: + # <<: *default + # path_to_files: /mnt/isilon/irods/home/rods/galaxy_in_progress/files + + # pre_tasks: + # - playbook: setup_irods_server.yml + # host: irods_server + # extra_vars: + # irods_host_volume: /mnt/isilon + # - playbook: setup_irods_fuse_client.yml + # host: irods_client + # extra_vars: + # irods_host_volume: /mnt/volume_under_test + # irods_server_host: "132.230.223.139" + # irods_host_volume_owner_uid: "1000" + # post_tasks: + # - playbook: cleanup_irods_server.yml + # host: irods_server + # extra_vars: + # irods_host_volume: /mnt/isilon + # - playbook: cleanup_irods_fuse_client.yml + # host: irods_client + # extra_vars: + # irods_host_volume: /mnt/volume_under_test + + benchmark_galaxy_10000x1K_irods_via_davrods_on_isilon_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/volume_under_test/irods-webdav + galaxy_job: + <<: *default + path_to_files: /mnt/isilon/irods/home/rods/galaxy_in_progress/files + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + irods_use_davrods: true + davrods_owner_uid: "1450" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1450" + + post_tasks: + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + irods_use_davrods: true + + benchmark_galaxy_10000x1K_irods_via_davrods_on_netapp_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/volume_under_test/irods-webdav + galaxy_job: + <<: *default + path_to_files: /mnt/netapp/irods/home/rods/galaxy_in_progress/files + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + irods_use_davrods: true + davrods_owner_uid: "1450" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1450" + + post_tasks: + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + irods_use_davrods: true + + benchmark_galaxy_10000x1K_irods_via_davrods_on_s3_backend: + type: GalaxyFileGenOnIrodsOnS3Job + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + base_url: https://s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_use_s3: true + irods_use_davrods: true + davrods_owner_uid: "1450" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1450" + + post_tasks: + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_use_davrods: true + + benchmark_galaxy_10000x1K_irods_on_isilon_backend: + type: GalaxyFileGenOnIrodsOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + path_to_files: /mnt/isilon/irods/home/rods + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + + benchmark_galaxy_10000x1K_irods_on_netapp_backend: + type: GalaxyFileGenOnIrodsOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + path_to_files: /mnt/netapp/irods/home/rods + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + + benchmark_galaxy_10000x1K_irods_on_s3_backend: + type: GalaxyFileGenOnIrodsOnS3Job + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + base_url: https://s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_use_s3: true + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server diff --git a/examples/06_benchmark_galaxy_1000x1K.yml b/examples/06_benchmark_galaxy_1000x1K.yml new file mode 100644 index 00000000..94bf0727 --- /dev/null +++ b/examples/06_benchmark_galaxy_1000x1K.yml @@ -0,0 +1,242 @@ +config: + log_ansible_output: false + results_print: false + results_save_raw_results: false + results_save_to_file: true + +Default: &default + input: + num_files: 1000 + file_size_in_bytes: 1024 + verification_timeout_in_s: 1000 + export_volume: /mnt/local + + +benchmarks: + benchmark_galaxy_1000x1K_isilon_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/isilon + galaxy_job: + <<: *default + path_to_files: /mnt/isilon/galaxy_in_progress/files + + benchmark_galaxy_1000x1K_netapp_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/netapp + galaxy_job: + <<: *default + path_to_files: /mnt/netapp/galaxy_in_progress/files + + benchmark_galaxy_1000x1K_s3_backend: + type: GalaxyFileGenOnS3Job + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + base_url: https://s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + + # benchmark_galaxy_1000x1K_irods_via_fuse_on_isilon_backend: + # type: GalaxyFileGenOnMountVolumeJob + # repetitions: 1 + # destination: + # host: irods_client + # target_folder: /mnt/volume_under_test/irods-fuse + # galaxy_job: + # <<: *default + # path_to_files: /mnt/isilon/irods/home/rods/galaxy_in_progress/files + + # pre_tasks: + # - playbook: setup_irods_server.yml + # host: irods_server + # extra_vars: + # irods_host_volume: /mnt/isilon + # - playbook: setup_irods_fuse_client.yml + # host: irods_client + # extra_vars: + # irods_host_volume: /mnt/volume_under_test + # irods_server_host: "132.230.223.139" + # irods_host_volume_owner_uid: "1000" + # post_tasks: + # - playbook: cleanup_irods_server.yml + # host: irods_server + # extra_vars: + # irods_host_volume: /mnt/isilon + # - playbook: cleanup_irods_fuse_client.yml + # host: irods_client + # extra_vars: + # irods_host_volume: /mnt/volume_under_test + + benchmark_galaxy_1000x1K_irods_via_davrods_on_isilon_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/volume_under_test/irods-webdav + galaxy_job: + <<: *default + path_to_files: /mnt/isilon/irods/home/rods/galaxy_in_progress/files + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + irods_use_davrods: true + davrods_owner_uid: "1450" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1450" + + post_tasks: + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + irods_use_davrods: true + + benchmark_galaxy_1000x1K_irods_via_davrods_on_netapp_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/volume_under_test/irods-webdav + galaxy_job: + <<: *default + path_to_files: /mnt/netapp/irods/home/rods/galaxy_in_progress/files + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + irods_use_davrods: true + davrods_owner_uid: "1450" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1450" + + post_tasks: + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + irods_use_davrods: true + + benchmark_galaxy_1000x1K_irods_via_davrods_on_s3_backend: + type: GalaxyFileGenOnIrodsOnS3Job + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + base_url: https://s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_use_s3: true + irods_use_davrods: true + davrods_owner_uid: "1450" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1450" + + post_tasks: + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_use_davrods: true + + benchmark_galaxy_1000x1K_irods_on_isilon_backend: + type: GalaxyFileGenOnIrodsOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + path_to_files: /mnt/isilon/irods/home/rods + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + + benchmark_galaxy_1000x1K_irods_on_netapp_backend: + type: GalaxyFileGenOnIrodsOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + path_to_files: /mnt/netapp/irods/home/rods + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + + benchmark_galaxy_1000x1K_irods_on_s3_backend: + type: GalaxyFileGenOnIrodsOnS3Job + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + base_url: https://s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_use_s3: true + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server diff --git a/examples/06_benchmark_galaxy_100x1K.yml b/examples/06_benchmark_galaxy_100x1K.yml new file mode 100644 index 00000000..19441780 --- /dev/null +++ b/examples/06_benchmark_galaxy_100x1K.yml @@ -0,0 +1,242 @@ +config: + log_ansible_output: false + results_print: false + results_save_raw_results: false + results_save_to_file: true + +Default: &default + input: + num_files: 100 + file_size_in_bytes: 1024 + verification_timeout_in_s: 500 + export_volume: /mnt/local + + +benchmarks: + benchmark_galaxy_100x1K_isilon_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/isilon + galaxy_job: + <<: *default + path_to_files: /mnt/isilon/galaxy_in_progress/files + + benchmark_galaxy_100x1K_netapp_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/netapp + galaxy_job: + <<: *default + path_to_files: /mnt/netapp/galaxy_in_progress/files + + benchmark_galaxy_100x1K_s3_backend: + type: GalaxyFileGenOnS3Job + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + base_url: https://s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + + # benchmark_galaxy_100x1K_irods_via_fuse_on_isilon_backend: + # type: GalaxyFileGenOnMountVolumeJob + # repetitions: 1 + # destination: + # host: irods_client + # target_folder: /mnt/volume_under_test/irods-fuse + # galaxy_job: + # <<: *default + # path_to_files: /mnt/isilon/irods/home/rods/galaxy_in_progress/files + + # pre_tasks: + # - playbook: setup_irods_server.yml + # host: irods_server + # extra_vars: + # irods_host_volume: /mnt/isilon + # - playbook: setup_irods_fuse_client.yml + # host: irods_client + # extra_vars: + # irods_host_volume: /mnt/volume_under_test + # irods_server_host: "132.230.223.139" + # irods_host_volume_owner_uid: "1000" + # post_tasks: + # - playbook: cleanup_irods_server.yml + # host: irods_server + # extra_vars: + # irods_host_volume: /mnt/isilon + # - playbook: cleanup_irods_fuse_client.yml + # host: irods_client + # extra_vars: + # irods_host_volume: /mnt/volume_under_test + + benchmark_galaxy_100x1K_irods_via_davrods_on_isilon_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/volume_under_test/irods-webdav + galaxy_job: + <<: *default + path_to_files: /mnt/isilon/irods/home/rods/galaxy_in_progress/files + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + irods_use_davrods: true + davrods_owner_uid: "1450" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1450" + + post_tasks: + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + irods_use_davrods: true + + benchmark_galaxy_100x1K_irods_via_davrods_on_netapp_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/volume_under_test/irods-webdav + galaxy_job: + <<: *default + path_to_files: /mnt/netapp/irods/home/rods/galaxy_in_progress/files + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + irods_use_davrods: true + davrods_owner_uid: "1450" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1450" + + post_tasks: + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + irods_use_davrods: true + + benchmark_galaxy_100x1K_irods_via_davrods_on_s3_backend: + type: GalaxyFileGenOnIrodsOnS3Job + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + base_url: https://s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_use_s3: true + irods_use_davrods: true + davrods_owner_uid: "1450" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1450" + + post_tasks: + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_use_davrods: true + + benchmark_galaxy_100x1K_irods_on_isilon_backend: + type: GalaxyFileGenOnIrodsOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + path_to_files: /mnt/isilon/irods/home/rods + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + + benchmark_galaxy_100x1K_irods_on_netapp_backend: + type: GalaxyFileGenOnIrodsOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + path_to_files: /mnt/netapp/irods/home/rods + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + + benchmark_galaxy_100x1K_irods_on_s3_backend: + type: GalaxyFileGenOnIrodsOnS3Job + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + base_url: https://s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_use_s3: true + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server diff --git a/examples/06_benchmark_galaxy_10x1G.yml b/examples/06_benchmark_galaxy_10x1G.yml new file mode 100644 index 00000000..931526a7 --- /dev/null +++ b/examples/06_benchmark_galaxy_10x1G.yml @@ -0,0 +1,242 @@ +config: + log_ansible_output: false + results_print: false + results_save_raw_results: false + results_save_to_file: true + +Default: &default + input: + num_files: 10 + file_size_in_bytes: 1073741824 # 1 Gib + verification_timeout_in_s: 1000 + export_volume: /mnt/local + + +benchmarks: + benchmark_galaxy_10x1G_isilon_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/isilon + galaxy_job: + <<: *default + path_to_files: /mnt/isilon/galaxy_in_progress/files + + benchmark_galaxy_10x1G_netapp_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/netapp + galaxy_job: + <<: *default + path_to_files: /mnt/netapp/galaxy_in_progress/files + + # benchmark_galaxy_10x1G_s3_backend: + # type: GalaxyFileGenOnS3Job + # repetitions: 1 + # destination: + # host: irods_client + # galaxy_job: + # <<: *default + # base_url: https://s3.bwsfs.uni-freiburg.de + # bucket_name: frct-smoe-bench-ec61-01 + + # benchmark_galaxy_10x1G_irods_via_fuse_on_isilon_backend: + # type: GalaxyFileGenOnMountVolumeJob + # repetitions: 1 + # destination: + # host: irods_client + # target_folder: /mnt/volume_under_test/irods-fuse + # galaxy_job: + # <<: *default + # path_to_files: /mnt/isilon/irods/home/rods/galaxy_in_progress/files + + # pre_tasks: + # - playbook: setup_irods_server.yml + # host: irods_server + # extra_vars: + # irods_host_volume: /mnt/isilon + # - playbook: setup_irods_fuse_client.yml + # host: irods_client + # extra_vars: + # irods_host_volume: /mnt/volume_under_test + # irods_server_host: "132.230.223.139" + # irods_host_volume_owner_uid: "1000" + # post_tasks: + # - playbook: cleanup_irods_server.yml + # host: irods_server + # extra_vars: + # irods_host_volume: /mnt/isilon + # - playbook: cleanup_irods_fuse_client.yml + # host: irods_client + # extra_vars: + # irods_host_volume: /mnt/volume_under_test + + benchmark_galaxy_10x1G_irods_via_davrods_on_isilon_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/volume_under_test/irods-webdav + galaxy_job: + <<: *default + path_to_files: /mnt/isilon/irods/home/rods/galaxy_in_progress/files + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + irods_use_davrods: true + davrods_owner_uid: "1450" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1450" + + post_tasks: + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + irods_use_davrods: true + + benchmark_galaxy_10x1G_irods_via_davrods_on_netapp_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/volume_under_test/irods-webdav + galaxy_job: + <<: *default + path_to_files: /mnt/netapp/irods/home/rods/galaxy_in_progress/files + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + irods_use_davrods: true + davrods_owner_uid: "1450" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1450" + + post_tasks: + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + irods_use_davrods: true + + benchmark_galaxy_10x1G_irods_via_davrods_on_s3_backend: + type: GalaxyFileGenOnIrodsOnS3Job + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + base_url: https://s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_use_s3: true + irods_use_davrods: true + davrods_owner_uid: "1450" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1450" + + post_tasks: + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_use_davrods: true + + benchmark_galaxy_10x1G_irods_on_isilon_backend: + type: GalaxyFileGenOnIrodsOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + path_to_files: /mnt/isilon/irods/home/rods + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + + benchmark_galaxy_10x1G_irods_on_netapp_backend: + type: GalaxyFileGenOnIrodsOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + path_to_files: /mnt/netapp/irods/home/rods + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + + benchmark_galaxy_10x1G_irods_on_s3_backend: + type: GalaxyFileGenOnIrodsOnS3Job + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + base_url: https://s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_use_s3: true + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server diff --git a/examples/06_benchmark_galaxy_10x1K.yml b/examples/06_benchmark_galaxy_10x1K.yml new file mode 100644 index 00000000..53f83798 --- /dev/null +++ b/examples/06_benchmark_galaxy_10x1K.yml @@ -0,0 +1,242 @@ +config: + log_ansible_output: false + results_print: false + results_save_raw_results: false + results_save_to_file: true + +Default: &default + input: + num_files: 10 + file_size_in_bytes: 1024 + verification_timeout_in_s: 100 + export_volume: /mnt/local + + +benchmarks: + benchmark_galaxy_10x1K_isilon_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/isilon + galaxy_job: + <<: *default + path_to_files: /mnt/isilon/galaxy_in_progress/files + + benchmark_galaxy_10x1K_netapp_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/netapp + galaxy_job: + <<: *default + path_to_files: /mnt/netapp/galaxy_in_progress/files + + benchmark_galaxy_10x1K_s3_backend: + type: GalaxyFileGenOnS3Job + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + base_url: https://s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + + # benchmark_galaxy_10x1K_irods_via_fuse_on_isilon_backend: + # type: GalaxyFileGenOnMountVolumeJob + # repetitions: 1 + # destination: + # host: irods_client + # target_folder: /mnt/volume_under_test/irods-fuse + # galaxy_job: + # <<: *default + # path_to_files: /mnt/isilon/irods/home/rods/galaxy_in_progress/files + + # pre_tasks: + # - playbook: setup_irods_server.yml + # host: irods_server + # extra_vars: + # irods_host_volume: /mnt/isilon + # - playbook: setup_irods_fuse_client.yml + # host: irods_client + # extra_vars: + # irods_host_volume: /mnt/volume_under_test + # irods_server_host: "132.230.223.139" + # irods_host_volume_owner_uid: "1000" + # post_tasks: + # - playbook: cleanup_irods_server.yml + # host: irods_server + # extra_vars: + # irods_host_volume: /mnt/isilon + # - playbook: cleanup_irods_fuse_client.yml + # host: irods_client + # extra_vars: + # irods_host_volume: /mnt/volume_under_test + + benchmark_galaxy_10x1K_irods_via_davrods_on_isilon_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/volume_under_test/irods-webdav + galaxy_job: + <<: *default + path_to_files: /mnt/isilon/irods/home/rods/galaxy_in_progress/files + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + irods_use_davrods: true + davrods_owner_uid: "1450" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1450" + + post_tasks: + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + irods_use_davrods: true + + benchmark_galaxy_10x1K_irods_via_davrods_on_netapp_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/volume_under_test/irods-webdav + galaxy_job: + <<: *default + path_to_files: /mnt/netapp/irods/home/rods/galaxy_in_progress/files + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + irods_use_davrods: true + davrods_owner_uid: "1450" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1450" + + post_tasks: + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + irods_use_davrods: true + + benchmark_galaxy_10x1K_irods_via_davrods_on_s3_backend: + type: GalaxyFileGenOnIrodsOnS3Job + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + base_url: https://s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_use_s3: true + irods_use_davrods: true + davrods_owner_uid: "1450" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1450" + + post_tasks: + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_use_davrods: true + + benchmark_galaxy_10x1K_irods_on_isilon_backend: + type: GalaxyFileGenOnIrodsOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + path_to_files: /mnt/isilon/irods/home/rods + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + + benchmark_galaxy_10x1K_irods_on_netapp_backend: + type: GalaxyFileGenOnIrodsOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + path_to_files: /mnt/netapp/irods/home/rods + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + + benchmark_galaxy_10x1K_irods_on_s3_backend: + type: GalaxyFileGenOnIrodsOnS3Job + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + base_url: https://s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_use_s3: true + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server diff --git a/examples/06_benchmark_galaxy_1x0.yml b/examples/06_benchmark_galaxy_1x0.yml new file mode 100644 index 00000000..810f514a --- /dev/null +++ b/examples/06_benchmark_galaxy_1x0.yml @@ -0,0 +1,242 @@ +config: + log_ansible_output: false + results_print: false + results_save_raw_results: false + results_save_to_file: true + +Default: &default + input: + num_files: 1 + file_size_in_bytes: 0 + verification_timeout_in_s: 100 + export_volume: /mnt/local + + +benchmarks: + benchmark_galaxy_1x0_isilon_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/isilon + galaxy_job: + <<: *default + path_to_files: /mnt/isilon/galaxy_in_progress/files + + benchmark_galaxy_1x0_netapp_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/netapp + galaxy_job: + <<: *default + path_to_files: /mnt/netapp/galaxy_in_progress/files + + benchmark_galaxy_1x0_s3_backend: + type: GalaxyFileGenOnS3Job + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + base_url: https://s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + + # benchmark_galaxy_1x0_irods_via_fuse_on_isilon_backend: + # type: GalaxyFileGenOnMountVolumeJob + # repetitions: 1 + # destination: + # host: irods_client + # target_folder: /mnt/volume_under_test/irods-fuse + # galaxy_job: + # <<: *default + # path_to_files: /mnt/isilon/irods/home/rods/galaxy_in_progress/files + + # pre_tasks: + # - playbook: setup_irods_server.yml + # host: irods_server + # extra_vars: + # irods_host_volume: /mnt/isilon + # - playbook: setup_irods_fuse_client.yml + # host: irods_client + # extra_vars: + # irods_host_volume: /mnt/volume_under_test + # irods_server_host: "132.230.223.139" + # irods_host_volume_owner_uid: "1000" + # post_tasks: + # - playbook: cleanup_irods_server.yml + # host: irods_server + # extra_vars: + # irods_host_volume: /mnt/isilon + # - playbook: cleanup_irods_fuse_client.yml + # host: irods_client + # extra_vars: + # irods_host_volume: /mnt/volume_under_test + + benchmark_galaxy_1x0_irods_via_davrods_on_isilon_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/volume_under_test/irods-webdav + galaxy_job: + <<: *default + path_to_files: /mnt/isilon/irods/home/rods/galaxy_in_progress/files + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + irods_use_davrods: true + davrods_owner_uid: "1450" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1450" + + post_tasks: + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + irods_use_davrods: true + + benchmark_galaxy_1x0_irods_via_davrods_on_netapp_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/volume_under_test/irods-webdav + galaxy_job: + <<: *default + path_to_files: /mnt/netapp/irods/home/rods/galaxy_in_progress/files + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + irods_use_davrods: true + davrods_owner_uid: "1450" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1450" + + post_tasks: + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + irods_use_davrods: true + + benchmark_galaxy_1x0_irods_via_davrods_on_s3_backend: + type: GalaxyFileGenOnIrodsOnS3Job + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + base_url: https://s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_use_s3: true + irods_use_davrods: true + davrods_owner_uid: "1450" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1450" + + post_tasks: + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_use_davrods: true + + benchmark_galaxy_1x0_irods_on_isilon_backend: + type: GalaxyFileGenOnIrodsOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + path_to_files: /mnt/isilon/irods/home/rods + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + + benchmark_galaxy_1x0_irods_on_netapp_backend: + type: GalaxyFileGenOnIrodsOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + path_to_files: /mnt/netapp/irods/home/rods + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + + benchmark_galaxy_1x0_irods_on_s3_backend: + type: GalaxyFileGenOnIrodsOnS3Job + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + base_url: https://s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_use_s3: true + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server diff --git a/examples/06_benchmark_galaxy_1x1G.yml b/examples/06_benchmark_galaxy_1x1G.yml new file mode 100644 index 00000000..f64e5a38 --- /dev/null +++ b/examples/06_benchmark_galaxy_1x1G.yml @@ -0,0 +1,242 @@ +config: + log_ansible_output: false + results_print: false + results_save_raw_results: false + results_save_to_file: true + +Default: &default + input: + num_files: 1 + file_size_in_bytes: 1073741824 # 1 Gib + verification_timeout_in_s: 1000 + export_volume: /mnt/local + + +benchmarks: + benchmark_galaxy_1x1G_isilon_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/isilon + galaxy_job: + <<: *default + path_to_files: /mnt/isilon/galaxy_in_progress/files + + benchmark_galaxy_1x1G_netapp_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/netapp + galaxy_job: + <<: *default + path_to_files: /mnt/netapp/galaxy_in_progress/files + + # benchmark_galaxy_1x1G_s3_backend: + # type: GalaxyFileGenOnS3Job + # repetitions: 1 + # destination: + # host: irods_client + # galaxy_job: + # <<: *default + # base_url: https://s3.bwsfs.uni-freiburg.de + # bucket_name: frct-smoe-bench-ec61-01 + + # benchmark_galaxy_1x1G_irods_via_fuse_on_isilon_backend: + # type: GalaxyFileGenOnMountVolumeJob + # repetitions: 1 + # destination: + # host: irods_client + # target_folder: /mnt/volume_under_test/irods-fuse + # galaxy_job: + # <<: *default + # path_to_files: /mnt/isilon/irods/home/rods/galaxy_in_progress/files + + # pre_tasks: + # - playbook: setup_irods_server.yml + # host: irods_server + # extra_vars: + # irods_host_volume: /mnt/isilon + # - playbook: setup_irods_fuse_client.yml + # host: irods_client + # extra_vars: + # irods_host_volume: /mnt/volume_under_test + # irods_server_host: "132.230.223.139" + # irods_host_volume_owner_uid: "1000" + # post_tasks: + # - playbook: cleanup_irods_server.yml + # host: irods_server + # extra_vars: + # irods_host_volume: /mnt/isilon + # - playbook: cleanup_irods_fuse_client.yml + # host: irods_client + # extra_vars: + # irods_host_volume: /mnt/volume_under_test + + benchmark_galaxy_1x1G_irods_via_davrods_on_isilon_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/volume_under_test/irods-webdav + galaxy_job: + <<: *default + path_to_files: /mnt/isilon/irods/home/rods/galaxy_in_progress/files + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + irods_use_davrods: true + davrods_owner_uid: "1450" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1450" + + post_tasks: + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + irods_use_davrods: true + + benchmark_galaxy_1x1G_irods_via_davrods_on_netapp_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/volume_under_test/irods-webdav + galaxy_job: + <<: *default + path_to_files: /mnt/netapp/irods/home/rods/galaxy_in_progress/files + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + irods_use_davrods: true + davrods_owner_uid: "1450" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1450" + + post_tasks: + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + irods_use_davrods: true + + benchmark_galaxy_1x1G_irods_via_davrods_on_s3_backend: + type: GalaxyFileGenOnIrodsOnS3Job + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + base_url: https://s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_use_s3: true + irods_use_davrods: true + davrods_owner_uid: "1450" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1450" + + post_tasks: + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_use_davrods: true + + benchmark_galaxy_1x1G_irods_on_isilon_backend: + type: GalaxyFileGenOnIrodsOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + path_to_files: /mnt/isilon/irods/home/rods + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + + benchmark_galaxy_1x1G_irods_on_netapp_backend: + type: GalaxyFileGenOnIrodsOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + path_to_files: /mnt/netapp/irods/home/rods + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + + benchmark_galaxy_1x1G_irods_on_s3_backend: + type: GalaxyFileGenOnIrodsOnS3Job + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + base_url: https://s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_use_s3: true + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server diff --git a/examples/06_benchmark_galaxy_1x1K.yml b/examples/06_benchmark_galaxy_1x1K.yml new file mode 100644 index 00000000..cdc5384e --- /dev/null +++ b/examples/06_benchmark_galaxy_1x1K.yml @@ -0,0 +1,242 @@ +config: + log_ansible_output: false + results_print: false + results_save_raw_results: false + results_save_to_file: true + +Default: &default + input: + num_files: 1 + file_size_in_bytes: 1024 + verification_timeout_in_s: 100 + export_volume: /mnt/local + + +benchmarks: + benchmark_galaxy_1x1K_isilon_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/isilon + galaxy_job: + <<: *default + path_to_files: /mnt/isilon/galaxy_in_progress/files + + benchmark_galaxy_1x1K_netapp_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/netapp + galaxy_job: + <<: *default + path_to_files: /mnt/netapp/galaxy_in_progress/files + + benchmark_galaxy_1x1K_s3_backend: + type: GalaxyFileGenOnS3Job + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + base_url: https://s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + + # benchmark_galaxy_1x1K_irods_via_fuse_on_isilon_backend: + # type: GalaxyFileGenOnMountVolumeJob + # repetitions: 1 + # destination: + # host: irods_client + # target_folder: /mnt/volume_under_test/irods-fuse + # galaxy_job: + # <<: *default + # path_to_files: /mnt/isilon/irods/home/rods/galaxy_in_progress/files + + # pre_tasks: + # - playbook: setup_irods_server.yml + # host: irods_server + # extra_vars: + # irods_host_volume: /mnt/isilon + # - playbook: setup_irods_fuse_client.yml + # host: irods_client + # extra_vars: + # irods_host_volume: /mnt/volume_under_test + # irods_server_host: "132.230.223.139" + # irods_host_volume_owner_uid: "1000" + # post_tasks: + # - playbook: cleanup_irods_server.yml + # host: irods_server + # extra_vars: + # irods_host_volume: /mnt/isilon + # - playbook: cleanup_irods_fuse_client.yml + # host: irods_client + # extra_vars: + # irods_host_volume: /mnt/volume_under_test + + benchmark_galaxy_1x1K_irods_via_davrods_on_isilon_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/volume_under_test/irods-webdav + galaxy_job: + <<: *default + path_to_files: /mnt/isilon/irods/home/rods/galaxy_in_progress/files + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + irods_use_davrods: true + davrods_owner_uid: "1450" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1450" + + post_tasks: + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + irods_use_davrods: true + + benchmark_galaxy_1x1K_irods_via_davrods_on_netapp_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/volume_under_test/irods-webdav + galaxy_job: + <<: *default + path_to_files: /mnt/netapp/irods/home/rods/galaxy_in_progress/files + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + irods_use_davrods: true + davrods_owner_uid: "1450" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1450" + + post_tasks: + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + irods_use_davrods: true + + benchmark_galaxy_1x1K_irods_via_davrods_on_s3_backend: + type: GalaxyFileGenOnIrodsOnS3Job + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + base_url: https://s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_use_s3: true + irods_use_davrods: true + davrods_owner_uid: "1450" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1450" + + post_tasks: + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_use_davrods: true + + benchmark_galaxy_1x1K_irods_on_isilon_backend: + type: GalaxyFileGenOnIrodsOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + path_to_files: /mnt/isilon/irods/home/rods + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + + benchmark_galaxy_1x1K_irods_on_netapp_backend: + type: GalaxyFileGenOnIrodsOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + path_to_files: /mnt/netapp/irods/home/rods + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + + benchmark_galaxy_1x1K_irods_on_s3_backend: + type: GalaxyFileGenOnIrodsOnS3Job + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + base_url: https://s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_use_s3: true + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server diff --git a/examples/06_benchmark_galaxy_1x1M.yml b/examples/06_benchmark_galaxy_1x1M.yml new file mode 100644 index 00000000..ddc73d77 --- /dev/null +++ b/examples/06_benchmark_galaxy_1x1M.yml @@ -0,0 +1,242 @@ +config: + log_ansible_output: false + results_print: false + results_save_raw_results: false + results_save_to_file: true + +Default: &default + input: + num_files: 1 + file_size_in_bytes: 1048576 + verification_timeout_in_s: 100 + export_volume: /mnt/local + + +benchmarks: + benchmark_galaxy_1x1M_isilon_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/isilon + galaxy_job: + <<: *default + path_to_files: /mnt/isilon/galaxy_in_progress/files + + benchmark_galaxy_1x1M_netapp_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/netapp + galaxy_job: + <<: *default + path_to_files: /mnt/netapp/galaxy_in_progress/files + + benchmark_galaxy_1x1M_s3_backend: + type: GalaxyFileGenOnS3Job + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + base_url: https://s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + + # benchmark_galaxy_1x1M_irods_via_fuse_on_isilon_backend: + # type: GalaxyFileGenOnMountVolumeJob + # repetitions: 1 + # destination: + # host: irods_client + # target_folder: /mnt/volume_under_test/irods-fuse + # galaxy_job: + # <<: *default + # path_to_files: /mnt/isilon/irods/home/rods/galaxy_in_progress/files + + # pre_tasks: + # - playbook: setup_irods_server.yml + # host: irods_server + # extra_vars: + # irods_host_volume: /mnt/isilon + # - playbook: setup_irods_fuse_client.yml + # host: irods_client + # extra_vars: + # irods_host_volume: /mnt/volume_under_test + # irods_server_host: "132.230.223.139" + # irods_host_volume_owner_uid: "1000" + # post_tasks: + # - playbook: cleanup_irods_server.yml + # host: irods_server + # extra_vars: + # irods_host_volume: /mnt/isilon + # - playbook: cleanup_irods_fuse_client.yml + # host: irods_client + # extra_vars: + # irods_host_volume: /mnt/volume_under_test + + benchmark_galaxy_1x1M_irods_via_davrods_on_isilon_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/volume_under_test/irods-webdav + galaxy_job: + <<: *default + path_to_files: /mnt/isilon/irods/home/rods/galaxy_in_progress/files + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + irods_use_davrods: true + davrods_owner_uid: "1450" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1450" + + post_tasks: + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + irods_use_davrods: true + + benchmark_galaxy_1x1M_irods_via_davrods_on_netapp_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/volume_under_test/irods-webdav + galaxy_job: + <<: *default + path_to_files: /mnt/netapp/irods/home/rods/galaxy_in_progress/files + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + irods_use_davrods: true + davrods_owner_uid: "1450" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1450" + + post_tasks: + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + irods_use_davrods: true + + benchmark_galaxy_1x1M_irods_via_davrods_on_s3_backend: + type: GalaxyFileGenOnIrodsOnS3Job + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + base_url: https://s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_use_s3: true + irods_use_davrods: true + davrods_owner_uid: "1450" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1450" + + post_tasks: + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_use_davrods: true + + benchmark_galaxy_1x1M_irods_on_isilon_backend: + type: GalaxyFileGenOnIrodsOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + path_to_files: /mnt/isilon/irods/home/rods + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + + benchmark_galaxy_1x1M_irods_on_netapp_backend: + type: GalaxyFileGenOnIrodsOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + path_to_files: /mnt/netapp/irods/home/rods + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + + benchmark_galaxy_1x1M_irods_on_s3_backend: + type: GalaxyFileGenOnIrodsOnS3Job + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + base_url: https://s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_use_s3: true + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server diff --git a/examples/06_benchmark_galaxy_2000x1K.yml b/examples/06_benchmark_galaxy_2000x1K.yml new file mode 100644 index 00000000..787566e2 --- /dev/null +++ b/examples/06_benchmark_galaxy_2000x1K.yml @@ -0,0 +1,242 @@ +config: + log_ansible_output: false + results_print: false + results_save_raw_results: false + results_save_to_file: true + +Default: &default + input: + num_files: 2000 + file_size_in_bytes: 1024 + verification_timeout_in_s: 3600 + export_volume: /mnt/local + + +benchmarks: + benchmark_galaxy_2000x1K_isilon_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/isilon + galaxy_job: + <<: *default + path_to_files: /mnt/isilon/galaxy_in_progress/files + + benchmark_galaxy_2000x1K_netapp_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/netapp + galaxy_job: + <<: *default + path_to_files: /mnt/netapp/galaxy_in_progress/files + + benchmark_galaxy_2000x1K_s3_backend: + type: GalaxyFileGenOnS3Job + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + base_url: https://s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + + # benchmark_galaxy_2000x1K_irods_via_fuse_on_isilon_backend: + # type: GalaxyFileGenOnMountVolumeJob + # repetitions: 1 + # destination: + # host: irods_client + # target_folder: /mnt/volume_under_test/irods-fuse + # galaxy_job: + # <<: *default + # path_to_files: /mnt/isilon/irods/home/rods/galaxy_in_progress/files + + # pre_tasks: + # - playbook: setup_irods_server.yml + # host: irods_server + # extra_vars: + # irods_host_volume: /mnt/isilon + # - playbook: setup_irods_fuse_client.yml + # host: irods_client + # extra_vars: + # irods_host_volume: /mnt/volume_under_test + # irods_server_host: "132.230.223.139" + # irods_host_volume_owner_uid: "1000" + # post_tasks: + # - playbook: cleanup_irods_server.yml + # host: irods_server + # extra_vars: + # irods_host_volume: /mnt/isilon + # - playbook: cleanup_irods_fuse_client.yml + # host: irods_client + # extra_vars: + # irods_host_volume: /mnt/volume_under_test + + benchmark_galaxy_2000x1K_irods_via_davrods_on_isilon_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/volume_under_test/irods-webdav + galaxy_job: + <<: *default + path_to_files: /mnt/isilon/irods/home/rods/galaxy_in_progress/files + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + irods_use_davrods: true + davrods_owner_uid: "1450" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1450" + + post_tasks: + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + irods_use_davrods: true + + benchmark_galaxy_2000x1K_irods_via_davrods_on_netapp_backend: + type: GalaxyFileGenOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + target_folder: /mnt/volume_under_test/irods-webdav + galaxy_job: + <<: *default + path_to_files: /mnt/netapp/irods/home/rods/galaxy_in_progress/files + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + irods_use_davrods: true + davrods_owner_uid: "1450" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1450" + + post_tasks: + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + irods_use_davrods: true + + benchmark_galaxy_2000x1K_irods_via_davrods_on_s3_backend: + type: GalaxyFileGenOnIrodsOnS3Job + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + base_url: https://s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_use_s3: true + irods_use_davrods: true + davrods_owner_uid: "1450" + - playbook: setup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + irods_server_host: "132.230.223.139" + davrods_owner_uid: "1450" + + post_tasks: + - playbook: cleanup_irods_webdav_client.yml + host: irods_client + extra_vars: + irods_host_volume: /mnt/volume_under_test + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_use_davrods: true + + benchmark_galaxy_2000x1K_irods_on_isilon_backend: + type: GalaxyFileGenOnIrodsOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + path_to_files: /mnt/isilon/irods/home/rods + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/isilon + + benchmark_galaxy_2000x1K_irods_on_netapp_backend: + type: GalaxyFileGenOnIrodsOnMountVolumeJob + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + path_to_files: /mnt/netapp/irods/home/rods + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server + extra_vars: + irods_host_volume: /mnt/netapp + + benchmark_galaxy_2000x1K_irods_on_s3_backend: + type: GalaxyFileGenOnIrodsOnS3Job + repetitions: 1 + destination: + host: irods_client + galaxy_job: + <<: *default + irods_server_host: "132.230.223.139" + base_url: https://s3.bwsfs.uni-freiburg.de + bucket_name: frct-smoe-bench-ec61-01 + + pre_tasks: + - playbook: setup_irods_server.yml + host: irods_server + extra_vars: + irods_use_s3: true + post_tasks: + - playbook: cleanup_irods_server.yml + host: irods_server diff --git a/examples/20_netapp_evaluation.yml b/examples/20_netapp_evaluation.yml new file mode 100644 index 00000000..a1db6ad8 --- /dev/null +++ b/examples/20_netapp_evaluation.yml @@ -0,0 +1,28 @@ +config: + log_ansible_output: false + results_print: false + results_save_to_file: true + +Default: &default + type: DdNetappRead + repetitions: 5 + destination: + host: irods_server + target_folder: /mnt/netapp/read_benchmark + dd: + blocksize: 1024k + + dim_key: blockcount + dim_values: + - 1024 + - 2048 + - 4096 + - 8192 + - 16384 + +benchmarks: + netapp_cold_read: + <<: *default + + netapp_hot_read: + <<: *default diff --git a/examples/20_prepare_netapp_evaluation.yml b/examples/20_prepare_netapp_evaluation.yml new file mode 100644 index 00000000..f8f5e6f2 --- /dev/null +++ b/examples/20_prepare_netapp_evaluation.yml @@ -0,0 +1,22 @@ +config: + log_ansible_output: false + results_print: false + results_save_to_file: false + +benchmarks: + setup_directory_struture: + type: DdPrepareNetappRead + repetitions: 5 + destination: + host: irods_server + target_folder: /mnt/netapp/read_benchmark + dd: + blocksize: 1024k + + dim_key: blockcount + dim_values: + - 1024 + - 2048 + - 4096 + - 8192 + - 16384 diff --git a/examples/99_evaluate_benchmark_tools.yml b/examples/99_evaluate_benchmark_tools.yml new file mode 100644 index 00000000..7b1385fd --- /dev/null +++ b/examples/99_evaluate_benchmark_tools.yml @@ -0,0 +1,93 @@ +config: + log_ansible_output: false + results_print: false + results_save_to_file: true + + +DefaultValues: &fio_defaults + type: FioFixedParams + repetitions: 5 + destinations: + - host: irods_server + target_folder: /mnt/netapp + + +benchmarks: + # fio_throughput_read: + # <<: *fio_defaults + # fio: + # mode: read + # blocksize: "1024k" + # numjobs: 4 + # iodepth: 32 + + # fio_throughput_write: + # <<: *fio_defaults + # fio: + # mode: write + # blocksize: "1024k" + # numjobs: 4 + # iodepth: 32 + + # fio_throughput_rw: + # <<: *fio_defaults + # fio: + # mode: rw + # blocksize: "1024k" + # numjobs: 4 + # iodepth: 32 + + # fio_iops_read: + # <<: *fio_defaults + # fio: + # mode: randread + # blocksize: "4k" + # numjobs: 4 + # iodepth: 32 + + # fio_iops_write: + # <<: *fio_defaults + # fio: + # mode: randwrite + # blocksize: "4k" + # numjobs: 4 + # iodepth: 32 + + # fio_iops_rw: + # <<: *fio_defaults + # fio: + # mode: randrw + # blocksize: "4k" + # numjobs: 4 + # iodepth: 32 + + # fio_latency_read: + # <<: *fio_defaults + # fio: + # mode: randread + # blocksize: "4k" + # numjobs: 1 + # iodepth: 1 + + # fio_latency_write: + # <<: *fio_defaults + # fio: + # mode: randwrite + # blocksize: "4k" + # numjobs: 1 + # iodepth: 1 + + # fio_latency_rw: + # <<: *fio_defaults + # fio: + # mode: randrw + # blocksize: "4k" + # numjobs: 1 + # iodepth: 1 + + dd_throughput_read: + type: DdBenchmark + repetitions: 5 + destinations: + - host: irods_server + target_folder: /mnt/netapp diff --git a/examples/99_evaluate_ram_cache_effect_on_iops.yml b/examples/99_evaluate_ram_cache_effect_on_iops.yml new file mode 100644 index 00000000..c13690ee --- /dev/null +++ b/examples/99_evaluate_ram_cache_effect_on_iops.yml @@ -0,0 +1,30 @@ +config: + log_ansible_output: true + results_print: false + + +benchmarks: + fio_iops_over_time: + type: FioOneDimParams + repetitions: 1 + destinations: + - host: irods_server + target_folder: /mnt/netapp + + fio: + mode: randread + blocksize: "4k" + numjobs: 4 + iodepth: 32 + filesize: "1G" + refill_buffers: false + + dim_key: runtime_in_s + dim_values: + - 10 + - 20 + - 40 + - 80 + - 160 + - 320 + - 640 diff --git a/galaxy_benchmarker/__main__.py b/galaxy_benchmarker/__main__.py index 6db237e1..20782958 100644 --- a/galaxy_benchmarker/__main__.py +++ b/galaxy_benchmarker/__main__.py @@ -1,68 +1,101 @@ -import yaml import argparse -import sys -import bioblend -from time import sleep -from benchmarker import Benchmarker import logging -import time -import requests import os -from requests.adapters import HTTPAdapter +from datetime import datetime +from pathlib import Path -logging.basicConfig() -log = logging.getLogger("GalaxyBenchmarker") -log.setLevel(logging.INFO) -log_handler = logging.StreamHandler(sys.stdout) -log_handler.setLevel(logging.DEBUG) +from serde.yaml import from_yaml -# Log to file -log_filename = r'logs/{filename}.log'.format(filename=time.time()) -os.makedirs(os.path.dirname(log_filename), exist_ok=True) -fh = logging.FileHandler(log_filename, mode='w') -log.addHandler(fh) +from galaxy_benchmarker.benchmarker import Benchmarker, BenchmarkerConfig, GlobalConfig +from galaxy_benchmarker.utils import ansible -s = requests.Session() -s.mount('http://', HTTPAdapter(max_retries=20)) - -def main(): +def main() -> None: parser = argparse.ArgumentParser() - parser.add_argument("--config", type=str, default="benchmark_config.yml", help="Path to config file") + parser.add_argument( + "--cfgs", nargs="+", default=[], help="Path(s) to config file(s)" + ) + parser.add_argument("--only-pre-tasks", dest="only_pre_tasks", action="store_true") + parser.add_argument("--only-benchmark", dest="only_benchmark", action="store_true") + parser.add_argument( + "--only-post-tasks", dest="only_post_tasks", action="store_true" + ) + parser.add_argument("--verbose", dest="verbose", action="store_true") + parser.set_defaults( + verbose=False, only_pre_tasks=False, only_benchmark=False, only_post_tasks=False + ) + parser.add_argument( + "--benchmarks", nargs="+", default=[], help="List of benchmark(s)" + ) + args = parser.parse_args() - log.debug("Loading Configuration from file {filename}".format(filename=args.config)) - with open(args.config, "r") as stream: - try: - config = yaml.safe_load(stream) + log = configure_logger(args.verbose) + + for config_name in args.cfgs: + cfg_path = Path(config_name) + if not cfg_path.is_file(): + raise ValueError(f"Path to config '{config_name}' is not a file") + + log.info( + "Loading Configuration from file {filename}".format(filename=config_name) + ) + + cfg = from_yaml(GlobalConfig, cfg_path.read_text()) + + ansible.AnsibleTask.register(cfg.tasks or {}) + + log.info("Initializing Benchmarker.") + benchmarker = Benchmarker(cfg.config or BenchmarkerConfig(), cfg.benchmarks) - log.info("Initializing Benchmarker.") - benchmarker = Benchmarker(config) + log.info("Start benchmarker.") + if args.only_pre_tasks: + flags = (True, False, False) + elif args.only_benchmark: + flags = (False, True, False) + elif args.only_post_tasks: + flags = (False, False, True) + else: + flags = (True, True, True) - benchmarker.run_pre_tasks() + pre, bench, post = flags + benchmarker.run( + run_pretasks=pre, + run_benchmarks=bench, + run_posttasks=post, + filter_benchmarks=args.benchmarks, + ) - log.info("Starting to run benchmarks.") - try: - benchmarker.run() - except bioblend.ConnectionError: - log.error("There was a problem with the connection. Benchmark canceled.") - results_filename = "results/results_{time}".format(time=time.time()) - log.info("Saving results to file: '{filename}.json'.".format(filename=results_filename)) - os.makedirs(os.path.dirname(results_filename), exist_ok=True) - benchmarker.save_results(results_filename) +def configure_logger(verbose: bool) -> logging.Logger: + # Formatter + fmt_with_time = logging.Formatter( + "%(asctime)s - %(name)s - %(levelname)s - %(message)s" + ) + fmt_no_time = logging.Formatter("%(name)s - %(levelname)s - %(message)s") - if benchmarker.inflx_db is not None: - log.info("Sending results to influxDB.") - benchmarker.send_results_to_influxdb() + # Create logger + log = logging.getLogger("galaxy_benchmarker") + if verbose: + log.setLevel(logging.DEBUG) + else: + log.setLevel(logging.INFO) - benchmarker.run_post_tasks() + # Create console handler + stream_handler = logging.StreamHandler() + stream_handler.setLevel(logging.DEBUG) + stream_handler.setFormatter(fmt_no_time) + log.addHandler(stream_handler) - except yaml.YAMLError as exc: - print(exc) - except IOError as err: - print(err) + # Create file handler + log_filename = f"logs/{datetime.now().replace(microsecond=0).isoformat()}.log" + os.makedirs(os.path.dirname(log_filename), exist_ok=True) + file_handler = logging.FileHandler(log_filename, mode="w") + file_handler.setLevel(logging.DEBUG) + file_handler.setFormatter(fmt_with_time) + log.addHandler(file_handler) + return log -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/galaxy_benchmarker/ansible_bridge.py b/galaxy_benchmarker/ansible_bridge.py deleted file mode 100644 index 747ba67f..00000000 --- a/galaxy_benchmarker/ansible_bridge.py +++ /dev/null @@ -1,17 +0,0 @@ -import os -import subprocess -from typing import Dict - - -def run_playbook(playbook_path, host, user, private_key, values: Dict = None): - """ - Run ansible-playbook with the given parameters. Additional variables can be given in values as a dict. - """ - commands = ["ansible-playbook", playbook_path, "-i", host+",", "-u", user, "--private-key", private_key] - if values is not None: - for key, value in values.items(): - commands.append("-e") - commands.append(key + "=" + value) - - with open(os.devnull, 'w') as devnull: - subprocess.check_call(commands) diff --git a/galaxy_benchmarker/benchmark.py b/galaxy_benchmarker/benchmark.py deleted file mode 100644 index 56ced3c9..00000000 --- a/galaxy_benchmarker/benchmark.py +++ /dev/null @@ -1,528 +0,0 @@ -""" -Definition of different benchmark-types. -""" -import logging -import time -import threading -import random -from datetime import datetime -from destination import BaseDestination, GalaxyDestination, PulsarMQDestination, GalaxyCondorDestination, CondorDestination -from workflow import BaseWorkflow, GalaxyWorkflow, CondorWorkflow -from task import BaseTask, AnsiblePlaybookTask, BenchmarkerTask -from typing import List, Dict, Union -from task import configure_task -from influxdb_bridge import InfluxDB -from bioblend import ConnectionError - - -log = logging.getLogger("GalaxyBenchmarker") - - -class BaseBenchmark: - """ - The Base-Class of Benchmark. All Benchmarks should inherit from it. - """ - allowed_dest_types = [] - allowed_workflow_types = [] - benchmarker = None - galaxy = None - benchmark_results = dict() - pre_tasks: List[BaseTask] = None - post_tasks: List[BaseTask] = None - - def __init__(self, name, benchmarker, destinations: List[BaseDestination], - workflows: List[BaseWorkflow], runs_per_workflow=1): - self.name = name - self.benchmarker = benchmarker - self.uuid = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f") + "_" + name - self.destinations = destinations - self.workflows = workflows - self.runs_per_workflow = runs_per_workflow - - def run_pre_task(self): - """ - Runs a Task before starting the actual Benchmark. - """ - if self.pre_tasks is None: - return - for task in self.pre_tasks: - log.info("Running task {task}".format(task=task)) - task.run() - - def run_post_task(self): - """ - Runs a Task after Benchmark finished (useful for cleaning up etc.). - """ - if self.post_tasks is None: - return - for task in self.post_tasks: - log.info("Running task {task}".format(task=task)) - task.run() - - def run(self, benchmarker): - raise NotImplementedError - - def save_results_to_influxdb(self, inflxdb: InfluxDB): - """ - Sends all the metrics of the benchmark_results to influxDB. - """ - for run_type, per_dest_results in self.benchmark_results.items(): - for dest_name, workflows in per_dest_results.items(): - for workflow_name, runs in workflows.items(): - for run in runs: - if run is None: - continue - - if "workflow_metrics" in run: - # Save metrics per workflow-run - tags = { - "benchmark_name": self.name, - "benchmark_uuid": self.uuid, - "benchmark_type": type(self), - "destination_name": dest_name, - "workflow_name": workflow_name, - "history_name": run["history_name"] if "history_name" in run else None, - "run_type": run_type, - } - - inflxdb.save_workflow_metrics(tags, run["workflow_metrics"]) - - # Save job-metrics if workflow succeeded - if runs is None or run["status"] == "error" or "jobs" not in run or run["jobs"] is None: - continue - - for job in run["jobs"].values(): - tags = { - "benchmark_name": self.name, - "benchmark_uuid": self.uuid, - "benchmark_type": type(self), - "destination_name": dest_name, - "workflow_name": workflow_name, - "history_name": run["history_name"] if "history_name" in run else None, - "run_type": run_type, - } - inflxdb.save_job_metrics(tags, job) - - def __str__(self): - return self.name - - __repr__ = __str__ - - -class ColdWarmBenchmark(BaseBenchmark): - allowed_dest_types = [GalaxyDestination, PulsarMQDestination] - allowed_workflow_types = [GalaxyWorkflow] - cold_pre_task: AnsiblePlaybookTask = None - warm_pre_task: AnsiblePlaybookTask = None - - def __init__(self, name, benchmarker, destinations: List[Union[PulsarMQDestination, GalaxyDestination]], - workflows: List[GalaxyWorkflow], galaxy, runs_per_workflow=1): - super().__init__(name, benchmarker, destinations, workflows, runs_per_workflow) - self.destinations = destinations - self.workflows = workflows - self.galaxy = galaxy - - def run(self, benchmarker): - """ - Runs the Workflow on each Destinations. First runs all Workflows "cold" (cleaning Pulsar up before each run), - after that the "warm"-runs begin. - """ - - for run_type in ["cold", "warm"]: - try: - self.benchmark_results[run_type] = run_galaxy_benchmark(self, benchmarker.glx, self.destinations, - self.workflows, - self.runs_per_workflow, run_type) - except KeyboardInterrupt as e: - self.benchmark_results[run_type] = e.args[0] - break - - -class DestinationComparisonBenchmark(BaseBenchmark): - allowed_dest_types = [GalaxyDestination, PulsarMQDestination, GalaxyCondorDestination] - allowed_workflow_types = [GalaxyWorkflow] - - def __init__(self, name, benchmarker, destinations: List[Union[PulsarMQDestination, GalaxyDestination]], - workflows: List[GalaxyWorkflow], galaxy, runs_per_workflow=1, warmup=True): - super().__init__(name, benchmarker, destinations, workflows, runs_per_workflow) - self.destinations = destinations - self.workflows = workflows - self.galaxy = galaxy - self.warmup = warmup - - def run(self, benchmarker): - """ - Runs the Workflows on each Destination. Uses "warm"-run, so for each Destination and Workflow, each Workflow - runs for the first time without consideration (so Pulsar has opportunity to install all tools) to not spoil - any metrics. - """ - try: - self.benchmark_results["warm"] = run_galaxy_benchmark(self, benchmarker.glx, self.destinations, - self.workflows, - self.runs_per_workflow, "warm", self.warmup) - except KeyboardInterrupt as e: - self.benchmark_results["warm"] = e.args[0] - - -class BurstBenchmark(BaseBenchmark): - allowed_dest_types = [GalaxyDestination, GalaxyCondorDestination, PulsarMQDestination, CondorDestination] - allowed_workflow_types = [GalaxyWorkflow, CondorWorkflow] - - background_tasks: List[Dict] = list() - - def __init__(self, name, benchmarker, destinations: List[BaseDestination], - workflows: List[BaseWorkflow], runs_per_workflow=1, burst_rate=1): - super().__init__(name, benchmarker, destinations, workflows, runs_per_workflow) - self.burst_rate = burst_rate - - if len(self.destinations) != 1: - raise ValueError("BurstBenchmark can only be used with exactly one Destination.") - - if len(self.workflows) != 1: - raise ValueError("BurstBenchmark can only be used with exactly one Workflow.") - - self.destination_type = type(self.destinations[0]) - - if self.destination_type is CondorDestination: - self.workflows: List[CondorWorkflow] - self.destinations: List[CondorDestination] - - # Deploy Workflow to Destination - for destination in self.destinations: - for workflow in self.workflows: - if type(workflow) is not CondorWorkflow: - raise ValueError("CondorDestination can only work with CondorWorkflow!") - - destination.deploy_workflow(workflow) - - def run(self, benchmarker): - background_task_process = self.BackgroundTaskThread(self) - background_task_process.start() - - threads = [] - results = [None]*self.runs_per_workflow - total_runs = next_runs = 0 - while total_runs < self.runs_per_workflow: - next_runs += self.burst_rate - # If burst_rate < 1, workflow should be run less than 1x per second. So just wait, until next_runs > 1 - if next_runs < 1: - time.sleep(1) - continue - - # Make sure, runs_per_workflow won't be exceeded - if total_runs + next_runs >= self.runs_per_workflow: - next_runs = self.runs_per_workflow - total_runs - - for _ in range(0, int(next_runs)): - process = self.BurstThread(self, total_runs, results) - process.start() - threads.append(process) - total_runs += 1 - - next_runs = 0 - time.sleep(1) - - # Wait for all Benchmarks being executed - finished_jobs = 0 - for process in threads: - process.join() - finished_jobs += 1 - log.info("{finished} out of {total} workflows are finished.".format(finished=finished_jobs, - total=total_runs)) - background_task_process.stop = True - - self.benchmark_results = { - "warm": { - self.destinations[0].name: { - self.workflows[0].name: results - } - } - } - - class BackgroundTaskThread(threading.Thread): - stop = False - - def __init__(self, bm): - threading.Thread.__init__(self) - self.bm = bm - - def run(self): - if len(self.bm.background_tasks) == 0: - return - - log.info("Starting to run BackgroundTaskThread") - for task in self.bm.background_tasks: - task["next_run"] = time.monotonic() + task["first_run_after"] - if "run_until" in task: - task["run_until"] += time.monotonic() - - while True: - for task in self.bm.background_tasks: - if task["next_run"] <= time.monotonic(): - log.info("Running background task {task}".format(task=task)) - task["task"].run() - task["next_run"] = time.monotonic() + task["run_every"] - if "run_until" in task: - if task["run_until"] <= time.monotonic() and task["next_run"] < float("inf"): - task["next_run"] = float("inf") - log.info("Stopped background task {task}, as run_until passed".format(task=task)) - if self.stop: - break - - time.sleep(1) - - class BurstThread(threading.Thread): - """ - Class to run a Workflow within a thread to allow multiple runs a the same time. - """ - def __init__(self, bm, thread_id, results: List): - threading.Thread.__init__(self) - self.bm = bm - self.thread_id = thread_id - self.results = results - - def run(self): - """ - Runs a GalaxyWorkflow or a CondorWorkflow. - """ - log.info("Running with thread_id {thread_id}".format(thread_id=self.thread_id)) - if self.bm.destination_type is PulsarMQDestination: - try: - res = run_galaxy_benchmark(self, self.bm.galaxy, self.bm.destinations, self.bm.workflows, - 1, "warm", False) - self.results[self.thread_id] = res[self.bm.destinations[0].name][self.bm.workflows[0].name][0] # TODO: Handle error-responses - except ConnectionError: - log.error("ConnectionError!") - self.results[self.thread_id] = {"status": "error"} - - if self.bm.destination_type is CondorDestination: - for destination in self.bm.destinations: - for workflow in self.bm.workflows: - result = destination.run_workflow(workflow) - result["history_name"] = str(time.time_ns()) + str(random.randrange(0, 99999)) - result["workflow_metrics"] = { - "status": { - "name": "workflow_status", - "type": "string", - "plugin": "benchmarker", - "value": result["status"] - }, - "total_runtime": { - "name": "total_workflow_runtime", - "type": "float", - "plugin": "benchmarker", - "value": result["total_workflow_runtime"] - }, - "submit_time": { - "name": "submit_time", - "type": "float", - "plugin": "benchmarker", - "value": result["submit_time"] - } - } - - self.results[self.thread_id] = result - - -def run_galaxy_benchmark(benchmark, galaxy, destinations: List[PulsarMQDestination], - workflows: List[GalaxyWorkflow], runs_per_workflow=1, run_type="warm", warmup=True): - """ - Runs the given list of Workflows on the given list of Destinations as a cold or warm benchmark on a - PulsarMQDestination for runs_per_workflow times. Handles failures too and retries up to one time. - """ - if run_type not in ["cold", "warm"]: - raise ValueError("'run_type' must be of type 'cold' or 'warm'.") - - benchmark_results = dict() - - # Add +1 to warm up Pulsar, if run_type is "warm" and warmup should happen - if warmup and run_type == "warm": - runs_per_workflow += 1 - - log.info("Starting to run {type} benchmarks.".format(type=run_type)) - try: - for destination in destinations: - benchmark_results[destination.name] = dict() - - log.info("Running {type} benchmark for destination: {dest}.".format(type=run_type, dest=destination.name)) - for workflow in workflows: - benchmark_results[destination.name][workflow.name] = list() - retries = 0 - i = 0 - while i < runs_per_workflow: - if warmup and run_type == "warm" and i == 0: - log.info("First run! Warming up. Results won't be considered for the first time.") - result = destination.run_workflow(workflow) - if result["status"] == "error": - retries += 1 - else: - if run_type == "cold" and benchmark.cold_pre_task is not None: - log.info("Running cold pre-task for Cleanup.") - destination.run_task(benchmark.cold_pre_task) - - log.info("Running {type} '{workflow}' for the {i} time on {dest}.".format(type=run_type, - workflow=workflow.name, - i=i + 1, - dest=destination.name)) - result = destination.run_workflow(workflow) - - if "history_name" in result and result["status"] == "success": - result["jobs"] = destination.get_jobs(result["history_name"]) - - result["workflow_metrics"] = { - "status": { - "name": "workflow_status", - "type": "string", - "plugin": "benchmarker", - "value": result["status"] - }, - "total_runtime": { - "name": "total_workflow_runtime", - "type": "float", - "plugin": "benchmarker", - "value": result["total_workflow_runtime"] - } - } - - log.info("Finished running '{workflow}' with status '{status}' in {time} seconds." - .format(workflow=workflow.name, status=result["status"], - time=result["total_workflow_runtime"])) - - # Handle possible errors and maybe retry - if result["status"] == "error": - log.info("Result won't be considered.") - - if retries < 4: - retry_wait = 60 * 2 ** retries - log.info("Retrying after {wait} seconds..".format(wait=retry_wait)) - time.sleep(retry_wait) - retries += 1 - i -= 1 - # If too many retries, continue with next workflow - else: - break - else: - benchmark_results[destination.name][workflow.name].append(result) - retries = 0 - - i += 1 - except KeyboardInterrupt: - log.info("Received KeyboardInterrupt. Stopping benchmark and saving current results.") - # So previous results are saved - raise KeyboardInterrupt(benchmark_results) - - return benchmark_results - - -def configure_benchmark(bm_config: Dict, destinations: Dict, workflows: Dict, glx, benchmarker) -> BaseBenchmark: - """ - Initializes and configures a Benchmark according to the given configuration. Returns the configured Benchmark. - """ - # Check, if all set properly - if bm_config["type"] not in ["ColdvsWarm", "DestinationComparison", "Burst"]: - raise ValueError("Benchmark-Type '{type}' not valid".format(type=bm_config["type"])) - - runs_per_workflow = bm_config["runs_per_workflow"] if "runs_per_workflow" in bm_config else 1 - - if bm_config["type"] == "ColdvsWarm": - benchmark = ColdWarmBenchmark(bm_config["name"], benchmarker, - _get_needed_destinations(bm_config, destinations, ColdWarmBenchmark), - _get_needed_workflows(bm_config, workflows, ColdWarmBenchmark), glx, - runs_per_workflow) - benchmark.galaxy = glx - if "cold_pre_task" in bm_config: - if bm_config["cold_pre_task"]["type"] == "AnsiblePlaybook": - benchmark.cold_pre_task = AnsiblePlaybookTask(benchmark, bm_config["cold_pre_task"]["playbook"]) - - if "warm_pre_task" in bm_config: - if bm_config["warm_pre_task"]["type"] == "AnsiblePlaybook": - benchmark.warm_pre_task = AnsiblePlaybookTask(benchmark, bm_config["warm_pre_task"]["playbook"]) - - if bm_config["type"] == "DestinationComparison": - warmup = True if "warmup" not in bm_config else bm_config["warmup"] - benchmark = DestinationComparisonBenchmark(bm_config["name"], benchmarker, - _get_needed_destinations(bm_config, destinations, - DestinationComparisonBenchmark), - _get_needed_workflows(bm_config, workflows, - DestinationComparisonBenchmark), - glx, runs_per_workflow, warmup) - - if bm_config["type"] == "Burst": - benchmark = BurstBenchmark(bm_config["name"], benchmarker, - _get_needed_destinations(bm_config, destinations, BurstBenchmark), - _get_needed_workflows(bm_config, workflows, BurstBenchmark), - runs_per_workflow, bm_config["burst_rate"]) - benchmark.galaxy = glx - - if "background_tasks" in bm_config: - benchmark.background_tasks = list() - for task_conf in bm_config["background_tasks"]: - task_conf["task"] = configure_task(task_conf, benchmark) - benchmark.background_tasks.append(task_conf) - - if "pre_tasks" in bm_config: - benchmark.pre_tasks = list() - for task in bm_config["pre_tasks"]: - if task["type"] == "AnsiblePlaybook": - benchmark.pre_tasks.append(AnsiblePlaybookTask(benchmark, task["playbook"])) - elif task["type"] == "BenchmarkerTask": - benchmark.pre_tasks.append(BenchmarkerTask(benchmark, task["name"])) - else: - raise ValueError("Task of type '{type}' is not supported".format(type=task["type"])) - - if "post_tasks" in bm_config: - benchmark.post_tasks = list() - for task in bm_config["post_tasks"]: - if task["type"] == "AnsiblePlaybook": - benchmark.post_tasks.append(AnsiblePlaybookTask(benchmark, task["playbook"])) - elif task["type"] == "BenchmarkerTask": - benchmark.post_tasks.append(BenchmarkerTask(benchmark, task["name"])) - else: - raise ValueError("Task of type '{type}' is not supported".format(type=task["type"])) - - return benchmark - - -def _get_needed_destinations(bm_config: Dict, destinations: Dict, bm_type) -> List: - """ - Returns a list of the destinations that were set in the configuration of the benchmark. - """ - if "destinations" not in bm_config or bm_config["destinations"] is None or len(bm_config["destinations"]) == 0: - raise ValueError("No destination set in benchmark '{name}'".format(name=bm_config["name"])) - - needed_destinations = list() - for dest_name in bm_config["destinations"]: - # Make sure, that destination exists - if dest_name not in destinations: - raise ValueError("Destination '{name}' not set in workflows-configuration.".format(name=dest_name)) - # Make sure, that destination-type is allowed - if type(destinations[dest_name]) not in bm_type.allowed_dest_types: - raise ValueError("Destination-Type {dest} is not allowed in benchmark-type {bm}. \ - Error in benchmark name {bm_name}".format(dest=type(destinations[dest_name]), - bm=type(bm_type), bm_name=bm_config["name"])) - needed_destinations.append(destinations[dest_name]) - - return needed_destinations - - -def _get_needed_workflows(bm_config: Dict, workflows: Dict, bm_type) -> List: - """ - Returns a list of the workflows that were set in the configuration of the benchmark. - """ - if "workflows" not in bm_config or bm_config["workflows"] is None or len(bm_config["workflows"]) == 0: - raise ValueError("No workflow set in benchmark '{name}'".format(name=bm_config["name"])) - - needed_workflows = list() - for wf_name in bm_config["workflows"]: - # Make sure, that workflow exists - if wf_name not in workflows: - raise ValueError("Workflow '{name}' not set in workflows-configuration.".format(name=wf_name)) - # Make sure, that workflow-type is allowed - if type(workflows[wf_name]) not in bm_type.allowed_workflow_types: - raise ValueError("Workflow-Type {wf} is not allowed in benchmark-type {bm}. \ - Error in benchmark name {bm_name}".format(wf=type(workflows[wf_name]), - bm=type(bm_type), - bm_name=bm_config["name"])) - needed_workflows.append(workflows[wf_name]) - - return needed_workflows diff --git a/galaxy_benchmarker/benchmarker.py b/galaxy_benchmarker/benchmarker.py index ce5f6b96..7da146d1 100644 --- a/galaxy_benchmarker/benchmarker.py +++ b/galaxy_benchmarker/benchmarker.py @@ -1,94 +1,130 @@ -from typing import Dict -import workflow -import destination -import benchmark -from galaxy_bridge import Galaxy -import logging -import json -from influxdb_bridge import InfluxDB -from openstack_bridge import OpenStackCompute - -log = logging.getLogger("GalaxyBenchmarker") - - -class Benchmarker: - glx: Galaxy - inflx_db: InfluxDB - workflows: Dict[str, workflow.BaseWorkflow] - destinations: Dict[str, destination.BaseDestination] - benchmarks: Dict[str, benchmark.BaseBenchmark] - - def __init__(self, config): - glx_conf = config["galaxy"] - self.glx = Galaxy(glx_conf["url"], glx_conf["user_key"], glx_conf.get("shed_install", False), - glx_conf.get("ssh_user", None), glx_conf.get("ssh_key", None), - glx_conf.get("galaxy_root_path", None), glx_conf.get("galaxy_config_dir", None), - glx_conf.get("galaxy_user", None)) - - if "influxdb" in config: - inf_conf = config["influxdb"] - self.inflx_db = InfluxDB(inf_conf["host"], inf_conf["port"], inf_conf["username"], inf_conf["password"], - inf_conf["db_name"]) - else: - self.inflx_db = None +from __future__ import annotations - if "openstack" in config: - os_conf = config["openstack"] - self.openstack = OpenStackCompute(os_conf["auth_url"], os_conf["compute_endpoint_version"], - os_conf["username"], os_conf["password"], os_conf["project_id"], - os_conf["region_name"], os_conf["user_domain_name"]) - - self.workflows = dict() - for wf_config in config["workflows"]: - self.workflows[wf_config["name"]] = workflow.configure_workflow(wf_config) - - self.destinations = dict() - for dest_config in config["destinations"]: - self.destinations[dest_config["name"]] = destination.configure_destination(dest_config, self.glx) - - self.benchmarks = dict() - for bm_config in config["benchmarks"]: - self.benchmarks[bm_config["name"]] = benchmark.configure_benchmark(bm_config, self.destinations, - self.workflows, self.glx, self) +import logging +import signal +from dataclasses import dataclass +from datetime import datetime +from pathlib import Path +from types import FrameType +from typing import Optional - if glx_conf.get("configure_job_destinations", False): - log.info("Creating job_conf for Galaxy and deploying it") - destination.create_galaxy_job_conf(self.glx, self.destinations) - self.glx.deploy_job_conf() +from serde import serde - if glx_conf["shed_install"]: - self.glx.install_tools_for_workflows(list(self.workflows.values())) +from galaxy_benchmarker.benchmarks.base import Benchmark +from galaxy_benchmarker.typing import NamedConfigDicts +from galaxy_benchmarker.utils import ansible - def run_pre_tasks(self): - log.info("Running pre-tasks for benchmarks") - for bm in self.benchmarks.values(): - bm.run_pre_task() +log = logging.getLogger(__name__) - def run_post_tasks(self): - log.info("Running post-tasks for benchmarks") - for bm in self.benchmarks.values(): - bm.run_post_task() - def run(self): - for bm in self.benchmarks.values(): - log.info("Running benchmark '{bm_name}'".format(bm_name=bm.name)) - bm.run(self) +@serde +@dataclass +class BenchmarkerConfig: + results_path: str = "results/" + results_save_to_file: bool = True + results_save_raw_results: bool = False + results_print: bool = True - def get_results(self): - for bm in self.benchmarks.values(): - print(bm.benchmark_results) + log_ansible_output: bool = False - def save_results(self, filename="results"): - results = list() - for bm in self.benchmarks.values(): - results.append(bm.benchmark_results) - json_results = json.dumps(results, indent=2) - with open(filename+".json", "w") as fh: - fh.write(json_results) +@serde +@dataclass +class GlobalConfig: + config: Optional[BenchmarkerConfig] - def send_results_to_influxdb(self): - for bm in self.benchmarks.values(): - bm.save_results_to_influxdb(self.inflx_db) + tasks: Optional[NamedConfigDicts] + benchmarks: NamedConfigDicts +class Benchmarker: + def __init__(self, config: BenchmarkerConfig, benchmarks: NamedConfigDicts): + self.config = config + self.current_benchmark: Optional[Benchmark] = None + + self.benchmarks: list[Benchmark] = [] + for name, b_config in benchmarks.items(): + self.benchmarks.append(Benchmark.create(name, b_config, self)) + + self.results = Path(config.results_path) + if self.results.exists(): + if not self.results.is_dir(): + raise ValueError("'results_path' has to be a folder") + else: + self.results.mkdir(parents=True) + + if config.log_ansible_output: + ansible.LOG_ANSIBLE_OUTPUT = True + + # Safe results in case of interrupt + def handle_signal(signum: int, frame: Optional[FrameType]) -> None: + ansible.stop_playbook(signum) + self.save_results_of_current_benchmark() + exit(0) + + signal.signal(signal.SIGINT, handle_signal) + + def run( + self, + run_pretasks: bool = True, + run_benchmarks: bool = True, + run_posttasks: bool = True, + filter_benchmarks: list[str] = [], + ) -> None: + """Run all benchmarks sequentially + + Steps: + - Run pre_tasks + - Run benchmark + - Print results (optional) + - Save results to file (optional) + - Run post_tasks + """ + + for i, benchmark in enumerate(self.benchmarks): + self.current_benchmark = benchmark + current_run = f"({i+1}/{len(self.benchmarks)})" + + if filter_benchmarks and benchmark.name not in filter_benchmarks: + log.info("%s Skipping %s", current_run, benchmark.name) + continue + + try: + if run_pretasks: + log.info("%s Pre task for %s", current_run, benchmark.name) + benchmark.run_pre_tasks() + + if run_benchmarks: + log.info("%s Start run for %s", current_run, benchmark.name) + benchmark.run() + except Exception as e: + log.exception( + "Benchmark run failed with exception. Continuing with next benchmark" + ) + + if run_benchmarks: + # Save results only when actual benchmark ran + self.save_results_of_current_benchmark() + + if run_posttasks: + log.info("%s Post task for %s", current_run, benchmark.name) + benchmark.run_post_tasks() + + time = datetime.now().replace(microsecond=0).isoformat() + log.info("%s Finished benchmark run at %s", current_run, time) + + def save_results_of_current_benchmark(self) -> None: + """Save the result of the current benchmark + + Extracted as function for SIGNAL-handling""" + if not self.current_benchmark: + log.warning("Nothing to save.") + return + + if self.config.results_print: + print(f"#### Results for benchmark {self.current_benchmark}") + print(self.current_benchmark.benchmark_results) + + if self.config.results_save_to_file: + file = self.current_benchmark.save_results_to_file(self.results) + log.info("Saving results to file: '%s'.", file) diff --git a/galaxy_benchmarker/benchmarks/__init__.py b/galaxy_benchmarker/benchmarks/__init__.py new file mode 100644 index 00000000..e668db7d --- /dev/null +++ b/galaxy_benchmarker/benchmarks/__init__.py @@ -0,0 +1,8 @@ +from galaxy_benchmarker.benchmarks.base import * +from galaxy_benchmarker.benchmarks.dd import * +from galaxy_benchmarker.benchmarks.fio import * +from galaxy_benchmarker.benchmarks.galaxy import * +from galaxy_benchmarker.benchmarks.mdtest import * +from galaxy_benchmarker.benchmarks.s3benchmark import * +from galaxy_benchmarker.benchmarks.utils import * +from galaxy_benchmarker.benchmarks.warp import * diff --git a/galaxy_benchmarker/benchmarks/base.py b/galaxy_benchmarker/benchmarks/base.py new file mode 100644 index 00000000..b7f71ee7 --- /dev/null +++ b/galaxy_benchmarker/benchmarks/base.py @@ -0,0 +1,253 @@ +from __future__ import annotations + +import dataclasses +import json +import logging +import tempfile +import time +from datetime import datetime +from pathlib import Path +from typing import TYPE_CHECKING, Type, TypeVar + +from galaxy_benchmarker.typing import BenchmarkResults +from galaxy_benchmarker.utils.ansible import AnsibleTask + +if TYPE_CHECKING: + from galaxy_benchmarker.benchmarker import Benchmarker + +log = logging.getLogger(__name__) + + +_registered_benchmarks: dict[str, Type["Benchmark"]] = {} + + +SubClass = TypeVar("SubClass", bound="Type[Benchmark]") + + +def register_benchmark(cls: SubClass) -> SubClass: + """Register a benchmark for factory method""" + + name = cls.__name__ + log.debug("Registering benchmark %s", name) + + if name in _registered_benchmarks: + module = cls.__module__ + raise ValueError(f"Already registered. Use another name for {module}.{name}") + + _registered_benchmarks[name] = cls + + return cls + + +class BenchmarkConfig: + def asdict(self): + return {k: v for k, v in dataclasses.asdict(self).items() if v is not None} + + +class Benchmark: + """ + The Base-Class of Benchmark. All Benchmarks should inherit from it. + """ + + def __init__(self, name: str, config: dict, benchmarker: Benchmarker): + self.name = name + self.benchmarker = benchmarker + + repetitions = config.get("repetitions", None) + if not repetitions: + raise ValueError(f"'repetitions' property is missing for '{name}'") + + try: + self.repetitions = int(repetitions) + except ValueError as e: + raise ValueError(f"'repetitions' has to be a number for '{name}'") from e + + self.id = datetime.now().replace(microsecond=0).isoformat() + self.benchmark_results: BenchmarkResults = {} + self.config = BenchmarkConfig() + + # Parse pre tasks + self._pre_tasks: list[AnsibleTask] = [] + if "pre_task" in config: + if "pre_tasks" in config: + raise ValueError(f"'pre_task' and 'pre_tasks' given for '{name}'") + + pre_task = AnsibleTask.from_config(config["pre_task"], f"{name}_pre_task") + self._pre_tasks.append(pre_task) + elif "pre_tasks" in config: + for i, t_config in enumerate(config.get("pre_tasks", [])): + pre_task = AnsibleTask.from_config(t_config, f"{name}_pre_task_{i}") + self._pre_tasks.append(pre_task) + + # Parse post tasks + self._post_tasks: list[AnsibleTask] = [] + if "post_task" in config: + if "post_tasks" in config: + raise ValueError(f"'post_task' and 'post_tasks' given for '{name}'") + + post_task = AnsibleTask.from_config( + config["post_task"], f"{name}_post_task" + ) + self._post_tasks.append(post_task) + elif "post_tasks" in config: + for i, t_config in enumerate(config.get("post_tasks", [])): + post_task = AnsibleTask.from_config(t_config, f"{name}_post_task_{i}") + self._post_tasks.append(post_task) + + @staticmethod + def create(name: str, config: dict, benchmarker: Benchmarker): + """Factory method for benchmarks + + name: benchmark name + config: benchmark specific config + global_config: global config for lookups + """ + + benchmark_type = config.get("type", None) + if benchmark_type is None: + raise ValueError(f"'type' property is missing for benchmark {config}") + + if benchmark_type not in _registered_benchmarks: + raise ValueError(f"Unkown benchmark type: {benchmark_type}") + + benchmark_class = _registered_benchmarks[benchmark_type] + return benchmark_class(name, config, benchmarker) + + def run_pre_tasks(self) -> None: + """Run setup tasks""" + for task in self._pre_tasks: + task.run() + + def run_post_tasks(self) -> None: + """Run clean up task""" + for task in self._post_tasks: + task.run() + + def run(self): + """Run benchmark""" + + with tempfile.TemporaryDirectory() as temp_dir: + log.info("Start %s", self.name) + self.benchmark_results[self.name] = [] + for i in range(self.repetitions): + log.info("Run %d of %d", i + 1, self.repetitions) + result_file = Path(temp_dir) / f"{self.name}_{i}.json" + + result = self._run_at(result_file, i, self.config) + self.benchmark_results[self.name].append(result) + + def _run_at( + self, result_file: Path, repetition: int, config: BenchmarkConfig + ) -> dict: + raise NotImplementedError( + "Benchmark._run_at is not defined. Overwrite in child class" + ) + + def save_results_to_file(self, directory: Path) -> str: + """Write all metrics to a file.""" + file = directory / self.result_file.name + results = {"tags": self.get_tags(), "results": self.benchmark_results} + json_results = json.dumps(results, indent=2) + file.write_text(json_results) + + return str(file) + + @property + def result_file(self) -> Path: + return Path(f"{self.id}_{self.name}.json") + + def get_tags(self) -> dict[str, str]: + return { + "plugin": "benchmarker", + "benchmark_name": self.name, + "benchmark_id": self.id, + "benchmark_type": self.__class__.__name__, + } + + def __str__(self): + return self.name + + __repr__ = __str__ + + +class BenchmarkOneDimMixin: + """Run benchmark with multiple values for a singel dimension""" + + def __init__(self, name: str, config: dict, benchmarker: Benchmarker): + super().__init__(name, config, benchmarker) + + self.dim_key = config.get("dim_key", None) + if not self.dim_key: + raise ValueError( + f"Property 'dim_key' (str) is missing for {name}. Must be a vaild config property name" + ) + + self.dim_values = config.get("dim_values", []) + if not self.dim_values: + raise ValueError( + f"Property 'dim_values' (list) is missing for {name}. Must be a list of values for 'dim_key'" + ) + + def run(self): + """Run benchmark with one changing parameter""" + + with tempfile.TemporaryDirectory() as temp_dir: + key = self.dim_key + for value in self.dim_values: + log.info("Run with %s set to %s", key, value) + + current_config = dataclasses.replace(self.config, **{key: value}) + + self.benchmark_results[value] = [] + for i in range(self.repetitions): + log.info("Run %d of %d", i + 1, self.repetitions) + + result_file = Path(temp_dir) / f"{self.name}_{value}_{i}.json" + result = self._run_at(result_file, i, current_config) + self.benchmark_results[value].append(result) + + def get_tags(self) -> dict[str, str]: + return { + **super().get_tags(), + "dim_key": self.dim_key, + "dim_values": self.dim_values, + } + + +@register_benchmark +class SetupTimeBenchmark(Benchmark): + """Compare the setuptime/ansible connection time between different destinations. + + Useful for basic setup tests and to check if everything is configured correctly + """ + + def __init__(self, name: str, config: dict, benchmarker: Benchmarker): + super().__init__(name, config, benchmarker) + + if "hosts" not in config: + raise ValueError( + f"'hosts' property (type: list[str]) is missing for '{name}'" + ) + + self.hosts: list[str] = config["hosts"] + + if not all(isinstance(value, str) for value in self.hosts): + raise ValueError("'hosts' property has to be of type list[str]") + + self._run_task = AnsibleTask(playbook="connection_test.yml") + + def run(self): + """Run the connection_test playbook on each destination""" + + for host in self.hosts: + log.info("Start %s for %s", self.name, host) + results = [] + for i in range(self.repetitions): + log.info("Run %d of %d", i + 1, self.repetitions) + start_time = time.monotonic() + + self._run_task.run_at(host) + + total_runtime = time.monotonic() - start_time + results.append({"runtime_in_s": total_runtime}) + self.benchmark_results[host] = results diff --git a/galaxy_benchmarker/benchmarks/dd.py b/galaxy_benchmarker/benchmarks/dd.py new file mode 100644 index 00000000..3d2de54f --- /dev/null +++ b/galaxy_benchmarker/benchmarks/dd.py @@ -0,0 +1,207 @@ +""" +Definition of dd-based benchmarks +""" +from __future__ import annotations + +import dataclasses +import logging +import re +import shutil +import time +from pathlib import Path +from typing import TYPE_CHECKING, Any + +from galaxy_benchmarker.benchmarks import base +from galaxy_benchmarker.utils import ansible +from galaxy_benchmarker.utils.destinations import PosixBenchmarkDestination + +if TYPE_CHECKING: + from galaxy_benchmarker.benchmarker import Benchmarker + +log = logging.getLogger(__name__) + + +@dataclasses.dataclass +class DdConfig(base.BenchmarkConfig): + blocksize: str = "" + blockcount: str = "" + input: str = "/dev/zero" + output: str = "/mnt/volume_under_test/dd-testfile.bin" + flush: bool = True + cleanup: bool = True + parallel: bool = False + + +def parse_result_file(file: Path) -> dict[str, Any]: + if not file.is_file(): + raise ValueError(f"{file} is not a file.") + + # Example output + # 512+0 records in + # 512+0 records out + # 512+0 records in + # 512+0 records out + # 536870912 bytes (537 MB, 512 MiB) copied, 2.32072 s, 231 MB/s + # 536870912 bytes (537 MB, 512 MiB) copied, 2.3709 s, 226 MB/s + + pattern = re.compile(r"([0-9]+) bytes .* copied, ([0-9\.]+) s, ([0-9\.]+) MB/s$") + + matches = [] + with file.open() as file_handle: + for line in file_handle: + match = pattern.findall(line) + if match: + matches.extend(match) + + total_bw_in_MiB = 0.0 + total_bw_in_mb = 0.0 + for bytes, time, bw_in_MB in matches: + bytes, time, bw_in_MB = int(bytes), float(time), float(bw_in_MB) + bw_in_MiB = (bytes / 1024**2) / time + bw_in_MB_calulated = (bytes / 1000**2) / time + + if bw_in_MB not in [round(bw_in_MB_calulated, 0), round(bw_in_MB_calulated, 1)]: + log.warning( + "Missmatch between calculated and parsed bandwidth in MB: Parsed: %.2f, Calculated %.2f", + bw_in_MB, + bw_in_MB_calulated, + ) + total_bw_in_MiB += bw_in_MiB + total_bw_in_mb += bw_in_MB + + return { + "bw_in_MiB": total_bw_in_MiB, + "bw_in_mb": total_bw_in_mb, + "detected_matches": len(matches), + } + + +@base.register_benchmark +class DdFixedParams(base.Benchmark): + """Benchmarking system with 'dd'""" + + def __init__(self, name: str, config: dict, benchmarker: Benchmarker): + super().__init__(name, config, benchmarker) + + if not "dd" in config: + raise ValueError(f"'dd' property (type: dict) is missing for '{self.name}'") + self.config = DdConfig(**config.get("dd", {})) + + dest = config.get("destination", {}) + if not dest: + raise ValueError( + f"'destination' property (type: dict) is missing for '{self.name}'" + ) + self.destination = PosixBenchmarkDestination(**dest) + + self._run_task = ansible.AnsibleTask(playbook="run_dd_benchmark.yml") + + def _run_at(self, result_file: Path, repetition: int, dd_config: DdConfig) -> dict: + """Perform a single run""" + + start_time = time.monotonic() + + self._run_task.run_at( + self.destination.host, + { + "dd_dir": self.destination.target_folder, + "dd_result_file": result_file.name, + "controller_dir": result_file.parent, + **{f"dd_{key}": value for key, value in dd_config.asdict().items()}, + }, + ) + + total_runtime = time.monotonic() - start_time + + if self.benchmarker.config.results_save_raw_results: + new_path = self.benchmarker.results / self.result_file.stem + new_path.mkdir(exist_ok=True) + shutil.copy(result_file, new_path / result_file.name) + + result = parse_result_file(result_file) + log.info("Run took %d s", total_runtime) + + return result + + def get_tags(self) -> dict[str, str]: + return {**super().get_tags(), "dd": self.config.asdict()} + + +@base.register_benchmark +class DdOneDimParams(base.BenchmarkOneDimMixin, DdFixedParams): + """Run dd with multiple values for a singel dimension""" + + +@base.register_benchmark +class DdPrepareNetappRead(DdOneDimParams): + """Setup directory struture for DD read benchmark""" + + def _run_at(self, result_file: Path, repetition: int, dd_config: DdConfig) -> dict: + """Perform a single run""" + + start_time = time.monotonic() + + filename = f"{dd_config.blockcount}x{dd_config.blocksize}.{repetition}.bin" + current_config = dataclasses.replace( + dd_config, + input="/dev/urandom", + output=f"/mnt/volume_under_test/{filename}", + cleanup=False, + ) + + self._run_task.run_at( + self.destination.host, + { + "dd_dir": self.destination.target_folder, + "dd_result_file": result_file.name, + "controller_dir": result_file.parent, + **{ + f"dd_{key}": value for key, value in current_config.asdict().items() + }, + }, + ) + + total_runtime = time.monotonic() - start_time + log.info("Run took %d s", total_runtime) + return {"runtime_in_s": total_runtime} + + +@base.register_benchmark +class DdNetappRead(DdOneDimParams): + def _run_at(self, result_file: Path, repetition: int, dd_config: DdConfig) -> dict: + """Perform a single run""" + + start_time = time.monotonic() + + filename = f"{dd_config.blockcount}x{dd_config.blocksize}.{repetition}.bin" + current_config = dataclasses.replace( + dd_config, + input=f"/mnt/volume_under_test/{filename}", + output="/dev/null", + flush=False, + cleanup=False, + ) + + self._run_task.run_at( + self.destination.host, + { + "dd_dir": self.destination.target_folder, + "dd_result_file": result_file.name, + "controller_dir": result_file.parent, + **{ + f"dd_{key}": value for key, value in current_config.asdict().items() + }, + }, + ) + + total_runtime = time.monotonic() - start_time + + if self.benchmarker.config.results_save_raw_results: + new_path = self.benchmarker.results / self.result_file.stem + new_path.mkdir(exist_ok=True) + shutil.copy(result_file, new_path / result_file.name) + + result = parse_result_file(result_file) + log.info("Run took %d s", total_runtime) + + return result diff --git a/galaxy_benchmarker/benchmarks/fio.py b/galaxy_benchmarker/benchmarks/fio.py new file mode 100644 index 00000000..2e450e2b --- /dev/null +++ b/galaxy_benchmarker/benchmarks/fio.py @@ -0,0 +1,270 @@ +""" +Definition of fio-based benchmarks +""" +from __future__ import annotations + +import dataclasses +import json +import logging +import shutil +import tempfile +import time +from pathlib import Path +from typing import TYPE_CHECKING, Any + +from galaxy_benchmarker.benchmarks import base +from galaxy_benchmarker.typing import RunResult +from galaxy_benchmarker.utils import ansible +from galaxy_benchmarker.utils.destinations import PosixBenchmarkDestination + +if TYPE_CHECKING: + from galaxy_benchmarker.benchmarker import Benchmarker + +log = logging.getLogger(__name__) + + +@dataclasses.dataclass +class FioConfig(base.BenchmarkConfig): + """Available parameters for fio""" + + blocksize: str = "" + filesize: str = "5G" + iodepth: int = 32 + ioengine: str = "libaio" + mode: str = "" + numjobs: int = 4 + ramptime_in_s: int = 0 + refill_buffers: bool = True + runtime_in_s: int = 60 + time_based: bool = True + prepare_read_benchmark_in_tmp: bool = False + + +@base.register_benchmark +class FioFixedParams(base.Benchmark): + """Run fio with fixed params""" + + def __init__(self, name: str, config: dict, benchmarker: Benchmarker): + super().__init__(name, config, benchmarker) + + if not "fio" in config: + raise ValueError( + f"'fio' property (type: dict) is missing for '{self.name}'" + ) + self.config = FioConfig(**config.get("fio", {})) + + dest = config.get("destination", {}) + if not dest: + raise ValueError( + f"'destination' property (type: dict) is missing for '{self.name}'" + ) + self.destination = PosixBenchmarkDestination(**dest) + + self._run_task = ansible.AnsibleTask(playbook="run_fio_benchmark.yml") + + def _run_at( + self, result_file: Path, repetition: int, fio_config: FioConfig + ) -> RunResult: + """Perform a single run""" + + start_time = time.monotonic() + + self._run_task.run_at( + self.destination.host, + { + "fio_dir": self.destination.target_folder, + "fio_result_file": result_file.name, + "controller_dir": result_file.parent, + "fio_jobname": self.name, + "fio_filesize_in_bytes": self._filesize_to_bytes(fio_config.filesize), + **{f"fio_{key}": value for key, value in fio_config.asdict().items()}, + }, + ) + + total_runtime = time.monotonic() - start_time + + if self.benchmarker.config.results_save_raw_results: + new_path = self.benchmarker.results / self.result_file.stem + new_path.mkdir(exist_ok=True) + shutil.copy(result_file, new_path / result_file.name) + + result = parse_result_file(result_file, self.name) + log.info("Run took %d s", total_runtime) + + return result + + def get_tags(self) -> dict[str, str]: + return {**super().get_tags(), "fio": self.config.asdict()} + + @staticmethod + def _filesize_to_bytes(filesize: str) -> str: + """ + Examples: + "5G" -> "5368709120" + """ + unit = filesize.strip()[-1] + number = filesize.strip()[:-1] + match unit.upper(): + case "G": + multiplier = 1024**3 + case "M": + multiplier = 1024**2 + case "K": + multiplier = 1024**1 + case _: + assert filesize.isnumeric(), "Unknown unit for filesize" + multiplier = 1024**0 + number = filesize.strip() + return str(int(number) * multiplier) + + +@base.register_benchmark +class FioOneDimParams(base.BenchmarkOneDimMixin, FioFixedParams): + """Run fio with multiple values for a singel dimension""" + + +@base.register_benchmark +class FioNotContainerized(FioFixedParams): + """Run fio outside of a container""" + + def __init__(self, name: str, config: dict, benchmarker: Benchmarker): + super().__init__(name, config, benchmarker) + + self._pre_task = ansible.AnsibleTask( + playbook="run_fio_benchmark_not_containerized_check.yml", + host=f"{self.destination.host},", + ) + + self._run_task = ansible.AnsibleTask( + playbook="run_fio_benchmark_not_containerized.yml" + ) + + +@base.register_benchmark +class FioFullPosix(FioFixedParams): + def run(self): + """Run 'fio'""" + + with tempfile.TemporaryDirectory() as temp_dir: + log.info("Start %s", self.name) + + throughput_write_config = dataclasses.replace( + self.config, + mode="write", + blocksize="1024k", + numjobs=4, + iodepth=32, + ) + throughput_read_config = dataclasses.replace( + self.config, + mode="read", + blocksize="1024k", + numjobs=4, + iodepth=32, + ) + iops_write_config = dataclasses.replace( + self.config, + mode="randwrite", + blocksize="4k", + numjobs=4, + iodepth=32, + ) + iops_read_config = dataclasses.replace( + self.config, + mode="randread", + blocksize="4k", + numjobs=4, + iodepth=32, + ) + latency_write_config = dataclasses.replace( + self.config, + mode="randwrite", + blocksize="4k", + numjobs=1, + iodepth=1, + ) + latency_read_config = dataclasses.replace( + self.config, + mode="randread", + blocksize="4k", + numjobs=1, + iodepth=1, + ) + + runs = [ + ("throughput_read", throughput_read_config), + ("throughput_write", throughput_write_config), + ("iops_read", iops_read_config), + ("iops_write", iops_write_config), + ("latency_read", latency_read_config), + ("latency_write", latency_write_config), + ] + + for name, config in runs: + self.benchmark_results[name] = [] + log.info("Start %s", name) + for i in range(self.repetitions): + log.info("Run %d of %d", i + 1, self.repetitions) + result_file = Path(temp_dir) / f"{name}_{i}.json" + + try: + result = self._run_at(result_file, i, config) + self.benchmark_results[name].append(result) + except Exception as e: + log.exception("Error during '%s' run: %s", name, e) + + +def parse_result_file(file: Path, jobname: str) -> dict[str, Any]: + if not file.is_file(): + raise ValueError(f"{file} is not a fio result file.") + + with file.open() as file_handle: + lines = file_handle.readlines() + lines = [l for l in lines if not l.startswith("fio: pid=")] + result_json = json.loads("\n".join(lines)) + + jobs = [job for job in result_json["jobs"] if job["jobname"] == jobname] + + if len(jobs) != 1: + raise ValueError(f"Job '{jobname}' is missing in result {file}") + + result = {} + mode = result_json["global options"].get("rw", None) + if mode is None: + # Fallback + mode = jobs[0]["job options"]["rw"] + + if mode in ["read", "randread", "rw", "randrw"]: + read = result_json["jobs"][0]["read"] + result.update(_parse_job_result(read, "read")) + if mode in ["write", "randwrite", "rw", "randrw"]: + write = result_json["jobs"][0]["write"] + result.update(_parse_job_result(write, "write")) + + return result + + +def _parse_job_result(result: dict, prefix: str) -> dict[str, Any]: + # Sometimes latency has some values even when the run crashes and all other metrics have 0.0 + # -> Set latency also to 0 in that case + return { + f"{prefix}_bw_min_in_MiB": result["bw_min"] / 1024, + f"{prefix}_bw_max_in_MiB": result["bw_max"] / 1024, + f"{prefix}_bw_mean_in_MiB": result["bw_mean"] / 1024, + f"{prefix}_iops_min": result["iops_min"], + f"{prefix}_iops_max": result["iops_max"], + f"{prefix}_iops_mean": result["iops_mean"], + f"{prefix}_iops_stddev": result["iops_stddev"], + f"{prefix}_lat_min_in_ms": result["lat_ns"]["min"] / 1_000_000 + if result["bw_mean"] > 0 + else 0.0, + f"{prefix}_lat_max_in_ms": result["lat_ns"]["max"] / 1_000_000 + if result["bw_mean"] > 0 + else 0.0, + f"{prefix}_lat_mean_in_ms": result["lat_ns"]["mean"] / 1_000_000 + if result["bw_mean"] > 0 + else 0.0, + f"{prefix}_lat_stddev_in_ms": result["lat_ns"]["stddev"] / 1_000_000 + if result["bw_mean"] > 0 + else 0.0, + } diff --git a/galaxy_benchmarker/benchmarks/galaxy.py b/galaxy_benchmarker/benchmarks/galaxy.py new file mode 100644 index 00000000..0f39199b --- /dev/null +++ b/galaxy_benchmarker/benchmarks/galaxy.py @@ -0,0 +1,331 @@ +""" +Definition of galaxyjob-based benchmarks +""" +from __future__ import annotations + +import dataclasses +import json +import logging +import shlex +import time +from pathlib import Path +from typing import TYPE_CHECKING + +from galaxy_benchmarker.benchmarks import base +from galaxy_benchmarker.utils import ansible, s3 +from galaxy_benchmarker.utils.destinations import ( + BenchmarkDestination, + PosixBenchmarkDestination, +) + +if TYPE_CHECKING: + from galaxy_benchmarker.benchmarker import Benchmarker + +log = logging.getLogger(__name__) + + +@dataclasses.dataclass +class GalaxyFileGenInput: + num_files: int + file_size_in_bytes: int + + +@dataclasses.dataclass +class GalaxyFileGenConfig(base.BenchmarkConfig): + input: GalaxyFileGenInput + verification_timeout_in_s: int + export_volume: str + + +class GalaxyFileGenJob(base.Benchmark): + """Benchmarking galaxy with the file_gen job""" + + galaxy_tool_id = "file_gen" + galaxy_job_config_class = GalaxyFileGenConfig + + def __init__(self, name: str, config: dict, benchmarker: Benchmarker): + super().__init__(name, config, benchmarker) + + if not self.galaxy_tool_id: + raise ValueError( + "Subclass of GalaxyJob has to specify class property 'galaxy_tool_id' (str)" + ) + + if not "galaxy_job" in config: + raise ValueError( + f"'galaxy_job' property (type: dict) is missing for '{self.name}'" + ) + glx_job_config = config.get("galaxy_job") + + if not "input" in glx_job_config: + raise ValueError( + f"'galaxy_job'->'input' property (type: dict) is missing for '{self.name}'" + ) + + self.config = self.galaxy_job_config_class( + input=GalaxyFileGenInput(**glx_job_config.pop("input")), + **glx_job_config, + ) + + dest = config.get("destination", {}) + if not dest: + raise ValueError( + f"'destination' property (type: dict) is missing for '{self.name}'" + ) + self.destination = BenchmarkDestination(**dest) + + self._run_task = ansible.AnsibleTask(playbook="run_galaxy_job.yml") + + def _run_at( + self, result_file: Path, repetition: int, galaxy_job_config: GalaxyFileGenConfig + ) -> dict: + """Perform a single run""" + + start_time = time.monotonic() + + input_str = json.dumps(dataclasses.asdict(galaxy_job_config.input)) + + self._run_task.run_at( + self.destination.host, + { + "glx_tool_id": self.galaxy_tool_id, + "glx_tool_input": shlex.quote(input_str), + "glx_expected_num_files": galaxy_job_config.input.num_files, + "glx_expected_size_in_bytes": galaxy_job_config.input.file_size_in_bytes, + **{ + f"glx_{key}": value + for key, value in galaxy_job_config.asdict().items() + }, + }, + ) + + total_runtime = time.monotonic() - start_time + + result = {"total_runtime_in_s": total_runtime} + log.info("Run took %d s", total_runtime) + + return result + + def get_tags(self) -> dict[str, str]: + return { + **super().get_tags(), + "galaxy_tool_id": self.galaxy_tool_id, + "galaxy_tool_config": self.config.asdict(), + } + + +@dataclasses.dataclass +class GalaxyFileGenOnMountVolumeConfig(GalaxyFileGenConfig): + path_to_files: str + + +@base.register_benchmark +class GalaxyFileGenOnMountVolumeJob(GalaxyFileGenJob): + galaxy_job_config_class = GalaxyFileGenOnMountVolumeConfig + + def __init__(self, name: str, config: dict, benchmarker: Benchmarker): + # Store destination + dest_conf = config.pop("destination", {}) + config["destination"] = {"host": "dummy"} + + super().__init__(name, config, benchmarker) + + # Restore destination + config["destination"] = dest_conf + self.destination = PosixBenchmarkDestination(**dest_conf) + + self._run_task = ansible.AnsibleTask(playbook="run_galaxy_mount_benchmark.yml") + self._pre_tasks.append( + ansible.AnsibleTask( + playbook="setup_galaxy_server.yml", + host=self.destination.host, + extra_vars={ + "galaxy_use_mount": True, + "galaxy_host_volume": self.destination.target_folder, + "galaxy_export_volume": self.config.export_volume, + }, + ) + ) + self._post_tasks.append( + ansible.AnsibleTask( + playbook="cleanup_galaxy_server.yml", + host=self.destination.host, + extra_vars={ + "galaxy_host_volume": self.destination.target_folder, + "galaxy_export_volume": self.config.export_volume, + }, + ) + ) + + +@dataclasses.dataclass +class GalaxyFileGenOnS3Config(s3.S3Config, GalaxyFileGenConfig): + pass + + +@base.register_benchmark +class GalaxyFileGenOnS3Job(GalaxyFileGenJob): + galaxy_job_config_class = GalaxyFileGenOnS3Config + config: GalaxyFileGenOnS3Config + + def __init__(self, name: str, config: dict, benchmarker: Benchmarker): + super().__init__(name, config, benchmarker) + + self._pre_tasks.append( + ansible.AnsibleTask( + playbook="setup_galaxy_server.yml", + host=self.destination.host, + extra_vars={ + "galaxy_use_s3": True, + "galaxy_export_volume": self.config.export_volume, + "S3_ACCESS_KEY": self.config.access_key_id, + "S3_SECRET_KEY": self.config.secret_access_key, + "S3_BASE_URL": self.config.base_url.split("://")[1], + "S3_BUCKET_NAME": self.config.bucket_name, + }, + ) + ) + self._post_tasks.append( + ansible.AnsibleTask( + playbook="cleanup_galaxy_server.yml", + host=self.destination.host, + extra_vars={ + "galaxy_export_volume": self.config.export_volume, + }, + ) + ) + + def _run_at( + self, + result_file: Path, + repetition: int, + galaxy_job_config: GalaxyFileGenOnS3Config, + ) -> dict: + """Perform a single run""" + + # Make sure bucket is empty + log.info("Empty s3 bucket") + s3.empty_bucket(galaxy_job_config) + log.info("Empty s3 bucket done") + + start_time = time.monotonic() + + # Trigger galaxy job + super()._run_at(result_file, repetition, galaxy_job_config) + + # Check s3 bucket for files + s3.check_bucket_for_files( + galaxy_job_config, + galaxy_job_config.input.num_files, + galaxy_job_config.input.file_size_in_bytes, + galaxy_job_config.verification_timeout_in_s, + ) + + total_runtime = time.monotonic() - start_time + result = {"total_runtime_in_s": total_runtime} + log.info("Run took %d s", total_runtime) + + return result + + +@dataclasses.dataclass +class GalaxyFileGenOnIrodsOnMountVolumeConfig(GalaxyFileGenConfig): + irods_server_host: str + path_to_files: str + + +@base.register_benchmark +class GalaxyFileGenOnIrodsOnMountVolumeJob(GalaxyFileGenJob): + galaxy_job_config_class = GalaxyFileGenOnIrodsOnMountVolumeConfig + + def __init__(self, name: str, config: dict, benchmarker: Benchmarker): + super().__init__(name, config, benchmarker) + + self._run_task = ansible.AnsibleTask(playbook="run_galaxy_mount_benchmark.yml") + self._pre_tasks.append( + ansible.AnsibleTask( + playbook="setup_galaxy_server.yml", + host=self.destination.host, + extra_vars={ + "galaxy_use_irods": True, + "galaxy_export_volume": self.config.export_volume, + "IRODS_HOST": self.config.irods_server_host, + }, + ) + ) + self._post_tasks.append( + ansible.AnsibleTask( + playbook="cleanup_galaxy_server.yml", + host=self.destination.host, + extra_vars={ + "galaxy_export_volume": self.config.export_volume, + }, + ) + ) + + +@dataclasses.dataclass +class GalaxyFileGenOnIrodsOnS3Config(s3.S3Config, GalaxyFileGenConfig): + irods_server_host: str + + +@base.register_benchmark +class GalaxyFileGenOnIrodsOnS3Job(GalaxyFileGenJob): + galaxy_job_config_class = GalaxyFileGenOnIrodsOnS3Config + config: GalaxyFileGenOnIrodsOnS3Config + + def __init__(self, name: str, config: dict, benchmarker: Benchmarker): + super().__init__(name, config, benchmarker) + + self._pre_tasks.append( + ansible.AnsibleTask( + playbook="setup_galaxy_server.yml", + host=self.destination.host, + extra_vars={ + "galaxy_use_irods": True, + "galaxy_export_volume": self.config.export_volume, + "IRODS_HOST": self.config.irods_server_host, + }, + ) + ) + self._post_tasks.append( + ansible.AnsibleTask( + playbook="cleanup_galaxy_server.yml", + host=self.destination.host, + extra_vars={ + "galaxy_export_volume": self.config.export_volume, + }, + ) + ) + + def _run_at( + self, + result_file: Path, + repetition: int, + galaxy_job_config: GalaxyFileGenOnS3Config, + ) -> dict: + """Perform a single run""" + + # Make sure bucket is empty + log.info("Empty s3 bucket") + s3.empty_bucket(galaxy_job_config) + log.info("Empty s3 bucket done") + + start_time = time.monotonic() + + # Trigger galaxy job + super()._run_at(result_file, repetition, galaxy_job_config) + + # Check s3 bucket for files + s3.check_bucket_for_files( + galaxy_job_config, + galaxy_job_config.input.num_files, + galaxy_job_config.input.file_size_in_bytes, + galaxy_job_config.verification_timeout_in_s, + ) + + total_runtime = time.monotonic() - start_time + result = {"total_runtime_in_s": total_runtime} + log.info("Run took %d s", total_runtime) + + return result diff --git a/galaxy_benchmarker/benchmarks/mdtest.py b/galaxy_benchmarker/benchmarks/mdtest.py new file mode 100644 index 00000000..f702446a --- /dev/null +++ b/galaxy_benchmarker/benchmarks/mdtest.py @@ -0,0 +1,140 @@ +""" +Definition of mdtest-based benchmarks +""" +from __future__ import annotations + +import dataclasses +import logging +import shutil +import time +from pathlib import Path +from typing import TYPE_CHECKING, Any + +from galaxy_benchmarker.benchmarks import base +from galaxy_benchmarker.typing import RunResult +from galaxy_benchmarker.utils import ansible +from galaxy_benchmarker.utils.destinations import PosixBenchmarkDestination + +if TYPE_CHECKING: + from galaxy_benchmarker.benchmarker import Benchmarker + +log = logging.getLogger(__name__) + + +@dataclasses.dataclass +class MdtestConfig(base.BenchmarkConfig): + """Available parameters for mdtest""" + + num_files: int + + +@base.register_benchmark +class MdtestFixedParams(base.Benchmark): + """Run mdtest with fixed params""" + + def __init__(self, name: str, config: dict, benchmarker: Benchmarker): + super().__init__(name, config, benchmarker) + + if not "mdtest" in config: + raise ValueError( + f"'mdtest' property (type: dict) is missing for '{self.name}'" + ) + mdtest_conf = config.get("mdtest") + if mdtest_conf is None: + mdtest_conf = {} + self.config = MdtestConfig(**mdtest_conf) + + dest = config.get("destination", {}) + if not dest: + raise ValueError( + f"'destination' property (type: dict) is missing for '{self.name}'" + ) + self.destination = PosixBenchmarkDestination(**dest) + + self._run_task = ansible.AnsibleTask(playbook="run_mdtest_benchmark.yml") + + def _run_at( + self, result_file: Path, repetition: int, mdtest_config: MdtestConfig + ) -> RunResult: + """Perform a single run""" + + start_time = time.monotonic() + + self._run_task.run_at( + self.destination.host, + { + "mdtest_dir": self.destination.target_folder, + "mdtest_result_file": result_file.name, + "controller_dir": result_file.parent, + **{ + f"mdtest_{key}": value + for key, value in mdtest_config.asdict().items() + }, + }, + ) + + total_runtime = time.monotonic() - start_time + + if self.benchmarker.config.results_save_raw_results: + new_path = self.benchmarker.results / self.result_file.stem + new_path.mkdir(exist_ok=True) + shutil.copy(result_file, new_path / result_file.name) + + result = parse_result_file(result_file) + log.info("Run took %d s", total_runtime) + + return result + + def get_tags(self) -> dict[str, str]: + return {**super().get_tags(), "mdtest": self.config.asdict()} + + +@base.register_benchmark +class MdtestOneDimParams(base.BenchmarkOneDimMixin, MdtestFixedParams): + """Run mdtest with multiple values for a singel dimension""" + + +def parse_result_file(file: Path) -> dict[str, Any]: + if not file.is_file(): + raise ValueError(f"{file} is not a mdtest result file.") + + with file.open() as file_handle: + lines = file_handle.readlines() + + metrics = { + "Directory creation": "dir_create", + "Directory stat": "dir_stat", + "Directory rename": "dir_rename", + "Directory removal": "dir_remove", + "File creation": "file_create", + "File stat": "file_stat", + "File read": "file_read", + "File removal": "file_remove", + "Tree creation": "tree_create", + "Tree removal": "tree_remove", + } + + result = {} + + for line in lines: + l = line.split() + if len(l) < 2: + continue + if (key := f"{l[0]} {l[1]}") not in metrics: + continue + # Line has to be of format: + # Op Max Min Mean Std Dev + # "Directory creation 48.473 48.473 48.473 0.000" + def is_float(value): + try: + float(value) + except ValueError: + return False + return True + + floats = [float(item) for item in l if is_float(item)] + assert len(floats) == 4 + metric = metrics[key] + result[metric] = floats[2] + + return result diff --git a/galaxy_benchmarker/benchmarks/s3benchmark.py b/galaxy_benchmarker/benchmarks/s3benchmark.py new file mode 100644 index 00000000..aee2d04d --- /dev/null +++ b/galaxy_benchmarker/benchmarks/s3benchmark.py @@ -0,0 +1,157 @@ +""" +Definition of s3benchmark-based benchmarks +""" +from __future__ import annotations + +import dataclasses +import logging +import os +import re +import shutil +import time +from pathlib import Path +from typing import TYPE_CHECKING, Any + +from galaxy_benchmarker.benchmarks import base +from galaxy_benchmarker.utils import ansible +from galaxy_benchmarker.utils.destinations import BenchmarkDestination + +if TYPE_CHECKING: + from galaxy_benchmarker.benchmarker import Benchmarker + +log = logging.getLogger(__name__) + + +@dataclasses.dataclass +class S3BenchmarkConfig(base.BenchmarkConfig): + ## Credentials are loaded from AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY + # access_key_id: str = "" + base_url: str = "" + bucket_name: str = "" + filesize: str = "" + region: str = "" + runtime_in_s: int = 60 + # secret_access_key: str = "" + threads: int = 8 + + +def parse_result_file(file: Path) -> dict[str, Any]: + if not file.is_file(): + raise ValueError(f"{file} is not a file.") + + # Example output + # Loop 1: PUT time 10.2 secs, objects = 1074, speed = 105.3MB/sec, 105.3 operations/sec. Slowdowns = 0 + # Loop 1: GET time 10.1 secs, objects = 1124, speed = 111.4MB/sec, 111.4 operations/sec. Slowdowns = 0 + # Loop 1: DELETE time 0.8 secs, 1286.9 deletes/sec. Slowdowns = 0 + + l_get, l_put, l_delete = "", "", "" + with file.open() as file_handle: + for line in file_handle: + if not line.startswith("Loop"): + continue + + striped = line.split(":")[1].lstrip() + if striped.startswith("GET"): + l_get = striped + elif striped.startswith("PUT"): + l_put = striped + elif striped.startswith("DELETE"): + l_delete = striped + else: + raise ValueError(f"Unknown linestart: {striped}") + + pattern = re.compile( + r", objects = ([0-9]+), speed = ([0-9\.]+)([MK]B)/sec, ([0-9\.]+) operations/sec" + ) + get_num_obj, get_bw, get_bw_unit, get_ops = pattern.search(l_get).groups() + put_num_obj, put_bw, put_bw_unit, put_ops = pattern.search(l_put).groups() + del_ops = re.search(r", ([0-9\.]+) deletes/sec", l_delete).groups()[0] + + if get_bw_unit == "KB": + get_bw = str(float(get_bw) / 1024.0) + if put_bw_unit == "KB": + put_bw = str(float(put_bw) / 1024.0) + + return { + "get_num_objects": get_num_obj, + "get_bw_in_mibi": get_bw, + "get_op_per_s": get_ops, + "put_num_objects": put_num_obj, + "put_bw_in_mibi": put_bw, + "put_op_per_s": put_ops, + "del_op_per_s": del_ops, + } + + +@base.register_benchmark +class S3BenchmarkFixedParams(base.Benchmark): + """Benchmarking system with 's3benchmark'""" + + def __init__(self, name: str, config: dict, benchmarker: Benchmarker): + super().__init__(name, config, benchmarker) + + if not "s3benchmark" in config: + raise ValueError( + f"'s3benchmark' property (type: dict) is missing for '{self.name}'" + ) + self.config = S3BenchmarkConfig(**config.get("s3benchmark")) + + dest = config.get("destination", {}) + if not dest: + raise ValueError( + f"'destination' property (type: dict) is missing for '{self.name}'" + ) + self.destination = BenchmarkDestination(**dest) + + self._run_task = ansible.AnsibleTask(playbook="run_s3-benchmark_benchmark.yml") + + def _run_at( + self, result_file: Path, repetition: int, s3benchmark_config: S3BenchmarkConfig + ) -> dict: + """Perform a single run""" + + start_time = time.monotonic() + + access_key = os.getenv("AWS_ACCESS_KEY_ID") + if access_key is None: + raise ValueError("Missing S3 credentials in env vars: AWS_ACCESS_KEY_ID") + + secret_key = os.getenv("AWS_SECRET_ACCESS_KEY") + if secret_key is None: + raise ValueError( + "Missing S3 credentials in env vars: AWS_SECRET_ACCESS_KEY" + ) + + self._run_task.run_at( + self.destination.host, + { + "s3b_result_file": result_file.name, + "controller_dir": result_file.parent, + "s3b_access_key_id": access_key, + "s3b_secret_access_key": secret_key, + **{ + f"s3b_{key}": value + for key, value in s3benchmark_config.asdict().items() + }, + }, + ) + + total_runtime = time.monotonic() - start_time + + if self.benchmarker.config.results_save_raw_results: + new_path = self.benchmarker.results / self.result_file.stem + new_path.mkdir(exist_ok=True) + shutil.copy(result_file, new_path / result_file.name) + + result = parse_result_file(result_file) + log.info("Run took %d s", total_runtime) + + return result + + def get_tags(self) -> dict[str, str]: + return {**super().get_tags(), "s3b": self.config.asdict()} + + +@base.register_benchmark +class S3BenchmarkOneDimParams(base.BenchmarkOneDimMixin, S3BenchmarkFixedParams): + """Run s3benchmark with multiple values for a singel dimension""" diff --git a/galaxy_benchmarker/benchmarks/utils.py b/galaxy_benchmarker/benchmarks/utils.py new file mode 100644 index 00000000..018671d5 --- /dev/null +++ b/galaxy_benchmarker/benchmarks/utils.py @@ -0,0 +1,134 @@ +from __future__ import annotations + +import json +import logging +import threading +from functools import wraps +from pathlib import Path +from typing import TYPE_CHECKING + +from galaxy_benchmarker.benchmarks import base + +if TYPE_CHECKING: + from galaxy_benchmarker.benchmarker import Benchmarker + +log = logging.getLogger(__name__) + + +@base.register_benchmark +class BenchmarkCompare(base.Benchmark): + """ + Compare two benchmarks side by side. + """ + + def __init__(self, name: str, config: dict, benchmarker: Benchmarker): + super().__init__(name, config, benchmarker) + + conf_a = config.get("bench_a", {}) + if not conf_a: + raise ValueError( + f"Property 'bench_a' (dict) is missing for {name}. Must be a valid benchmark config" + ) + conf_b = config.get("bench_b", {}) + if not conf_b: + raise ValueError( + f"Property 'bench_b' (dict) is missing for {name}. Must be a valid benchmark config" + ) + self.bench_a = base.Benchmark.create(f"{name}_a", conf_a, benchmarker) + self.bench_b = base.Benchmark.create(f"{name}_b", conf_b, benchmarker) + + def run_pre_tasks(self) -> None: + """Run setup tasks""" + self.bench_a.run_pre_tasks() + self.bench_b.run_pre_tasks() + + def run_post_tasks(self) -> None: + """Run clean up task""" + self.bench_a.run_post_tasks() + self.bench_b.run_post_tasks() + + def run(self): + """Run two benchmarks in the following order: a1, b1, a2, b2, ...""" + sync_lock = threading.Condition() + sync_required = True + + def run_at_wrapper(method, note): + """Wrapper for _ran_at + + Wait after each _run_at-call so the other benchmark can run + """ + + @wraps(method) + def _impl(self, *method_args, **method_kwargs): + with sync_lock: + log.info("Start run in thread %s", note) + method_output = method(self, *method_args, **method_kwargs) + sync_lock.notify() + if sync_required: + sync_lock.wait() + return method_output + + return _impl + + def run_in_thread(method): + """Wrapper for thread call + + After call to run() has finished notify the other thread so it can + also finish + """ + + def _impl(): + method_output = method() + global sync_required + sync_required = False + with sync_lock: + sync_lock.notify() + return method_output + + return _impl + + _bench_a_run_at = self.bench_a._run_at + _bench_b_run_at = self.bench_b._run_at + + try: + # Inject threading mechanism to interleave execution of both benchmarks + self.bench_a._run_at = run_at_wrapper(_bench_a_run_at, "BenchA") + self.bench_b._run_at = run_at_wrapper(_bench_b_run_at, "BenchB") + + # creating threads + t1 = threading.Thread(target=run_in_thread(self.bench_a.run)) + t2 = threading.Thread(target=run_in_thread(self.bench_b.run)) + t1.start() + t2.start() + + # wait until threads finish their job + t1.join() + t2.join() + finally: + # Restore original functions + self.bench_a._run_at = _bench_a_run_at + self.bench_b._run_at = _bench_b_run_at + + def save_results_to_file(self, directory: Path) -> str: + """Write all metrics to a file.""" + file = directory / self.result_file.name + results = {"tags": self.get_tags()} + json_results = json.dumps(results, indent=2) + file.write_text(json_results) + + self.bench_a.save_results_to_file(directory) + self.bench_b.save_results_to_file(directory) + + return str(file) + + def get_tags(self) -> dict[str, str]: + return { + "plugin": "benchmarker", + "benchmark_name": self.name, + "benchmark_id": self.id, + "benchmark_type": self.__class__.__name__, + "bench_a": self.bench_a.get_tags(), + "bench_a_results": self.bench_a.result_file.name, + "bench_b": self.bench_b.get_tags(), + "bench_b_results": self.bench_b.result_file.name, + } diff --git a/galaxy_benchmarker/benchmarks/warp.py b/galaxy_benchmarker/benchmarks/warp.py new file mode 100644 index 00000000..de7116b1 --- /dev/null +++ b/galaxy_benchmarker/benchmarks/warp.py @@ -0,0 +1,162 @@ +""" +Definition of warp-based benchmarks +""" +from __future__ import annotations + +import dataclasses +import logging +import os +import re +import shutil +import time +from pathlib import Path +from typing import TYPE_CHECKING, Any + +from galaxy_benchmarker.benchmarks import base +from galaxy_benchmarker.utils import ansible +from galaxy_benchmarker.utils.destinations import BenchmarkDestination + +if TYPE_CHECKING: + from galaxy_benchmarker.benchmarker import Benchmarker + +log = logging.getLogger(__name__) + + +@dataclasses.dataclass +class WarpConfig(base.BenchmarkConfig): + mode: str = "" + ## Credentials are loaded from AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY + # access_key_id: str = "" + base_url: str = "" + bucket_name: str = "" + filesize: str = "" + region: str = "" + runtime: str = "60s" + # secret_access_key: str = "" + concurrent_ops: int = 20 + + +def parse_result_file(file: Path) -> dict[str, Any]: + + if not file.is_file(): + raise ValueError(f"{file} is not a file.") + + # Example output + # Operation: DELETE, 11%, Concurrency: 20, Ran 3m47s. + # * Throughput: 0.28 obj/s + + # Operation: GET, 38%, Concurrency: 20, Ran 4m15s. + # * Throughput: 8.21 MiB/s, 0.82 obj/s + + # Operation: PUT, 13%, Concurrency: 20, Ran 4m19s. + # * Throughput: 2.85 MiB/s, 0.29 obj/s + + # Operation: STAT, 33%, Concurrency: 20, Ran 4m2s. + # * Throughput: 0.65 obj/s + result = {} + op = "" + pattern_op = re.compile(r"Operation: ([A-Z]+),?") + pattern_throughput = re.compile(r"([0-9\.]+) ([KM]iB)/s,") + pattern_ops = re.compile(r"([0-9\.]+) obj/s") + + with file.open() as file_handle: + for line in file_handle: + if line.startswith("Operation: "): + # Get op to parse next line + op = pattern_op.match(line).groups()[0] + continue + if not op: + continue + + throughput_match = pattern_throughput.search(line) + ops_match = pattern_ops.search(line) + + if throughput_match: + throughput_value, unit = throughput_match.groups() + if unit == "KiB": + throughput_value = str(float(throughput_value) / 1024.0) + + if op == "GET": + result["get_bw_in_MiB"] = throughput_value + result["get_ops"] = ops_match.groups()[0] + elif op == "PUT": + result["put_bw_in_MiB"] = throughput_value + result["put_ops"] = ops_match.groups()[0] + elif op == "DELETE": + result["delete_ops"] = ops_match.groups()[0] + elif op == "STAT": + result["stat_ops"] = ops_match.groups()[0] + op = "" + + return result + + +@base.register_benchmark +class WarpFixedParams(base.Benchmark): + """Benchmarking system with 'warp'""" + + def __init__(self, name: str, config: dict, benchmarker: Benchmarker): + super().__init__(name, config, benchmarker) + + if not "warp" in config: + raise ValueError( + f"'warp' property (type: dict) is missing for '{self.name}'" + ) + self.config = WarpConfig(**config.get("warp")) + + dest = config.get("destination", {}) + if not dest: + raise ValueError( + f"'destination' property (type: dict) is missing for '{self.name}'" + ) + self.destination = BenchmarkDestination(**dest) + + self._run_task = ansible.AnsibleTask(playbook="run_warp_benchmark.yml") + + def _run_at( + self, result_file: Path, repetition: int, warp_config: WarpConfig + ) -> dict: + """Perform a single run""" + + start_time = time.monotonic() + + access_key = os.getenv("AWS_ACCESS_KEY_ID") + if access_key is None: + raise ValueError("Missing S3 credentials in env vars: AWS_ACCESS_KEY_ID") + + secret_key = os.getenv("AWS_SECRET_ACCESS_KEY") + if secret_key is None: + raise ValueError( + "Missing S3 credentials in env vars: AWS_SECRET_ACCESS_KEY" + ) + + self._run_task.run_at( + self.destination.host, + { + "warp_result_file": result_file.name, + "controller_dir": result_file.parent, + "warp_access_key_id": access_key, + "warp_secret_access_key": secret_key, + **{f"warp_{key}": value for key, value in warp_config.asdict().items()}, + }, + ) + + total_runtime = time.monotonic() - start_time + + if self.benchmarker.config.results_save_raw_results: + new_path = self.benchmarker.results / self.result_file.stem + new_path.mkdir(exist_ok=True) + shutil.copy(result_file, new_path / result_file.name) + + result = parse_result_file(result_file) + log.info("Run took %d s", total_runtime) + + return result + + def get_tags(self) -> dict[str, str]: + return {**super().get_tags(), "warp": self.config.asdict()} + + +@base.register_benchmark +class WarpOneDimParams(base.BenchmarkOneDimMixin, WarpFixedParams): + """Run warp with multiple values for a singel dimension""" diff --git a/galaxy_benchmarker/condor_bridge.py b/galaxy_benchmarker/condor_bridge.py deleted file mode 100644 index a7569626..00000000 --- a/galaxy_benchmarker/condor_bridge.py +++ /dev/null @@ -1,96 +0,0 @@ -import paramiko -import re -from typing import List, Dict -from datetime import datetime -import json -import metrics - - -def get_paramiko_client(host, username, key_file): - key = paramiko.RSAKey.from_private_key_file(key_file) - - client = paramiko.SSHClient() - client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - client.connect(host, username=username, pkey=key) - return client - - -def submit_job(client: paramiko.SSHClient, workflow_dir, job_file): - """ - Submits Condor-Job and returns the ID and a (start, end) of the sub-id range as a Dict. - """ - stdin, stdout, stderr = client.exec_command("cd {wf_dir}; condor_submit {job} -terse".format(wf_dir=workflow_dir, - job=job_file)) - - error = "" - for err in stderr: - error += err - - if error != "": - raise ValueError("An error with condor_submit occurred: {error}".format(error=error)) - - for output in stdout: - if output.find("ERROR") != -1: - raise Exception(output) - job_id = output.split(".")[0] - id_range = tuple(output.replace("\n", "").split(" - ")) - - return { - "id": job_id, - "range": id_range - } - - -def get_job_status(client: paramiko.SSHClient, job_id): - """ - Determines, job-status. If all jobs were run (status="done"), total_jobs and everything else will be 0 - """ - stdin, stdout, stderr = client.exec_command("condor_q {job_id}".format(job_id=job_id)) - - status = None - for output in stdout: - if output.find("jobs;") != -1: - status = list(map(int, re.findall(r'\d+', output))) - - if status is None or len(status) != 7: - raise Exception("Couldn't parse condor_q") - - return { - "status": "done" if status[0] == 0 or (status[3] == 0 and status[4] == 0) else "running", - "total_jobs": status[0], - "completed": status[1], - "idle": status[3], - "running": status[4], - "held": status[5] - } - - -def get_condor_history(client: paramiko.SSHClient, first_id: float, last_id: float = float("inf")) -> Dict[str, Dict]: - """ - Returns condor_history as a list of jobs. Returns all job_ids >= first_id - """ - output_filename = "condor_history_" + str(datetime.now().timestamp()) + ".json" - stdin, stdout, stderr = client.exec_command("condor_history -backwards -json -since {i} > {filename}" - .format(i=int(first_id)-1, filename=output_filename)) - - error = "" - for err in stderr: - error += err - - if error != "": - raise ValueError("An error with condor_history occurred: {error}".format(error=error)) - - # Get output file - ftp_client = client.open_sftp() - ftp_client.get(output_filename, "results/"+output_filename) - ftp_client.close() - with open("results/"+output_filename) as json_file: - job_list = json.load(json_file) - - result = {} - for job in job_list: - job["parsed_job_metrics"] = metrics.parse_condor_job_metrics(job) - job["id"] = job["GlobalJobId"] - result[job["id"]] = job - - return result diff --git a/galaxy_benchmarker/destination.py b/galaxy_benchmarker/destination.py deleted file mode 100644 index 025f20d1..00000000 --- a/galaxy_benchmarker/destination.py +++ /dev/null @@ -1,312 +0,0 @@ -""" -Definition of different destination-types for workflows. -""" -from __future__ import annotations -import ansible_bridge -import planemo_bridge -import condor_bridge -import metrics -import logging -import time -from multiprocessing import Pool, TimeoutError -from typing import Dict -from task import BaseTask, AnsiblePlaybookTask, BenchmarkerTask -from galaxy_bridge import Galaxy -from bioblend.galaxy import GalaxyInstance -from jinja2 import Template -# from workflow import GalaxyWorkflow, CondorWorkflow - -log = logging.getLogger("GalaxyBenchmarker") - - -class BaseDestination: - def __init__(self, name): - self.name = name - - def run_workflow(self, workflow: BaseWorkflow) -> Dict: - """ - Runs the given workflow on Destination. Should return a Dict describing the result. - Needs to be implemented by child. - """ - raise NotImplementedError - - def run_task(self, task: BaseTask): - """ - Runs a given task on the Destination. - """ - if task is None: - return - if type(task) is AnsiblePlaybookTask: - self.run_ansible_playbook_task(task) - - def run_ansible_playbook_task(self, task: AnsiblePlaybookTask): - raise NotImplementedError - - def __str__(self): - return self.name - - __repr__ = __str__ - - -class GalaxyDestination(BaseDestination): - host = "" - host_user = "" - ssh_key = "" - tool_dependency_dir = "/data/share/tools" - jobs_directory_dir = "/data/share/staging" - persistence_dir = "/data/share/persisted_data" - galaxy_user_name = "" - galaxy_user_key = "" - - def __init__(self, name, glx: Galaxy, galaxy_user_name=None, galaxy_user_key=None): - super().__init__(name) - self.galaxy = glx - self.galaxy_user_name = galaxy_user_name - self.galaxy_user_key = galaxy_user_key - - if self.galaxy_user_name is None or self.galaxy_user_key is None: - self._create_galaxy_destination_user(glx) - - def _create_galaxy_destination_user(self, glx): - """ - Creates a user specifically for this Destination-Instance. This one is later used for routing the jobs - to the right Pulsar-Server. - """ - self.galaxy_user_name = str.lower("dest_user_" + self.name) - _, self.galaxy_user_key = glx.create_user(self.galaxy_user_name) - - def run_ansible_playbook_task(self, task: AnsiblePlaybookTask): - """ - Runs the given AnsiblePlaybookTask on the destination. - """ - ansible_bridge.run_playbook(task.playbook, self.host, self.host_user, self.ssh_key, - {"tool_dependency_dir": self.tool_dependency_dir, - "jobs_directory_dir": self.jobs_directory_dir, - "persistence_dir": self.persistence_dir}) - - def get_jobs(self, history_name) -> Dict: - """ - Get all jobs together with their details from a given history_name. - """ - glx_instance = self.galaxy.impersonate(user_key=self.galaxy_user_key) - job_ids = get_job_ids_from_history_name(history_name, glx_instance) - - infos = dict() - for job_id in job_ids: - infos[job_id] = self.galaxy.instance.jobs.show_job(job_id, full_details=True) - - # Get JobMetrics and parse them for future usage in influxDB - infos[job_id]["job_metrics"] = self.galaxy.instance.jobs.get_metrics(job_id) - infos[job_id]["parsed_job_metrics"] = metrics.parse_galaxy_job_metrics(infos[job_id]["job_metrics"]) - - return infos - - def run_workflow(self, workflow: GalaxyWorkflow) -> Dict: - """ - Runs the given workflow on PulsarMQDestination. Returns Dict of the status and - history_name of the finished workflow. - """ - log.info("Running workflow '{wf_name}' using Planemo".format(wf_name=workflow.name)) - - start_time = time.monotonic() - - if workflow.timeout is None: - result = planemo_bridge.run_planemo(self.galaxy, self, workflow.path) - else: - # Run inside a Process to enable timeout - pool = Pool(processes=1) - pool_result = pool.apply_async(planemo_bridge.run_planemo, (self.galaxy, self, workflow.path)) - - try: - result = pool_result.get(timeout=workflow.timeout) - except TimeoutError: - log.info("Timeout after {timeout} seconds".format(timeout=workflow.timeout)) - result = {"status": "error"} - - result["total_workflow_runtime"] = time.monotonic() - start_time - - return result - - -class PulsarMQDestination(GalaxyDestination): - def __init__(self, name, glx: Galaxy, job_plugin_params: Dict, job_destination_params: Dict, amqp_url="", galaxy_user_name="", galaxy_user_key=""): - self.amqp_url = amqp_url - self.job_plugin_params = job_plugin_params - self.job_destination_params = job_destination_params - super().__init__(name, glx, galaxy_user_name, galaxy_user_key) - - -class GalaxyCondorDestination(GalaxyDestination): - def __init__(self, name, glx: Galaxy, job_plugin_params: Dict, job_destination_params: Dict, galaxy_user_name="", galaxy_user_key=""): - self.job_plugin_params = job_plugin_params - self.job_destination_params = job_destination_params - super().__init__(name, glx, galaxy_user_name, galaxy_user_key) - - -class CondorDestination(BaseDestination): - status_refresh_time = 0.5 # TODO: Figure out, if that timing is to fast - - def __init__(self, name, host, host_user, ssh_key, jobs_directory_dir): - super().__init__(name) - self.host = host - self.host_user = host_user - self.ssh_key = ssh_key - self.jobs_directory_dir = jobs_directory_dir - - def deploy_workflow(self, workflow: CondorWorkflow): - """ - Deploys the given workflow to the Condor-Server with Ansible. - """ - log.info("Deploying {workflow} to {destination}".format(workflow=workflow.name, destination=self.name)) - # Use ansible-playbook to upload *.job-file to Condor-Manager - values = { - "jobs_directory_dir": self.jobs_directory_dir, - "workflow_name": workflow.name, - "workflow_directory_path": workflow.path, - "condor_user": self.host_user - } - ansible_bridge.run_playbook("deploy_condor_workflow.yml", self.host, self.host_user, self.ssh_key, values) - - def run_workflow(self, workflow: CondorWorkflow) -> Dict: - """ - Runs the given workflow on CondorDestination. Returns Dict of ... - """ - ssh_client = condor_bridge.get_paramiko_client(self.host, self.host_user, self.ssh_key) - - remote_workflow_dir = "{jobs_dir}/{wf_name}".format(jobs_dir=self.jobs_directory_dir, - wf_name=workflow.name) - - log.info("Submitting workflow '{wf}' to '{dest}'".format(wf=workflow, dest=self)) - start_time = time.monotonic() - job_ids = condor_bridge.submit_job(ssh_client, remote_workflow_dir, workflow.job_file) - submit_time = time.monotonic() - start_time - log.info("Submitted in {seconds} seconds".format(seconds=submit_time)) - - # Check every 0.1s if status has changed - status = "unknown" - while status != "done": - try: - job_status = condor_bridge.get_job_status(ssh_client, job_ids["id"]) - except ValueError as error: - status = "error" - log.error("There was an error with run of {workflow}: {error}".format(workflow=self.name, - error=error)) - break - status = job_status["status"] - time.sleep(self.status_refresh_time) - - total_workflow_runtime = time.monotonic() - start_time - - log.info("Fetching condor_history") - jobs = condor_bridge.get_condor_history(ssh_client, float(job_ids["id"]), float(job_ids["id"])) - - result = { - "id": job_ids["id"], - "id_range": job_ids["range"], - "status": "success" if status == "done" else "error", - "total_workflow_runtime": total_workflow_runtime, - "submit_time": submit_time, - "jobs": jobs - } - - ssh_client.close() - - return result - - -def configure_destination(dest_config, glx): - """ - Initializes and configures a Destination according to the given configuration. Returns the configured Destination. - """ - # Check, if all set properly - if "name" not in dest_config: - raise ValueError("No Destination-Name set! Config: '{config}'".format(config=dest_config)) - if "type" not in dest_config: - raise ValueError("No Destination-Type set for '{dest}'".format(dest=dest_config["name"])) - if dest_config["type"] not in ["Galaxy", "PulsarMQ", "Condor", "GalaxyCondor"]: - raise ValueError("Destination-Type '{type}' not valid".format(type=dest_config["type"])) - - job_plugin_params = dict() if "job_plugin_params" not in dest_config else dest_config["job_plugin_params"] - job_destination_params = dict() if "job_destination_params" not in dest_config else dest_config["job_destination_params"] - - galaxy_user_name = None if "galaxy_user_name" not in dest_config else dest_config["galaxy_user_name"] - galaxy_user_key = None if "galaxy_user_key" not in dest_config else dest_config["galaxy_user_key"] - - if dest_config["type"] == "Galaxy": - destination = GalaxyDestination(dest_config["name"], glx, galaxy_user_name, galaxy_user_key) - - if dest_config["type"] == "PulsarMQ": - destination = PulsarMQDestination(dest_config["name"], glx, job_plugin_params, job_destination_params, - dest_config["amqp_url"], - galaxy_user_name, galaxy_user_key) - if "host" in dest_config: - destination.host = dest_config["host"] - destination.host_user = dest_config["host_user"] - destination.ssh_key = dest_config["ssh_key"] - destination.tool_dependency_dir = dest_config["tool_dependency_dir"] - - if dest_config["type"] == "Condor": - destination = CondorDestination(dest_config["name"], dest_config["host"], dest_config["host_user"], - dest_config["ssh_key"], dest_config["jobs_directory_dir"]) - if "status_refresh_time" in dest_config: - destination.status_refresh_time = dest_config["status_refresh_time"] - - if dest_config["type"] == "GalaxyCondor": - destination = GalaxyCondorDestination(dest_config["name"], glx, job_plugin_params, job_destination_params, - galaxy_user_name, - galaxy_user_key) - - return destination - - -def create_galaxy_job_conf(glx: Galaxy, destinations: Dict[str, BaseDestination]): - """ - Creates the job_conf.xml-file for Galaxy using the given Dict (key: dest_name, value dest) and saves it to - galaxy_files/job_conf.xml.tmp. - """ - with open('galaxy_files/job_conf.xml') as file_: - template = Template(file_.read()) - - pulsar_destinations = list() - galaxy_condor_destinations = list() - job_plugin_params = dict() - job_destination_params = dict() - - for dest in destinations.values(): - if type(dest) is PulsarMQDestination: - pulsar_destinations.append(dest) - if type(dest) is GalaxyCondorDestination: - galaxy_condor_destinations.append(dest) - if issubclass(type(dest), PulsarMQDestination) or issubclass(type(dest), GalaxyCondorDestination): - job_plugin_params[dest.name] = dest.job_plugin_params - job_destination_params[dest.name] = dest.job_destination_params - - job_conf = template.render(galaxy=glx, - pulsar_destinations=pulsar_destinations, - galaxy_condor_destinations=galaxy_condor_destinations, - job_plugin_params=job_plugin_params, - job_destination_params=job_destination_params) - - with open("galaxy_files/job_conf.xml.tmp", "w") as fh: - fh.write(job_conf) - - -def get_job_ids_from_history_name(history_name, impersonated_instance: GalaxyInstance): - """ - For a given history_name return all its associated job-ids. As a history is only accessible from the user it - was created by, an impersonated_instance is needed. - """ - histories = impersonated_instance.histories.get_histories(name=history_name) - - if len(histories) >= 1: - history_id = histories[0]["id"] - dataset_ids = impersonated_instance.histories.show_history(history_id)["state_ids"]["ok"] - job_ids = list() - - for dataset_id in dataset_ids: - job_ids.append(impersonated_instance.histories.show_dataset(history_id, dataset_id)["creating_job"]) - - return job_ids - - return [] - diff --git a/galaxy_benchmarker/galaxy_bridge.py b/galaxy_benchmarker/galaxy_bridge.py deleted file mode 100644 index dba8fad2..00000000 --- a/galaxy_benchmarker/galaxy_bridge.py +++ /dev/null @@ -1,93 +0,0 @@ -from bioblend.galaxy import GalaxyInstance -from typing import Tuple, List -from workflow import BaseWorkflow, GalaxyWorkflow -import ansible_bridge -import planemo_bridge -import logging -import string -import random -import re - -log = logging.getLogger("GalaxyBenchmarker") - - -class Galaxy: - def __init__(self, url, user_key, shed_install=False, - ssh_user=None, ssh_key=None, galaxy_root_path=None, - galaxy_config_dir=None, galaxy_user=None): - self.url = url - self.user_key = user_key - self.shed_install = shed_install - self.ssh_user = ssh_user - self.ssh_key = ssh_key - - self.galaxy_root_path = galaxy_root_path - self.galaxy_config_dir = galaxy_config_dir - self.galaxy_user = galaxy_user - - self.instance = GalaxyInstance(url, key=user_key) - - def impersonate(self, user=None, user_key=None) -> GalaxyInstance: - """ - Returns a GalaxyInstance for the given user_key. If user is provided, - user_key is fetched from Galaxy. - """ - if user is not None: - user_id = self.instance.users.get_users(f_name=user)[0]["id"] - user_key = self.instance.users.get_user_apikey(user_id) - return GalaxyInstance(self.url, key=user_key) - - def create_user(self, username) -> Tuple: - """ - Creates a new user (if not already created) with username and a random password and returns - its user_id and api_key as a tuple. - """ - if len(self.instance.users.get_users(f_name=username)) == 0: - password = ''.join([random.choice(string.ascii_letters + string.digits) for _ in range(32)]) - self.instance.users.create_local_user(username, - "{username}@galaxy.uni.andreas-sk.de".format(username=username), - password) - - user_id = self.instance.users.get_users(f_name=username)[0]["id"] - user_key = self.instance.users.get_user_apikey(user_id) - - if user_key == "Not available.": - user_key = self.instance.users.create_user_apikey(user_id) - - return user_id, user_key - - def delete_all_histories_for_user(self, user, purge=True): - """ - Deletes and - if not set otherwise - purges for a given username all its histories. - """ - impersonated = self.impersonate(user) - histories = impersonated.histories.get_histories() - - for history in histories: - impersonated.histories.delete_history(history["id"], purge) - - def install_tools_for_workflows(self, workflows: List[BaseWorkflow]): - log.info("Installing all necessary workflow-tools on Galaxy.") - for workflow in workflows: - if type(workflow) is GalaxyWorkflow: - log.info("Installing tools for workflow '{workflow}'".format(workflow=workflow.name)) - planemo_bridge.install_workflow([workflow.path], self.instance) - - def deploy_job_conf(self): - """ - Deploys the job_conf.xml-file to the Galaxy-Server. - """ - # Hostname parsed from the Galaxy-URL - host = re.findall("^[a-z][a-z0-9+\-.]*://([a-z0-9\-._~%!$&'()*+,;=]+@)?([a-z0-9\-._~%]+|\[[a-z0-9\-." - + "_~%!$&'()*+,;=:]+\])", self.url)[0][1] - - if None in (self.ssh_user, self.ssh_key, self.galaxy_root_path, self.galaxy_config_dir, self.galaxy_user): - raise ValueError("ssh_user, ssh_key, galaxy_root_path, galaxy_config_dir, and galaxy_user need " - "to be set in order to deploy the job_conf.xml-file!") - - values = { - "galaxy_root_path": self.galaxy_root_path, - "galaxy_config_dir": self.galaxy_config_dir, - "galaxy_user": self.galaxy_user - } - ansible_bridge.run_playbook("prepare_galaxy.yml", host, self.ssh_user, self.ssh_key, values) diff --git a/galaxy_benchmarker/influxdb_bridge.py b/galaxy_benchmarker/influxdb_bridge.py deleted file mode 100644 index 1f82cc43..00000000 --- a/galaxy_benchmarker/influxdb_bridge.py +++ /dev/null @@ -1,60 +0,0 @@ -from influxdb import InfluxDBClient -from typing import Dict - - -class InfluxDB: - def __init__(self, host, port, username, password, db_name): - self.client = InfluxDBClient(host=host, port=port, username=username, password=password, - ssl=False, database=db_name, retries=20) - - def save_job_metrics(self, tags: Dict, job_results: Dict): - """ - Saves the parsed job-specific metrics (see metrics.py) to InfluxDB. - """ - if "parsed_job_metrics" not in job_results: - return [] - - json_points = [] - - for metric in job_results["parsed_job_metrics"].values(): - metric_tags = tags.copy() - - if "job_id" in job_results: - metric_tags["job_id"] = job_results["id"] - if "tool_id" in job_results: - metric_tags["tool_id"] = job_results["tool_id"] - - if "plugin" in metric: - metric_tags["plugin"] = metric["plugin"] - - json_points.append({ - "measurement": metric["name"], - "tags": metric_tags, - "fields": { - "value": metric["value"] - } - }) - - self.client.write_points(json_points) - - def save_workflow_metrics(self, tags: Dict, metrics: Dict): - """ - Saves the workflow-specific metrics to InfluxDB. - """ - json_points = [] - - for metric in metrics.values(): - metric_tags = tags.copy() - - if "plugin" in metric: - metric_tags["plugin"] = metric["plugin"] - - json_points.append({ - "measurement": metric["name"], - "tags": metric_tags, - "fields": { - "value": metric["value"] - } - }) - - self.client.write_points(json_points) diff --git a/galaxy_benchmarker/metrics.py b/galaxy_benchmarker/metrics.py deleted file mode 100644 index b1fc0866..00000000 --- a/galaxy_benchmarker/metrics.py +++ /dev/null @@ -1,140 +0,0 @@ -from typing import List, Dict -from datetime import datetime -import logging - -log = logging.getLogger("GalaxyBenchmarker") - -# All the metrics that can safely be parsed as a float_metric (see parse_galaxy_job_metrics) -galaxy_float_metrics = {"processor_count", "memtotal", "swaptotal", "runtime_seconds", "memory.stat.pgmajfault", - "cpu.stat.nr_throttled", "memory.stat.total_rss_huge", "memory.memsw.failcnt", - "memory.oom_control.under_oom", "memory.kmem.failcnt", "memory.stat.total_pgfault", - "cpu.stat.nr_periods", "cpuacct.stat.user", "memory.stat.active_file", "memory.stat.mapped_file", - "memory.stat.rss_huge", "memory.memsw.limit_in_bytes", "memory.use_hierarchy", "memory.stat.cache", - "memory.stat.hierarchical_memsw_limit", "memory.kmem.tcp.failcnt", "cpu.rt_runtime_us", - "memory.stat.hierarchical_memory_limit", "memory.stat.total_cache", "memory.kmem.max_usage_in_bytes", - "cpuacct.usage", "memory.stat.total_pgmajfault", "memory.kmem.usage_in_bytes", - "memory.stat.inactive_file", "memory.swappiness", "memory.move_charge_at_immigrate", - "memory.memsw.usage_in_bytes", "cpu.rt_period_us", "memory.stat.inactive_anon", "memory.stat.swap", - "memory.stat.active_anon", "memory.stat.pgpgin", "memory.stat.total_inactive_file", - "memory.oom_control.oom_kill_disable", "memory.stat.total_pgpgout", "memory.stat.total_unevictable", - "memory.kmem.tcp.limit_in_bytes", "memory.stat.pgpgout", "memory.usage_in_bytes", - "memory.failcnt", "memory.memsw.max_usage_in_bytes", "memory.limit_in_bytes", - "memory.soft_limit_in_bytes", "memory.kmem.tcp.max_usage_in_bytes", "memory.stat.total_rss", - "cpu.shares", "memory.stat.total_swap", "memory.stat.rss", "memory.kmem.tcp.usage_in_bytes", - "cpu.stat.throttled_time", "memory.stat.unevictable", "memory.kmem.limit_in_bytes", - "cpu.cfs_quota_us", "cpuacct.stat.system", "memory.stat.total_active_anon", - "memory.max_usage_in_bytes", "memory.stat.total_active_file", "memory.stat.total_mapped_file", - "cpu.cfs_period_us", "memory.stat.pgfault", "memory.stat.total_pgpgin", - "memory.stat.total_inactive_anon", "preprocessing_time", "tool_preparation_time", - "down_collection_time"} -galaxy_string_metrics = {"cpuacct.usage_percpu"} -condor_float_metrics = {"NumRestarts", "NumJobRestarts", "JobStatus"} -condor_string_metrics = {"LastRemoteHost", "GlobalJobId", "Cmd"} -condor_time_metrics = {"JobStartDate", "JobCurrentStartDate", "CompletionDate"} - - -def parse_galaxy_job_metrics(job_metrics: List) -> Dict[str, Dict]: - """ - Parses the more or less "raw" metrics from Galaxy, so they can later be ingested by InfluxDB. - """ - parsed_metrics = { - "staging_time": { - "name": "staging_time", - "type": "float", - "value": float(0) - }, - } - - jobstatus_queued = jobstatus_running = None - for metric in job_metrics: - try: - if metric["name"] in galaxy_float_metrics: - parsed_metrics[metric["name"]] = { - "name": metric["name"], - "type": "float", - "plugin": metric["plugin"], - "value": float(metric["raw_value"]) - } - if metric["name"] in galaxy_string_metrics: - parsed_metrics[metric["name"]] = { - "name": metric["name"], - "type": "string", - "plugin": metric["plugin"], - "value": metric["raw_value"] - } - # For calculating the staging time (if the metrics exist) - if metric["plugin"] == "jobstatus" and metric["name"] == "queued": - jobstatus_queued = datetime.strptime(metric["value"], "%Y-%m-%d %H:%M:%S.%f") - if metric["plugin"] == "jobstatus" and metric["name"] == "running": - jobstatus_running = datetime.strptime(metric["value"], "%Y-%m-%d %H:%M:%S.%f") - except ValueError as e: - log.error("Error while trying to parse Galaxy job metrics '{name} = {value}': {error}. Ignoring.." - .format(error=e, name=metric["name"], value=metric["raw_value"])) - - # Calculate staging time - if jobstatus_queued is not None and jobstatus_running is not None: - parsed_metrics["staging_time"]["value"] = float((jobstatus_running - jobstatus_queued).seconds + - (jobstatus_running - jobstatus_queued).microseconds * 0.000001) - - return parsed_metrics - - -def parse_condor_job_metrics(job_metrics: Dict) -> Dict[str, Dict]: - parsed_metrics = {} - - for key, value in job_metrics.items(): - try: - if key in condor_float_metrics: - parsed_metrics[key] = { - "name": key, - "type": "float", - "plugin": "condor_history", - "value": float(value) - } - if key in condor_string_metrics: - parsed_metrics[key] = { - "name": key, - "type": "string", - "plugin": "condor_history", - "value": value - } - if key in condor_time_metrics: - parsed_metrics[key] = { - "name": key, - "type": "timestamp", - "plugin": "condor_history", - "value": value * 1000 - } - if key == "JobStatus": - if value == 1: - status = "idle" - elif value == 2: - status = "running" - elif value == 3: - status = "removed" - elif value == 4: - status = "success" - elif value == 5: - status = "held" - elif value == 6: - status = "transferring output" - else: - status = "unknown" - parsed_metrics["job_status"] = { - "name": "job_status", - "type": "string", - "plugin": "condor_history", - "value": status - } - if key == "RemoteWallClockTime": - parsed_metrics["runtime_seconds"] = { - "name": "runtime_seconds", - "type": "float", - "plugin": "condor_history", - "value": float(value) - } - except ValueError as e: - log.error("Error while trying to parse Condor job metrics '{key} = {value}': {error}. Ignoring.." - .format(error=e, key=key, value=value)) - - return parsed_metrics diff --git a/galaxy_benchmarker/openstack_bridge.py b/galaxy_benchmarker/openstack_bridge.py deleted file mode 100644 index 3702eb22..00000000 --- a/galaxy_benchmarker/openstack_bridge.py +++ /dev/null @@ -1,48 +0,0 @@ -import novaclient.v2.servers -import novaclient.client -from typing import List -import logging - -log = logging.getLogger("GalaxyBenchmarker") - - -class OpenStackCompute: - def __init__(self, auth_url, compute_endpoint_version, username, password, - project_id, region_name, user_domain_name): - self.client = novaclient.client.Client(compute_endpoint_version, username=username, password=password, - project_id=project_id, auth_url=auth_url, - user_domain_name=user_domain_name, region_name=region_name) - - def get_servers(self, name_contains="") -> List[novaclient.v2.servers.Server]: - """ - Returns all servers that contain name_contains in their name. - """ - result = [] - servers = self.client.servers.list() - - for server in servers: - if server.name.find(name_contains) != -1: - result.append(server) - - return result - - def reboot_servers(self, servers: List[novaclient.v2.servers.Server], hard=False): - """ - Reboots the given servers. - Inspired by: https://gist.github.com/gangwang2/9228989 - """ - reboot_type = "HARD" if hard else "SOFT" - for server in servers: - if server.status == 'ACTIVE': - log.info("Rebooting server {name}".format(name=server.name)) - server.reboot(reboot_type) - - def rebuild_servers(self, servers: List[novaclient.v2.servers.Server]): - """ - Rebuilds the given servers with the current settings (images, cloud-init, etc). - """ - for server in servers: - if server.status == 'ACTIVE': - log.info("Rebuilding server {name}".format(name=server.name)) - image = server.image["id"] - server.rebuild(image) diff --git a/galaxy_benchmarker/planemo_bridge.py b/galaxy_benchmarker/planemo_bridge.py deleted file mode 100644 index 194421f8..00000000 --- a/galaxy_benchmarker/planemo_bridge.py +++ /dev/null @@ -1,68 +0,0 @@ -""" -Bridge between Planemo and GalaxyBenchmarker -""" -from __future__ import annotations -import random -import bioblend -import time -import logging -import urllib3 -from galaxy_bridge import Galaxy -# from destination import PulsarMQDestination -from planemo import options -from planemo.cli import Context -from planemo.engine import engine_context -from planemo.galaxy.test import handle_reports_and_summary -from planemo.runnable import for_paths -from planemo.galaxy.workflows import install_shed_repos -from typing import Dict - -log = logging.getLogger("GalaxyBenchmarker") - - -def run_planemo(glx: Galaxy, dest: PulsarMQDestination, workflow_path) -> Dict: - """ - Runs workflow with Planemo and returns a dict of the status and history_name of the finished workflow. - """ - return _cli(Context(), [workflow_path], glx, dest.galaxy_user_key) - - -def install_workflow(workflow_path, glx_instance): - """ - Installs the tools necessary to run a given workflow (given as a path to the workflow). - """ - runnable = for_paths(workflow_path)[0] - install_shed_repos(runnable, glx_instance, False) - - -@options.galaxy_target_options() -@options.galaxy_config_options() -@options.test_options() -@options.engine_options() -def _cli(ctx, paths, glx, user_key, **kwds) -> Dict: - """ - Run specified tool's tests within Galaxy. - Returns a dict of the status and history_name of the finished workflow. - See https://github.com/galaxyproject/planemo/blob/master/planemo/commands/cmd_test.py - """ - kwds["engine"] = "external_galaxy" - kwds["shed_install"] = False - kwds["galaxy_url"] = glx.url - kwds["galaxy_admin_key"] = glx.user_key - kwds["history_name"] = "galaxy_benchmarker-" + str(time.time_ns()) + str(random.randrange(0, 99999)) - - if user_key is not None: - kwds["galaxy_user_key"] = user_key - - runnables = for_paths(paths) - - try: - with engine_context(ctx, **kwds) as engine: - test_data = engine.test(runnables) - exit_code = handle_reports_and_summary(ctx, test_data.structured_data, kwds=kwds) - status = "success" if exit_code == 0 else "error" - except Exception as e: - log.error("There was an error: {e}".format(e=e)) - status = "error" - - return {"status": status, "history_name": kwds["history_name"]} diff --git a/galaxy_benchmarker/task.py b/galaxy_benchmarker/task.py deleted file mode 100644 index 417d9fa3..00000000 --- a/galaxy_benchmarker/task.py +++ /dev/null @@ -1,93 +0,0 @@ -from random import randrange -from typing import Dict - -class BaseTask: - def __init__(self, benchmark): - self.benchmark = benchmark - - def run(self): - raise NotImplementedError - - -class AnsiblePlaybookTask(BaseTask): - def __init__(self, benchmark, playbook): - self.playbook = playbook - super().__init__(benchmark) - - def run(self): - for destination in self.benchmark.destinations: - destination.run_ansible_playbook_task(self) - - def __str__(self): - return "Ansible Playbook: " + self.playbook - - __repr__ = __str__ - - -class BenchmarkerTask(BaseTask): - def __init__(self, benchmark, name, params=dict()): - self.name = name - self.params = params - super().__init__(benchmark) - - def run(self): - if self.name == "delete_old_histories": - for destination in self.benchmark.destinations: - self._delete_old_histories(destination) - elif self.name == "reboot_openstack_servers": - self._reboot_openstack_servers() - elif self.name == "reboot_random_openstack_server": - self._reboot_random_openstack_server() - elif self.name == "rebuild_random_openstack_server": - self._rebuild_random_openstack_server() - else: - raise ValueError("{name} is not a valid BenchmarkerTask!".format(name=self.name)) - - def _delete_old_histories(self, destination): - destination.galaxy.delete_all_histories_for_user(destination.galaxy_user_name, True) - - def _reboot_openstack_servers(self): - if "name_contains" not in self.params: - raise ValueError("'name_contains' is needed for rebooting openstack servers") - reboot_type = self.params["reboot_type"] if "reboot_type" in self.params else "soft" - - os = self.benchmark.benchmarker.openstack - servers = os.get_servers(self.params["name_contains"]) - os.reboot_servers(servers, reboot_type == "hard") - - def _reboot_random_openstack_server(self): - if "name_contains" not in self.params: - raise ValueError("'name_contains' is needed for rebooting openstack servers") - reboot_type = self.params["reboot_type"] if "reboot_type" in self.params else "soft" - - os = self.benchmark.benchmarker.openstack - servers = os.get_servers(self.params["name_contains"]) - - rand_index = randrange(0, len(servers)) - os.reboot_servers([servers[rand_index]], reboot_type == "hard") - - def _rebuild_random_openstack_server(self): - if "name_contains" not in self.params: - raise ValueError("'name_contains' is needed for rebuilding openstack servers") - - os = self.benchmark.benchmarker.openstack - servers = os.get_servers(self.params["name_contains"]) - - rand_index = randrange(0, len(servers)) - os.rebuild_servers([servers[rand_index]]) - - def __str__(self): - return self.name - - __repr__ = __str__ - - -def configure_task(task_conf: Dict, benchmark): - if task_conf["type"] == "AnsiblePlaybook": - return AnsiblePlaybookTask(benchmark, task_conf["playbook"]) - - if task_conf["type"] == "BenchmarkerTask": - params = task_conf["params"] if "params" in task_conf else {} - return BenchmarkerTask(benchmark, task_conf["name"], params) - - raise ValueError("Task type '{type}' not allowed!".format(type=task_conf["type"])) diff --git a/galaxy_benchmarker/typing.py b/galaxy_benchmarker/typing.py new file mode 100644 index 00000000..d16c5128 --- /dev/null +++ b/galaxy_benchmarker/typing.py @@ -0,0 +1,10 @@ +from typing import Any + +# Mapping { name -> configdict } +NamedConfigDicts = dict[str, dict[str, Any]] + +RunName = str +RunResult = dict[str, str | int | float] +RunResults = list[RunResult] + +BenchmarkResults = dict[RunName, RunResults] diff --git a/galaxy_benchmarker/utils/__init__.py b/galaxy_benchmarker/utils/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/galaxy_benchmarker/utils/ansible.py b/galaxy_benchmarker/utils/ansible.py new file mode 100644 index 00000000..6e77e81d --- /dev/null +++ b/galaxy_benchmarker/utils/ansible.py @@ -0,0 +1,158 @@ +from __future__ import annotations + +import logging +import subprocess +import tempfile +from pathlib import Path +from typing import TYPE_CHECKING, Any, Dict, Optional + +if TYPE_CHECKING: + from galaxy_benchmarker.typing import NamedConfigDicts + +log = logging.getLogger(__name__) + +LOG_ANSIBLE_OUTPUT = False + +_running_playbook: Optional[subprocess.Popen] = None + + +def run_playbook(playbook: Path, host: str, extra_vars: Dict = {}): + """Run ansible-playbook with the given parameters. Additional variables + can be given in values as a dict. + """ + commands = [ + "ansible-playbook", + str(playbook), + "-i", + "ansible/inventory", + "-e", + f"host={host}", + ] + + for key, value in extra_vars.items(): + commands.append("-e") + commands.append(f"{key}={value}") + + log.log( + logging.INFO if LOG_ANSIBLE_OUTPUT else logging.DEBUG, + "Run ansible: %s", + commands, + ) + + with tempfile.TemporaryFile() as cached_output: + global _running_playbook + process = subprocess.Popen( + commands, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + _running_playbook = process + + while process.poll() is None: + assert process.stdout, "Stdout of ansible subprocess is None" + output = process.stdout.readline() + cached_output.write(output) + if LOG_ANSIBLE_OUTPUT: + log.info(output.decode("utf-8")) + + if process.returncode != 0: + if not LOG_ANSIBLE_OUTPUT: + cached_output.seek(0) + for line in cached_output.readlines(): + log.error(line.decode("utf-8")) + raise RuntimeError( + f"Ansible exited with non-zero exit code: {process.returncode}" + ) + + +def stop_playbook(signal: int) -> None: + """Stop possible running playbook""" + if _running_playbook is None: + return + _running_playbook.send_signal(signal) + + +_tasks: NamedConfigDicts = {} + + +class AnsibleTask: + def __init__( + self, + playbook: str, + playbook_folder: str = "ansible/", + name: Optional[str] = None, + host: str = "", + extra_vars: dict = {}, + *args, + **kwargs, + ) -> None: + """ + playbook: Name of the playbook which will be executed + playbook_folder: path to the playbook folder + name: Displayname used for logging and stuff + host: ansible host or host_pattern + extra_vars: Default extra_vars used when calling run() + """ + if name: + self.name = name + else: + self.name = playbook.split(".")[0] + + if not playbook: + raise ValueError(f"'playbook' property is missing for task {self.name}") + + self.playbook = Path(playbook_folder) / playbook + if not self.playbook.is_file(): + raise ValueError( + f"Playbook for task {self.name} is not a vaild file. Path: '{self.playbook}'" + ) + + if not isinstance(host, str): + raise ValueError( + f"'host' property has to be of type 'str' for task {self.name}" + ) + self.host = host + + if not isinstance(extra_vars, dict): + raise ValueError( + f"'extra_vars' property has to be of type 'dict' for task {self.name}" + ) + self.extra_vars = extra_vars + + @staticmethod + def register(configs: NamedConfigDicts) -> None: + """Register name -> config pairs for later use. Tasks can be referenced by + name and will be substituted with the values given here""" + global _tasks + _tasks = configs + + @staticmethod + def from_config(task_config: Any, name: str) -> AnsibleTask: + """Create a task from task_config. Config can be: + - an object defining the task + - a string refering to a task definition + """ + t_name, t_config = name, task_config + if isinstance(task_config, str): + if task_config not in _tasks: + raise ValueError(f"Unknown task reference {task_config}") + t_config = _tasks[task_config] + t_name = task_config + + if not isinstance(t_config, dict): + raise ValueError( + f"Expected dict as task config for {t_name}. Received {type(t_config)}" + ) + + return AnsibleTask(**t_config) + + def run(self) -> None: + """Run AnsibleTask on hosts given during creation""" + if not self.host: + raise ValueError( + f"'host' is required, when task is executed through 'run()' ({self.name})" + ) + + self.run_at(self.host, self.extra_vars) + + def run_at(self, host: str, extra_vars: dict = {}) -> None: + """Run AnsibleTask on specified host/hosts""" + run_playbook(self.playbook, host, extra_vars) diff --git a/galaxy_benchmarker/utils/destinations.py b/galaxy_benchmarker/utils/destinations.py new file mode 100644 index 00000000..e1abea5f --- /dev/null +++ b/galaxy_benchmarker/utils/destinations.py @@ -0,0 +1,30 @@ +import dataclasses + + +@dataclasses.dataclass +class BenchmarkDestination: + host: str = "" + + def __post_init__(self): + if not self.host: + raise ValueError(f"Property 'host' is missing for BenchmarkDestination") + + @property + def name(self): + return f"{self.host}" + + +@dataclasses.dataclass +class PosixBenchmarkDestination(BenchmarkDestination): + target_folder: str = "" + + def __post_init__(self): + super().__post_init__() + if not self.target_folder: + raise ValueError( + f"Property 'target_folder' is missing for host '{self.host}'" + ) + + @property + def name(self): + return f"{self.host}_{self.target_folder.replace('/','_')}" diff --git a/galaxy_benchmarker/utils/s3.py b/galaxy_benchmarker/utils/s3.py new file mode 100644 index 00000000..7623f642 --- /dev/null +++ b/galaxy_benchmarker/utils/s3.py @@ -0,0 +1,100 @@ +import dataclasses +import logging +import os +import time + +import boto3 + +log = logging.getLogger(__name__) + + +@dataclasses.dataclass +class S3Config: + base_url: str + bucket_name: str + + @property + def access_key_id(self): + access_key = os.getenv("AWS_ACCESS_KEY_ID") + if access_key is None: + raise ValueError("Missing S3 credentials in env vars: AWS_ACCESS_KEY_ID") + return access_key + + @property + def secret_access_key(self): + secret_key = os.getenv("AWS_SECRET_ACCESS_KEY") + if secret_key is None: + raise ValueError( + "Missing S3 credentials in env vars: AWS_SECRET_ACCESS_KEY" + ) + return secret_key + + +def empty_bucket(config: S3Config) -> None: + client = boto3.resource( + "s3", + aws_access_key_id=config.access_key_id, + aws_secret_access_key=config.secret_access_key, + endpoint_url=config.base_url, + ) + bucket = client.Bucket(config.bucket_name) + bucket.objects.all().delete() + + +def check_bucket_for_files( + config: S3Config, expected_num_files: int, expected_size_in_bytes: int, timeout: int +) -> None: + start_time = time.monotonic() + num_files = _get_current_num_files(config, expected_size_in_bytes) + + while num_files < expected_num_files: + log.info("Currently %d files present", num_files) + current_runtime = time.monotonic() - start_time + if current_runtime >= timeout: + raise RuntimeError("Verification timed out") + + time.sleep(5) + num_files = _get_current_num_files(config, expected_size_in_bytes) + + +def _get_current_num_files(config: S3Config, expected_size: int) -> int: + client = boto3.client( + "s3", + aws_access_key_id=config.access_key_id, + aws_secret_access_key=config.secret_access_key, + endpoint_url=config.base_url, + ) + + prefix = "" + common_pre = None + + # Search for the prefix where the fanout happens + ## i.e. /000,/001 -> "/" + ## /home/rods/000,/home/rods/000 -> "/home/rods" + while True: + result = client.list_objects_v2( + Bucket=config.bucket_name, Delimiter="/", Prefix=prefix + ) + if "CommonPrefixes" not in result: + break + + common_pre = result["CommonPrefixes"] + if len(common_pre) == 1: + prefix = common_pre[0]["Prefix"] + continue + break + + if common_pre is None: + # Bucket is empty, no prefixes -> No files + return 0 + + # Each prefix contains 1000 files, except the last one + num = (len(common_pre[:-1])) * 1000 + # Subtract one because prefix 000 only has 999 files + num = max(0, num - 1) + + last_prefix = common_pre[-1]["Prefix"] + resp = client.list_objects_v2(Bucket=config.bucket_name, Prefix=last_prefix) + num += len(list(item for item in resp["Contents"] if item["Size"] == expected_size)) + + return num diff --git a/galaxy_benchmarker/workflow.py b/galaxy_benchmarker/workflow.py deleted file mode 100644 index 53363576..00000000 --- a/galaxy_benchmarker/workflow.py +++ /dev/null @@ -1,75 +0,0 @@ -""" -Definition of different workflow-types. -""" -import os -import logging -from typing import Dict - -log = logging.getLogger("GalaxyBenchmarker") - - -class BaseWorkflow: - description = "" - - def __init__(self, name, path): - self.path = path - self.name = name - - def __str__(self): - return self.name - - __repr__ = __str__ - - -class GalaxyWorkflow(BaseWorkflow): - timeout = None - - def __init__(self, name, path): - # Make sure that workflow file exists - if not os.path.isfile(path): - raise IOError("Workflow-File at '{path}' in workflow '{wf_name}' could not be found".format(path=path, - wf_name=name)) - super().__init__(name, path) - - -class CondorWorkflow(BaseWorkflow): - def __init__(self, name, path, job_file): - super().__init__(name, path) - - # Check, if all workflow-directory exist - if not os.path.isdir(path): - raise IOError("Workflow-Directory at '{path}' in workflow '{wf_name}' could not be found" - .format(path=self.path, wf_name=name)) - - # Check, if condor-job_file exists - job_file_path = path + "/" + job_file - if not os.path.isfile(job_file_path): - raise IOError("Job-File at '{path}' in workflow '{wf_name}' could not be found".format(path=job_file_path, - wf_name=name)) - self.job_file = job_file - - -def configure_workflow(wf_config: Dict) -> BaseWorkflow: - """ - Initializes and configures a Workflow according to the given configuration. Returns the configured Workflow. - """ - # Check, if all set properly - if "name" not in wf_config: - raise ValueError("No Workflow-Name set! Config: '{config}'".format(config=wf_config)) - if "path" not in wf_config: - raise ValueError("No Workflow-Path set for '{workflow}'".format(workflow=wf_config["name"])) - if "type" not in wf_config: - raise ValueError("No Workflow-Type set for '{workflow}'".format(workflow=wf_config["name"])) - if wf_config["type"] not in ["Galaxy", "Condor"]: - raise ValueError("Workflow-Type '{type}' not valid".format(type=wf_config["type"])) - - if wf_config["type"] == "Galaxy": - workflow = GalaxyWorkflow(wf_config["name"], wf_config["path"]) - if "description" in wf_config: - workflow.description = wf_config["description"] - workflow.timeout = None if "timeout" not in wf_config else wf_config["timeout"] - - if wf_config["type"] == "Condor": - workflow = CondorWorkflow(wf_config["name"], wf_config["path"], wf_config["job_file"]) - - return workflow diff --git a/galaxy_files/dynamic_destination.py b/galaxy_files/dynamic_destination.py deleted file mode 100644 index c51add0a..00000000 --- a/galaxy_files/dynamic_destination.py +++ /dev/null @@ -1,6 +0,0 @@ -def dynamic_destination(user): - username = user.username - if username.startswith("dest_user_"): - return username[10:] - - return "local" diff --git a/galaxy_files/job_conf.xml b/galaxy_files/job_conf.xml deleted file mode 100644 index cf2425a0..00000000 --- a/galaxy_files/job_conf.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - {% for destination in pulsar_destinations %} - - {{ galaxy.url }} - {{ destination.amqp_url }} - {% for key, value in job_plugin_params[destination.name].items() %} - {{ value }} - {% endfor %} - - {% endfor %} - {% for destination in galaxy_condor_destinations %} - - {% endfor %} - - - - python - dynamic_destination - - - {% for destination in pulsar_destinations %} - - /data/share/staging - {{ destination.jobs_directory_dir }} - {{ destination.persistence_dir }} - {% for key, value in job_destination_params[destination.name].items() %} - {{ value }} - {% endfor %} - $_JAVA_OPTIONS -Xmx2048M -Xms256m - {{ galaxy.galaxy_config_dir }}/pulsar_actions.yml - - {% endfor %} - {% for destination in galaxy_condor_destinations %} - - {% endfor %} - - - - - - - - - diff --git a/galaxy_files/job_metrics_conf.xml b/galaxy_files/job_metrics_conf.xml deleted file mode 100644 index 3ca13b4c..00000000 --- a/galaxy_files/job_metrics_conf.xml +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/galaxy_files/job_status.py b/galaxy_files/job_status.py deleted file mode 100644 index 48f62717..00000000 --- a/galaxy_files/job_status.py +++ /dev/null @@ -1,43 +0,0 @@ -import logging -from sqlalchemy import create_engine -from sqlalchemy.sql import text - -from . import InstrumentPlugin -from .. import formatting - - -log = logging.getLogger(__name__) - - -class StatusFormatter(formatting.JobMetricFormatter): - - def format(self, key, value): - return key, value - - -class JobStatusPlugin(InstrumentPlugin): - """ Gather status - """ - plugin_type = "jobstatus" - formatter = StatusFormatter() - - def __init__(self, **kwargs): - pass - - def job_properties(self, job_id, job_directory): - return self._get_job_state_history(job_id) - - def _get_job_state_history(self, job_id): - engine = create_engine('postgresql:///galaxy?host=/var/run/postgresql') # TODO: Get it from Galaxy.yml - with engine.connect() as con: - query = text("SELECT update_time, state FROM job_state_history WHERE job_id = :job_id order by create_time") - query_res = con.execute(query, {"job_id": job_id}) - - job_state_history = dict() - for res in query_res: - job_state_history[res[1]] = res[0] - - return job_state_history - - -__all__ = ('JobStatusPlugin', ) \ No newline at end of file diff --git a/galaxy_files/pulsar_actions.yml b/galaxy_files/pulsar_actions.yml deleted file mode 100644 index 44ef8f59..00000000 --- a/galaxy_files/pulsar_actions.yml +++ /dev/null @@ -1,11 +0,0 @@ -paths: -# - path: /galaxy/data -# path_types: unstructured -# action: rewrite -# source_directory: /srv/galaxy/jobs/**/**/metadata/**.bai -# destination_directory: /work/galaxy/data - - path: /cvmfs/data.galaxyproject.org/byhand/dm3/bwa_index_v0.5.9-r16/dm3.fa - path_types: unstructured - action: rewrite - source_directory: /cvmfs/data.galaxyproject.org/byhand/dm3/bwa_index_v0.5.9-r16 - destination_directory: /cvmfs/data.galaxyproject.org/byhand/dm3/bwa_index diff --git a/galaxy_files/sleep.xml b/galaxy_files/sleep.xml deleted file mode 100644 index cda47a88..00000000 --- a/galaxy_files/sleep.xml +++ /dev/null @@ -1,14 +0,0 @@ - - sleep - - - - - - - - -sleep $time; echo "finished after $time seconds" > $out_file1 - - Sleep for x seconds. Arbitrary data can be uploaded just for fun.. - \ No newline at end of file diff --git a/galaxy_files/staging_time.py b/galaxy_files/staging_time.py deleted file mode 100644 index 9278383f..00000000 --- a/galaxy_files/staging_time.py +++ /dev/null @@ -1,50 +0,0 @@ -"""The module describes the ``uname`` job metrics plugin.""" -from . import InstrumentPlugin -from .. import formatting -import os.path -import logging - -log = logging.getLogger(__name__) - - -class StagingTimeFormatter(formatting.JobMetricFormatter): - - def format(self, key, value): - return key, value - - -class StagingTimePlugin(InstrumentPlugin): - """ Use uname to gather operating system information about remote system - job is running on. Linux only. - """ - plugin_type = "staging_time" - formatter = StagingTimeFormatter() - - def __init__(self, **kwargs): - pass - - def pre_execute_instrument(self, job_directory): - return "touch %s" % self._instrument_file_path(job_directory, "down_collection_time") - - def job_properties(self, job_id, job_directory): - result = {} - - preprocess_time_path = self._instrument_file_path(job_directory, "preprocessing_time") - if os.path.isfile(preprocess_time_path): - with open(preprocess_time_path) as f: - result["preprocessing_time"] = f.read() - - tool_preparation_time_path = self._instrument_file_path(job_directory, "tool_preparation_time") - if os.path.isfile(tool_preparation_time_path): - with open(tool_preparation_time_path) as f: - result["tool_preparation_time"] = f.read() - - collection_time_path = self._instrument_file_path(job_directory, "down_collection_time") - if os.path.isfile(collection_time_path): - with open(collection_time_path) as f: - result["down_collection_time"] = f.read() - - return result - - -__all__ = ('StagingTimePlugin', ) \ No newline at end of file diff --git a/grafana/config.ini b/grafana/config.ini deleted file mode 100644 index 6a2c0347..00000000 --- a/grafana/config.ini +++ /dev/null @@ -1,2 +0,0 @@ -[paths] -provisioning = /etc/grafana/provisioning \ No newline at end of file diff --git a/grafana/dashboards/Burst Benchmarks.json b/grafana/dashboards/Burst Benchmarks.json deleted file mode 100644 index ad112c30..00000000 --- a/grafana/dashboards/Burst Benchmarks.json +++ /dev/null @@ -1,1645 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "editable": true, - "gnetId": null, - "graphTooltip": 0, - "id": 2, - "iteration": 1563785924621, - "links": [], - "panels": [ - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 35, - "panels": [], - "title": "Status", - "type": "row" - }, - { - "aliasColors": {}, - "bars": true, - "dashLength": 10, - "dashes": false, - "datasource": "glx_benchmarker", - "fill": 1, - "gridPos": { - "h": 6, - "w": 24, - "x": 0, - "y": 1 - }, - "id": 39, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "hideEmpty": false, - "hideZero": false, - "max": false, - "min": false, - "show": false, - "total": false, - "values": false - }, - "lines": false, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": {}, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [ - { - "alias": "/error/", - "color": "#E02F44" - }, - { - "alias": "/success/", - "color": "#56A64B" - } - ], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "alias": "$tag_benchmark_uuid: $col", - "groupBy": [ - { - "params": [ - "benchmark_uuid" - ], - "type": "tag" - } - ], - "measurement": "workflow_status", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT success, error FROM (SELECT count(\"value\") as success FROM \"workflow_status\" WHERE (\"value\" = 'success') AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND $timeFilter GROUP BY \"benchmark_uuid\"), (SELECT count(\"value\") as error FROM \"workflow_status\" WHERE (\"value\" = 'error') AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND $timeFilter GROUP BY \"benchmark_uuid\") WHERE $timeFilter GROUP BY benchmark_uuid", - "rawQuery": true, - "refId": "A", - "resultFormat": "time_series", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "count" - } - ] - ], - "tags": [ - { - "key": "value", - "operator": "=", - "value": "error" - } - ] - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Workflow Status", - "tooltip": { - "shared": false, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "series", - "name": null, - "show": true, - "values": [ - "total" - ] - }, - "yaxes": [ - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": true, - "dashLength": 10, - "dashes": false, - "datasource": "glx_benchmarker", - "fill": 1, - "gridPos": { - "h": 6, - "w": 24, - "x": 0, - "y": 7 - }, - "id": 43, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "hideEmpty": false, - "hideZero": false, - "max": false, - "min": false, - "show": false, - "total": false, - "values": false - }, - "lines": false, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": {}, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [ - { - "alias": "/error/", - "color": "#E02F44" - }, - { - "alias": "/success/", - "color": "#56A64B" - } - ], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "alias": "$tag_benchmark_uuid: $col", - "groupBy": [ - { - "params": [ - "benchmark_uuid" - ], - "type": "tag" - } - ], - "measurement": "workflow_status", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT success, error FROM (SELECT count(\"value\") as success FROM \"job_status\" WHERE (\"value\" = 'success') AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND $timeFilter GROUP BY \"benchmark_uuid\"), (SELECT count(\"value\") as error FROM \"job_status\" WHERE (\"value\" != 'success') AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND $timeFilter GROUP BY \"benchmark_uuid\") WHERE $timeFilter GROUP BY benchmark_uuid", - "rawQuery": true, - "refId": "A", - "resultFormat": "time_series", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "count" - } - ] - ], - "tags": [ - { - "key": "value", - "operator": "=", - "value": "error" - } - ] - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Job Status", - "tooltip": { - "shared": false, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "series", - "name": null, - "show": true, - "values": [ - "total" - ] - }, - "yaxes": [ - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 13 - }, - "id": 25, - "panels": [], - "title": "Runtime", - "type": "row" - }, - { - "columns": [], - "datasource": "glx_benchmarker", - "fontSize": "100%", - "gridPos": { - "h": 16, - "w": 12, - "x": 0, - "y": 14 - }, - "id": 16, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "hidden" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "s" - } - ], - "targets": [ - { - "alias": "CPU-Time per Workflow", - "groupBy": [ - { - "params": [ - "benchmark_uid" - ], - "type": "tag" - }, - { - "params": [ - "destination_name" - ], - "type": "tag" - }, - { - "params": [ - "workflow_name" - ], - "type": "tag" - } - ], - "measurement": "staging_time", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT value FROM \"runtime_seconds\" WHERE (\"benchmark_type\" = '') AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\"", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "sum" - } - ] - ], - "tags": [ - { - "key": "run_type", - "operator": "=", - "value": "warm" - }, - { - "condition": "AND", - "key": "benchmark_type", - "operator": "=", - "value": "" - } - ] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Job runtime_seconds", - "transform": "table", - "type": "table" - }, - { - "columns": [], - "datasource": "glx_benchmarker", - "fontSize": "100%", - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 14 - }, - "id": 19, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "date" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "decimals": 2, - "mappingType": 1, - "pattern": "(median|mean|max|min)", - "thresholds": [], - "type": "number", - "unit": "s" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "short" - } - ], - "targets": [ - { - "alias": "$tag_ benchmark_uuid: $tag_workflow_name", - "groupBy": [], - "measurement": "runtime_seconds", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT median(\"value\"), mean(\"value\"), max(\"value\"), min(\"value\") FROM (SELECT value FROM \"runtime_seconds\" WHERE (\"benchmark_type\" = '') AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND $timeFilter GROUP BY \"workflow_name\", \"benchmark_uuid\", \"history_name\") WHERE $timeFilter GROUP BY \"benchmark_uuid\", \"workflow_name\" ", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - } - ] - ], - "tags": [ - { - "key": "benchmark_type", - "operator": "=", - "value": "" - } - ] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Runtime per Benchmark and Workflow", - "transform": "table", - "type": "table" - }, - { - "aliasColors": {}, - "bars": true, - "dashLength": 10, - "dashes": false, - "datasource": "glx_benchmarker", - "fill": 1, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 22 - }, - "id": 30, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": false, - "total": false, - "values": false - }, - "lines": false, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": {}, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "alias": "CPU-Time per Workflow", - "groupBy": [ - { - "params": [ - "benchmark_uid" - ], - "type": "tag" - }, - { - "params": [ - "destination_name" - ], - "type": "tag" - }, - { - "params": [ - "workflow_name" - ], - "type": "tag" - } - ], - "measurement": "staging_time", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT value FROM \"runtime_seconds\" WHERE (\"benchmark_type\" = '') AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND $timeFilter GROUP BY \"workflow_name\", \"benchmark_uuid\", \"history_name\"", - "rawQuery": true, - "refId": "A", - "resultFormat": "time_series", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "sum" - } - ] - ], - "tags": [ - { - "key": "run_type", - "operator": "=", - "value": "warm" - }, - { - "condition": "AND", - "key": "benchmark_type", - "operator": "=", - "value": "" - } - ] - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Runtime distribution", - "tooltip": { - "shared": false, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "histogram", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "decimals": -1, - "format": "none", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "columns": [], - "datasource": "glx_benchmarker", - "fontSize": "100%", - "gridPos": { - "h": 16, - "w": 12, - "x": 0, - "y": 30 - }, - "id": 40, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "hidden" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "s" - } - ], - "targets": [ - { - "alias": "CPU-Time per Workflow", - "groupBy": [ - { - "params": [ - "benchmark_uid" - ], - "type": "tag" - }, - { - "params": [ - "destination_name" - ], - "type": "tag" - }, - { - "params": [ - "workflow_name" - ], - "type": "tag" - } - ], - "measurement": "staging_time", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT sum(\"value\") FROM \"total_workflow_runtime\" WHERE (\"benchmark_type\" = '') AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\"", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "sum" - } - ] - ], - "tags": [ - { - "key": "run_type", - "operator": "=", - "value": "warm" - }, - { - "condition": "AND", - "key": "benchmark_type", - "operator": "=", - "value": "" - } - ] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Total Workflow Runtimes", - "transform": "table", - "type": "table" - }, - { - "columns": [], - "datasource": "glx_benchmarker", - "fontSize": "100%", - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 30 - }, - "id": 41, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "date" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "decimals": 2, - "mappingType": 1, - "pattern": "(median|mean|max|min)", - "thresholds": [], - "type": "number", - "unit": "s" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "short" - } - ], - "targets": [ - { - "alias": "$tag_ benchmark_uuid: $tag_workflow_name", - "groupBy": [], - "measurement": "runtime_seconds", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT median(\"sum\"), mean(\"sum\"), max(\"sum\"), min(\"sum\") FROM (SELECT sum(\"value\") FROM \"total_workflow_runtime\" WHERE (\"benchmark_type\" = '') AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND $timeFilter GROUP BY \"workflow_name\", \"benchmark_uuid\", \"history_name\") WHERE $timeFilter GROUP BY \"benchmark_uuid\", \"workflow_name\" ", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - } - ] - ], - "tags": [ - { - "key": "benchmark_type", - "operator": "=", - "value": "" - } - ] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Total Workflow Runtime per Benchmark and Workflow", - "transform": "table", - "type": "table" - }, - { - "aliasColors": {}, - "bars": true, - "dashLength": 10, - "dashes": false, - "datasource": "glx_benchmarker", - "fill": 1, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 38 - }, - "id": 42, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": false, - "total": false, - "values": false - }, - "lines": false, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": {}, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "alias": "CPU-Time per Workflow", - "groupBy": [ - { - "params": [ - "benchmark_uid" - ], - "type": "tag" - }, - { - "params": [ - "destination_name" - ], - "type": "tag" - }, - { - "params": [ - "workflow_name" - ], - "type": "tag" - } - ], - "measurement": "staging_time", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT sum FROM (SELECT sum(\"value\") FROM \"total_workflow_runtime\" WHERE (\"benchmark_type\" = '') AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\")", - "rawQuery": true, - "refId": "A", - "resultFormat": "time_series", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "sum" - } - ] - ], - "tags": [ - { - "key": "run_type", - "operator": "=", - "value": "warm" - }, - { - "condition": "AND", - "key": "benchmark_type", - "operator": "=", - "value": "" - } - ] - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Total Workflow Runtime distribution", - "tooltip": { - "shared": false, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "histogram", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "decimals": -1, - "format": "none", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "collapsed": true, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 46 - }, - "id": 23, - "panels": [ - { - "columns": [], - "datasource": "glx_benchmarker", - "fontSize": "100%", - "gridPos": { - "h": 13, - "w": 12, - "x": 0, - "y": 47 - }, - "id": 5, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "hidden" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "decimals": 2, - "mappingType": 1, - "pattern": "sum", - "thresholds": [], - "type": "number", - "unit": "ns" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "s" - } - ], - "targets": [ - { - "alias": "CPU-Time per Workflow", - "groupBy": [ - { - "params": [ - "benchmark_uid" - ], - "type": "tag" - }, - { - "params": [ - "destination_name" - ], - "type": "tag" - }, - { - "params": [ - "workflow_name" - ], - "type": "tag" - } - ], - "measurement": "cpuacct.usage", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT sum(\"value\") FROM \"cpuacct.usage\" WHERE (\"benchmark_type\" = '') AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\"", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "sum" - } - ] - ], - "tags": [ - { - "key": "run_type", - "operator": "=", - "value": "warm" - }, - { - "condition": "AND", - "key": "benchmark_type", - "operator": "=", - "value": "" - } - ] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "CPU-Usage", - "transform": "table", - "type": "table" - }, - { - "columns": [], - "datasource": "glx_benchmarker", - "fontSize": "100%", - "gridPos": { - "h": 5, - "w": 12, - "x": 12, - "y": 47 - }, - "id": 31, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "hidden" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "ns" - } - ], - "targets": [ - { - "alias": "CPU-Time per Workflow", - "groupBy": [ - { - "params": [ - "benchmark_uid" - ], - "type": "tag" - }, - { - "params": [ - "destination_name" - ], - "type": "tag" - }, - { - "params": [ - "workflow_name" - ], - "type": "tag" - } - ], - "measurement": "cpuacct.usage", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT median(\"sum\"), mean(\"sum\"), max(\"sum\"), min(\"sum\") FROM (SELECT sum(\"value\") FROM \"cpuacct.usage\" WHERE (\"benchmark_type\" = '') AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\") WHERE $timeFilter GROUP BY \"benchmark_uuid\", \"workflow_name\" ", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "sum" - } - ] - ], - "tags": [ - { - "key": "run_type", - "operator": "=", - "value": "warm" - }, - { - "condition": "AND", - "key": "benchmark_type", - "operator": "=", - "value": "" - } - ] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "CPU-Usage per Benchmark and Workflow", - "transform": "table", - "type": "table" - } - ], - "title": "CPU Time", - "type": "row" - }, - { - "collapsed": true, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 47 - }, - "id": 21, - "panels": [ - { - "columns": [], - "datasource": "glx_benchmarker", - "fontSize": "100%", - "gridPos": { - "h": 13, - "w": 12, - "x": 0, - "y": 61 - }, - "id": 29, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "date" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "short" - } - ], - "targets": [ - { - "groupBy": [ - { - "params": [ - "$__interval" - ], - "type": "time" - }, - { - "params": [ - "null" - ], - "type": "fill" - } - ], - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT sum(\"value\") FROM \"staging_time\" WHERE (\"benchmark_type\" = '') AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\"", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "mean" - } - ] - ], - "tags": [] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Staging-Time per Workflow-Run", - "transform": "table", - "type": "table" - }, - { - "columns": [], - "datasource": "glx_benchmarker", - "fontSize": "100%", - "gridPos": { - "h": 5, - "w": 12, - "x": 12, - "y": 61 - }, - "id": 32, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "hidden" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "s" - } - ], - "targets": [ - { - "alias": "CPU-Time per Workflow", - "groupBy": [ - { - "params": [ - "benchmark_uid" - ], - "type": "tag" - }, - { - "params": [ - "destination_name" - ], - "type": "tag" - }, - { - "params": [ - "workflow_name" - ], - "type": "tag" - } - ], - "measurement": "staging_time", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT median(\"sum\"), mean(\"sum\"), max(\"sum\"), min(\"sum\") FROM (SELECT sum(\"value\") FROM \"staging_time\" WHERE (\"benchmark_type\" = '') AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND $timeFilter GROUP BY \"workflow_name\", \"benchmark_uuid\", \"history_name\") WHERE $timeFilter GROUP BY \"benchmark_uuid\", \"workflow_name\" ", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "sum" - } - ] - ], - "tags": [ - { - "key": "run_type", - "operator": "=", - "value": "warm" - }, - { - "condition": "AND", - "key": "benchmark_type", - "operator": "=", - "value": "" - } - ] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Staging-Time per Benchmark and Workflow", - "transform": "table", - "type": "table" - }, - { - "aliasColors": {}, - "bars": true, - "dashLength": 10, - "dashes": false, - "datasource": "glx_benchmarker", - "fill": 1, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 66 - }, - "id": 33, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": false, - "total": false, - "values": false - }, - "lines": false, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": {}, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "alias": "CPU-Time per Workflow", - "groupBy": [ - { - "params": [ - "benchmark_uid" - ], - "type": "tag" - }, - { - "params": [ - "destination_name" - ], - "type": "tag" - }, - { - "params": [ - "workflow_name" - ], - "type": "tag" - } - ], - "measurement": "staging_time", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT sum FROM (SELECT sum(\"value\") FROM \"staging_time\" WHERE (\"benchmark_type\" = '') AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\")", - "rawQuery": true, - "refId": "A", - "resultFormat": "time_series", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "sum" - } - ] - ], - "tags": [ - { - "key": "run_type", - "operator": "=", - "value": "warm" - }, - { - "condition": "AND", - "key": "benchmark_type", - "operator": "=", - "value": "" - } - ] - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Staging-Time distribution", - "tooltip": { - "shared": false, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "histogram", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "none", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - } - ], - "title": "Staging Time", - "type": "row" - } - ], - "refresh": false, - "schemaVersion": 18, - "style": "dark", - "tags": [], - "templating": { - "list": [ - { - "allValue": null, - "current": { - "text": "CondorBurstBenchmark_2019-07-23 12:20:14.249471", - "value": [ - "CondorBurstBenchmark_2019-07-23 12:20:14.249471" - ] - }, - "datasource": "glx_benchmarker", - "definition": "SELECT benchmark_uuid , value FROM \"runtime_seconds\" WHERE \"benchmark_type\" = ''", - "hide": 0, - "includeAll": true, - "label": "Benchmark UUID", - "multi": true, - "name": "selected_benchmark_uuid", - "options": [], - "query": "SELECT benchmark_uuid , value FROM \"runtime_seconds\" WHERE \"benchmark_type\" = ''", - "refresh": 2, - "regex": "", - "skipUrlSync": false, - "sort": 4, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - } - ] - }, - "time": { - "from": "now-24h", - "to": "now" - }, - "timepicker": { - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ], - "time_options": [ - "5m", - "15m", - "1h", - "6h", - "12h", - "24h", - "2d", - "7d", - "30d" - ] - }, - "timezone": "", - "title": "Condor Benchmark", - "uid": "P1T8PsnWk", - "version": 10 -} \ No newline at end of file diff --git a/grafana/dashboards/Cold vs Warm Benchmarks.json b/grafana/dashboards/Cold vs Warm Benchmarks.json deleted file mode 100644 index c904cf23..00000000 --- a/grafana/dashboards/Cold vs Warm Benchmarks.json +++ /dev/null @@ -1,1825 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "editable": true, - "gnetId": null, - "graphTooltip": 0, - "id": 6, - "iteration": 1564648082706, - "links": [], - "panels": [ - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 21, - "panels": [], - "title": "Runtime", - "type": "row" - }, - { - "columns": [], - "datasource": "glx_benchmarker", - "fontSize": "100%", - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 1 - }, - "id": 23, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "date" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "short" - } - ], - "targets": [ - { - "groupBy": [ - { - "params": [ - "$__interval" - ], - "type": "time" - }, - { - "params": [ - "null" - ], - "type": "fill" - } - ], - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT sum(\"value\") FROM \"runtime_seconds\" WHERE (\"benchmark_type\" = '' AND \"run_type\" = 'cold' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/) AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\"", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "mean" - } - ] - ], - "tags": [] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Runtime for Cold-Run", - "transform": "table", - "type": "table" - }, - { - "columns": [], - "datasource": "glx_benchmarker", - "fontSize": "100%", - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 1 - }, - "id": 24, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "date" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "short" - } - ], - "targets": [ - { - "groupBy": [ - { - "params": [ - "$__interval" - ], - "type": "time" - }, - { - "params": [ - "null" - ], - "type": "fill" - } - ], - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT sum(\"value\") FROM \"runtime_seconds\" WHERE (\"benchmark_type\" = '' AND \"run_type\" = 'warm' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/) AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\"", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "mean" - } - ] - ], - "tags": [] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Runtime for Warm-Run", - "transform": "table", - "type": "table" - }, - { - "columns": [], - "datasource": "glx_benchmarker", - "fontSize": "100%", - "gridPos": { - "h": 6, - "w": 12, - "x": 0, - "y": 9 - }, - "id": 26, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "hidden" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "short" - } - ], - "targets": [ - { - "groupBy": [ - { - "params": [ - "$__interval" - ], - "type": "time" - }, - { - "params": [ - "null" - ], - "type": "fill" - } - ], - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT mean(\"sum\") FROM (SELECT sum(\"value\") FROM \"runtime_seconds\" WHERE (\"benchmark_type\" = '' AND \"run_type\" = 'warm' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/) AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\") GROUP BY benchmark_uuid, workflow_name", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "mean" - } - ] - ], - "tags": [] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Average Runtime for Warm-Run", - "transform": "table", - "type": "table" - }, - { - "columns": [], - "datasource": "glx_benchmarker", - "fontSize": "100%", - "gridPos": { - "h": 6, - "w": 12, - "x": 12, - "y": 9 - }, - "id": 25, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "hidden" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "short" - } - ], - "targets": [ - { - "groupBy": [ - { - "params": [ - "$__interval" - ], - "type": "time" - }, - { - "params": [ - "null" - ], - "type": "fill" - } - ], - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT mean(\"sum\") FROM (SELECT sum(\"value\") FROM \"runtime_seconds\" WHERE (\"benchmark_type\" = '' AND \"run_type\" = 'cold' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/) AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\") GROUP BY benchmark_uuid, workflow_name", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "mean" - } - ] - ], - "tags": [] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Average Runtime for Cold-Run", - "transform": "table", - "type": "table" - }, - { - "aliasColors": {}, - "bars": true, - "dashLength": 10, - "dashes": false, - "datasource": "glx_benchmarker", - "fill": 1, - "gridPos": { - "h": 8, - "w": 24, - "x": 0, - "y": 15 - }, - "id": 28, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": false, - "total": false, - "values": false - }, - "lines": false, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": {}, - "paceLength": 10, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "alias": "Cold $tag_workflow_name", - "groupBy": [ - { - "params": [ - "$__interval" - ], - "type": "time" - }, - { - "params": [ - "null" - ], - "type": "fill" - } - ], - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT mean(\"sum\") FROM (SELECT sum(\"value\") FROM \"runtime_seconds\" WHERE (\"benchmark_type\" = '' AND \"run_type\" = 'cold' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/) AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\") WHERE $timeFilter GROUP BY benchmark_uuid, workflow_name", - "rawQuery": true, - "refId": "A", - "resultFormat": "time_series", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "mean" - } - ] - ], - "tags": [] - }, - { - "alias": "Warm $tag_workflow_name", - "groupBy": [ - { - "params": [ - "$__interval" - ], - "type": "time" - }, - { - "params": [ - "null" - ], - "type": "fill" - } - ], - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT mean(\"sum\") FROM (SELECT sum(\"value\") FROM \"runtime_seconds\" WHERE (\"benchmark_type\" = '' AND \"run_type\" = 'warm' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/) AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\") WHERE $timeFilter GROUP BY benchmark_uuid, workflow_name", - "rawQuery": true, - "refId": "B", - "resultFormat": "time_series", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "mean" - } - ] - ], - "tags": [] - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Average Runtime Cold vs Warm per Workflow", - "tooltip": { - "shared": false, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "series", - "name": null, - "show": true, - "values": [ - "total" - ] - }, - "yaxes": [ - { - "format": "s", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 23 - }, - "id": 19, - "panels": [], - "title": "CPU Time", - "type": "row" - }, - { - "columns": [], - "datasource": "glx_benchmarker", - "description": "", - "fontSize": "100%", - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 24 - }, - "id": 2, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "hidden" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "s" - } - ], - "targets": [ - { - "alias": "CPU-Time per Workflow", - "groupBy": [ - { - "params": [ - "benchmark_uid" - ], - "type": "tag" - }, - { - "params": [ - "destination_name" - ], - "type": "tag" - }, - { - "params": [ - "workflow_name" - ], - "type": "tag" - } - ], - "measurement": "cpuacct.usage", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT sum(\"value\") FROM \"cpuacct.usage\" WHERE (\"run_type\" = 'cold') AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/ AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\"", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "sum" - } - ] - ], - "tags": [ - { - "key": "run_type", - "operator": "=", - "value": "cold" - } - ] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "CPU-Usage for Cold-Run", - "transform": "table", - "type": "table" - }, - { - "columns": [], - "datasource": "glx_benchmarker", - "fontSize": "100%", - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 24 - }, - "id": 5, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "hidden" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "s" - } - ], - "targets": [ - { - "alias": "CPU-Time per Workflow", - "groupBy": [ - { - "params": [ - "benchmark_uid" - ], - "type": "tag" - }, - { - "params": [ - "destination_name" - ], - "type": "tag" - }, - { - "params": [ - "workflow_name" - ], - "type": "tag" - } - ], - "measurement": "cpuacct.usage", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT sum(\"value\") FROM \"cpuacct.usage\" WHERE (\"run_type\" = 'warm' AND \"benchmark_type\" = '' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/) AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\"", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "sum" - } - ] - ], - "tags": [ - { - "key": "run_type", - "operator": "=", - "value": "warm" - }, - { - "condition": "AND", - "key": "benchmark_type", - "operator": "=", - "value": "" - } - ] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "CPU-Usage for Warm-Run", - "transform": "table", - "type": "table" - }, - { - "columns": [], - "datasource": "glx_benchmarker", - "description": "", - "fontSize": "100%", - "gridPos": { - "h": 5, - "w": 12, - "x": 0, - "y": 32 - }, - "id": 14, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "hidden" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "s" - } - ], - "targets": [ - { - "alias": "CPU-Time per Workflow", - "groupBy": [ - { - "params": [ - "benchmark_uid" - ], - "type": "tag" - }, - { - "params": [ - "destination_name" - ], - "type": "tag" - }, - { - "params": [ - "workflow_name" - ], - "type": "tag" - } - ], - "measurement": "cpuacct.usage", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT mean(\"sum\") FROM (SELECT sum(\"value\") FROM \"cpuacct.usage\" WHERE (\"run_type\" = 'cold') AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/ AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\") GROUP BY \"benchmark_uuid\", workflow_name", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "sum" - } - ] - ], - "tags": [ - { - "key": "run_type", - "operator": "=", - "value": "cold" - } - ] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Average CPU-Usage for Cold-Run per Benchmark", - "transform": "table", - "type": "table" - }, - { - "columns": [], - "datasource": "glx_benchmarker", - "fontSize": "100%", - "gridPos": { - "h": 5, - "w": 12, - "x": 12, - "y": 32 - }, - "id": 15, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "hidden" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "s" - } - ], - "targets": [ - { - "alias": "CPU-Time per Workflow", - "groupBy": [ - { - "params": [ - "benchmark_uid" - ], - "type": "tag" - }, - { - "params": [ - "destination_name" - ], - "type": "tag" - }, - { - "params": [ - "workflow_name" - ], - "type": "tag" - } - ], - "measurement": "cpuacct.usage", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT mean(\"sum\") FROM (SELECT sum(\"value\") FROM \"cpuacct.usage\" WHERE (\"run_type\" = 'warm' AND \"benchmark_type\" = '' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/) AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\") GROUP BY \"benchmark_uuid\", workflow_name", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "sum" - } - ] - ], - "tags": [ - { - "key": "run_type", - "operator": "=", - "value": "warm" - }, - { - "condition": "AND", - "key": "benchmark_type", - "operator": "=", - "value": "" - } - ] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Average CPU-Usage for Warm-Run per Benchmark", - "transform": "table", - "type": "table" - }, - { - "aliasColors": {}, - "bars": true, - "dashLength": 10, - "dashes": false, - "datasource": "glx_benchmarker", - "fill": 1, - "gridPos": { - "h": 8, - "w": 24, - "x": 0, - "y": 37 - }, - "id": 29, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": false, - "total": false, - "values": false - }, - "lines": false, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": {}, - "paceLength": 10, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "alias": "Cold $tag_workflow_name", - "groupBy": [ - { - "params": [ - "$__interval" - ], - "type": "time" - }, - { - "params": [ - "null" - ], - "type": "fill" - } - ], - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT mean(\"sum\") FROM (SELECT sum(\"value\") FROM \"cpuacct.usage\" WHERE (\"run_type\" = 'cold') AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/ AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\" ) WHERE $timeFilter GROUP BY \"benchmark_uuid\", workflow_name", - "rawQuery": true, - "refId": "A", - "resultFormat": "time_series", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "mean" - } - ] - ], - "tags": [] - }, - { - "alias": "Warm $tag_workflow_name", - "groupBy": [ - { - "params": [ - "$__interval" - ], - "type": "time" - }, - { - "params": [ - "null" - ], - "type": "fill" - } - ], - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT mean(\"sum\") FROM (SELECT sum(\"value\") FROM \"cpuacct.usage\" WHERE (\"run_type\" = 'warm' AND \"benchmark_type\" = '' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/) AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\") WHERE $timeFilter GROUP BY \"benchmark_uuid\", workflow_name", - "rawQuery": true, - "refId": "B", - "resultFormat": "time_series", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "mean" - } - ] - ], - "tags": [] - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Average CPU-Time Cold vs Warm per Workflow", - "tooltip": { - "shared": false, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "series", - "name": null, - "show": true, - "values": [ - "total" - ] - }, - "yaxes": [ - { - "format": "s", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 45 - }, - "id": 17, - "panels": [], - "title": "Staging Time", - "type": "row" - }, - { - "columns": [], - "datasource": "glx_benchmarker", - "fontSize": "100%", - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 46 - }, - "id": 6, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "hidden" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "s" - } - ], - "targets": [ - { - "alias": "CPU-Time per Workflow", - "groupBy": [ - { - "params": [ - "destination_name" - ], - "type": "tag" - }, - { - "params": [ - "workflow_name" - ], - "type": "tag" - }, - { - "params": [ - "benchmark_uuid" - ], - "type": "tag" - }, - { - "params": [ - "history_name" - ], - "type": "tag" - } - ], - "measurement": "staging_time", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT sum(\"value\") FROM \"staging_time\" WHERE (\"run_type\" = 'cold' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/) AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\"", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "sum" - } - ] - ], - "tags": [ - { - "key": "run_type", - "operator": "=", - "value": "cold" - } - ] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Staging-Time for Cold-Run", - "transform": "table", - "type": "table" - }, - { - "columns": [], - "datasource": "glx_benchmarker", - "fontSize": "100%", - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 46 - }, - "id": 7, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "hidden" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "s" - } - ], - "targets": [ - { - "alias": "CPU-Time per Workflow", - "groupBy": [ - { - "params": [ - "benchmark_uid" - ], - "type": "tag" - }, - { - "params": [ - "destination_name" - ], - "type": "tag" - }, - { - "params": [ - "workflow_name" - ], - "type": "tag" - } - ], - "measurement": "staging_time", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT sum(\"value\") FROM \"staging_time\" WHERE (\"run_type\" = 'warm' AND \"benchmark_type\" = '' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/) AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\"", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "sum" - } - ] - ], - "tags": [ - { - "key": "run_type", - "operator": "=", - "value": "warm" - }, - { - "condition": "AND", - "key": "benchmark_type", - "operator": "=", - "value": "" - } - ] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Staging-Time for Warm-Run", - "transform": "table", - "type": "table" - }, - { - "columns": [], - "datasource": "glx_benchmarker", - "fontSize": "100%", - "gridPos": { - "h": 6, - "w": 12, - "x": 0, - "y": 54 - }, - "id": 13, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "hidden" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "s" - } - ], - "targets": [ - { - "alias": "CPU-Time per Workflow", - "groupBy": [ - { - "params": [ - "destination_name" - ], - "type": "tag" - }, - { - "params": [ - "workflow_name" - ], - "type": "tag" - }, - { - "params": [ - "benchmark_uuid" - ], - "type": "tag" - }, - { - "params": [ - "history_name" - ], - "type": "tag" - } - ], - "measurement": "staging_time", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT mean(\"sum\") FROM (SELECT sum(\"value\") FROM \"staging_time\" WHERE (\"run_type\" = 'cold' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/) AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\") GROUP BY \"benchmark_uuid\", workflow_name", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "sum" - } - ] - ], - "tags": [ - { - "key": "run_type", - "operator": "=", - "value": "cold" - } - ] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Average Staging-Time for Cold-Run per Benchmark", - "transform": "table", - "type": "table" - }, - { - "columns": [], - "datasource": "glx_benchmarker", - "fontSize": "100%", - "gridPos": { - "h": 6, - "w": 12, - "x": 12, - "y": 54 - }, - "id": 12, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "hidden" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "s" - } - ], - "targets": [ - { - "alias": "CPU-Time per Workflow", - "groupBy": [ - { - "params": [ - "benchmark_uid" - ], - "type": "tag" - }, - { - "params": [ - "destination_name" - ], - "type": "tag" - }, - { - "params": [ - "workflow_name" - ], - "type": "tag" - } - ], - "measurement": "staging_time", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT mean(\"sum\") FROM (SELECT sum(\"value\") FROM \"staging_time\" WHERE (\"run_type\" = 'warm' AND \"benchmark_type\" = '' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/) AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\") GROUP BY benchmark_uuid, workflow_name", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "sum" - } - ] - ], - "tags": [ - { - "key": "run_type", - "operator": "=", - "value": "warm" - }, - { - "condition": "AND", - "key": "benchmark_type", - "operator": "=", - "value": "" - } - ] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Average Staging-Time for Warm-Run per Benchmark", - "transform": "table", - "type": "table" - }, - { - "aliasColors": {}, - "bars": true, - "dashLength": 10, - "dashes": false, - "datasource": "glx_benchmarker", - "fill": 1, - "gridPos": { - "h": 8, - "w": 24, - "x": 0, - "y": 60 - }, - "id": 30, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "max": false, - "min": false, - "rightSide": false, - "show": false, - "total": false, - "values": false - }, - "lines": false, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": {}, - "paceLength": 10, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "alias": "Cold $tag_workflow_name", - "groupBy": [ - { - "params": [ - "$__interval" - ], - "type": "time" - }, - { - "params": [ - "null" - ], - "type": "fill" - } - ], - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT mean(\"sum\") FROM (SELECT sum(\"value\") FROM \"staging_time\" WHERE (\"run_type\" = 'cold' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/) AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\") WHERE $timeFilter GROUP BY \"benchmark_uuid\", workflow_name", - "rawQuery": true, - "refId": "A", - "resultFormat": "time_series", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "mean" - } - ] - ], - "tags": [] - }, - { - "alias": "Warm $tag_workflow_name", - "groupBy": [ - { - "params": [ - "$__interval" - ], - "type": "time" - }, - { - "params": [ - "null" - ], - "type": "fill" - } - ], - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT mean(\"sum\") FROM (SELECT sum(\"value\") FROM \"staging_time\" WHERE (\"run_type\" = 'warm' AND \"benchmark_type\" = '' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/) AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\") WHERE $timeFilter GROUP BY benchmark_uuid, workflow_name", - "rawQuery": true, - "refId": "B", - "resultFormat": "time_series", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "mean" - } - ] - ], - "tags": [] - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Average Staging-Time Cold vs Warm per Workflow", - "tooltip": { - "shared": false, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "series", - "name": null, - "show": true, - "values": [ - "total" - ] - }, - "yaxes": [ - { - "format": "s", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - } - ], - "schemaVersion": 18, - "style": "dark", - "tags": [], - "templating": { - "list": [ - { - "allValue": null, - "current": { - "tags": [], - "text": "All", - "value": [ - "$__all" - ] - }, - "datasource": "glx_benchmarker", - "definition": "SELECT benchmark_uuid , value FROM \"runtime_seconds\" WHERE \"benchmark_type\" = '' ", - "hide": 0, - "includeAll": true, - "label": "Benchmark UUID", - "multi": true, - "name": "selected_benchmark_uuid", - "options": [], - "query": "SELECT benchmark_uuid , value FROM \"runtime_seconds\" WHERE \"benchmark_type\" = '' ", - "refresh": 2, - "regex": "", - "skipUrlSync": false, - "sort": 0, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "allValue": null, - "current": { - "text": "All", - "value": "$__all" - }, - "datasource": "glx_benchmarker", - "definition": "SELECT workflow_name, value FROM \"runtime_seconds\" WHERE \"benchmark_type\" = '' GROUP BY workflow_name", - "hide": 0, - "includeAll": true, - "label": "Workflow", - "multi": true, - "name": "selected_workflow_name", - "options": [], - "query": "", - "refresh": 2, - "regex": "", - "skipUrlSync": false, - "sort": 0, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - } - ] - }, - "time": { - "from": "now-30d", - "to": "now" - }, - "timepicker": { - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ], - "time_options": [ - "5m", - "15m", - "1h", - "6h", - "12h", - "24h", - "2d", - "7d", - "30d" - ] - }, - "timezone": "", - "title": "Cold vs Warm Benchmarks", - "uid": "JwXRTNnZz", - "version": 2 -} \ No newline at end of file diff --git a/grafana/dashboards/Destination-Comparison Benchmarks.json b/grafana/dashboards/Destination-Comparison Benchmarks.json deleted file mode 100644 index f1034a41..00000000 --- a/grafana/dashboards/Destination-Comparison Benchmarks.json +++ /dev/null @@ -1,2510 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "editable": true, - "gnetId": null, - "graphTooltip": 0, - "id": 4, - "iteration": 1563885333107, - "links": [], - "panels": [ - { - "collapsed": true, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 39, - "panels": [ - { - "aliasColors": {}, - "bars": true, - "dashLength": 10, - "dashes": false, - "datasource": "glx_benchmarker", - "fill": 1, - "gridPos": { - "h": 6, - "w": 24, - "x": 0, - "y": 1 - }, - "id": 34, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "hideEmpty": false, - "hideZero": false, - "max": false, - "min": false, - "show": false, - "total": false, - "values": false - }, - "lines": false, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": {}, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [ - { - "alias": "/error/", - "color": "#E02F44" - }, - { - "alias": "/success/", - "color": "#56A64B" - } - ], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "alias": "$tag_benchmark_uuid: $col", - "groupBy": [ - { - "params": [ - "benchmark_uuid" - ], - "type": "tag" - } - ], - "measurement": "workflow_status", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT success, error FROM (SELECT count(\"value\") as success FROM \"workflow_status\" WHERE (\"value\" = 'success') AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/ AND \"destination_name\" =~ /$selected_destination_name/ AND $timeFilter GROUP BY \"benchmark_uuid\"), (SELECT count(\"value\") as error FROM \"workflow_status\" WHERE (\"value\" = 'error') AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/ AND \"destination_name\" =~ /$selected_destination_name/ AND $timeFilter GROUP BY \"benchmark_uuid\") GROUP BY benchmark_uuid", - "rawQuery": true, - "refId": "A", - "resultFormat": "time_series", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "count" - } - ] - ], - "tags": [ - { - "key": "value", - "operator": "=", - "value": "error" - } - ] - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Workflow Status", - "tooltip": { - "shared": false, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "series", - "name": null, - "show": true, - "values": [ - "total" - ] - }, - "yaxes": [ - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "columns": [], - "datasource": "glx_benchmarker", - "fontSize": "100%", - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 7 - }, - "id": 36, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "date" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "none" - } - ], - "targets": [ - { - "groupBy": [ - { - "params": [ - "$__interval" - ], - "type": "time" - }, - { - "params": [ - "null" - ], - "type": "fill" - } - ], - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT value FROM /$selected_measurement/ WHERE (\"benchmark_type\" = '' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/ AND \"destination_name\" =~ /$selected_destination_name/) AND $timeFilter GROUP BY benchmark_uuid, destination_name, workflow_name", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "mean" - } - ] - ], - "tags": [] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Selected Measurements", - "transform": "table", - "type": "table" - }, - { - "columns": [], - "datasource": "glx_benchmarker", - "fontSize": "100%", - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 7 - }, - "id": 37, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "date" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "none" - } - ], - "targets": [ - { - "groupBy": [ - { - "params": [ - "$__interval" - ], - "type": "time" - }, - { - "params": [ - "null" - ], - "type": "fill" - } - ], - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT min(\"sum\"), max(\"sum\"), mean(\"sum\"), stddev(\"sum\") FROM (SELECT sum(\"value\") FROM /$selected_measurement/ WHERE (\"benchmark_type\" = '' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/ AND \"destination_name\" =~ /$selected_destination_name/) AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\") GROUP BY \"benchmark_uuid\", \"destination_name\", \"workflow_name\" ", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "mean" - } - ] - ], - "tags": [] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Average over selected measurements", - "transform": "table", - "type": "table" - } - ], - "title": "Custom Metrics", - "type": "row" - }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 1 - }, - "id": 25, - "panels": [], - "title": "Runtime", - "type": "row" - }, - { - "columns": [], - "datasource": "glx_benchmarker", - "fontSize": "100%", - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 2 - }, - "id": 16, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "hidden" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "s" - } - ], - "targets": [ - { - "alias": "CPU-Time per Workflow", - "groupBy": [ - { - "params": [ - "benchmark_uid" - ], - "type": "tag" - }, - { - "params": [ - "destination_name" - ], - "type": "tag" - }, - { - "params": [ - "workflow_name" - ], - "type": "tag" - } - ], - "measurement": "staging_time", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT sum(\"value\") FROM \"runtime_seconds\" WHERE (\"benchmark_type\" = '' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/ AND \"destination_name\" =~ /$selected_destination_name/) AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\"", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "sum" - } - ] - ], - "tags": [ - { - "key": "run_type", - "operator": "=", - "value": "warm" - }, - { - "condition": "AND", - "key": "benchmark_type", - "operator": "=", - "value": "" - } - ] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Runtime", - "transform": "table", - "type": "table" - }, - { - "columns": [], - "datasource": "glx_benchmarker", - "fontSize": "100%", - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 2 - }, - "id": 17, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "hidden" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "s" - } - ], - "targets": [ - { - "alias": "CPU-Time per Workflow", - "groupBy": [ - { - "params": [ - "benchmark_uid" - ], - "type": "tag" - }, - { - "params": [ - "destination_name" - ], - "type": "tag" - }, - { - "params": [ - "workflow_name" - ], - "type": "tag" - } - ], - "measurement": "staging_time", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT min(\"sum\"), max(\"sum\"), mean(\"sum\"), stddev(\"sum\") FROM (SELECT sum(\"value\") FROM \"runtime_seconds\" WHERE (\"benchmark_type\" = '' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/ AND \"destination_name\" =~ /$selected_destination_name/) AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\") GROUP BY \"benchmark_uuid\", \"destination_name\", \"workflow_name\" ", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "sum" - } - ] - ], - "tags": [ - { - "key": "run_type", - "operator": "=", - "value": "warm" - }, - { - "condition": "AND", - "key": "benchmark_type", - "operator": "=", - "value": "" - } - ] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Average Runtime per Benchmark, Destination and Workflow", - "transform": "table", - "type": "table" - }, - { - "columns": [], - "datasource": "glx_benchmarker", - "fontSize": "100%", - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 10 - }, - "id": 41, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "hidden" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "s" - } - ], - "targets": [ - { - "alias": "CPU-Time per Workflow", - "groupBy": [ - { - "params": [ - "benchmark_uid" - ], - "type": "tag" - }, - { - "params": [ - "destination_name" - ], - "type": "tag" - }, - { - "params": [ - "workflow_name" - ], - "type": "tag" - } - ], - "measurement": "staging_time", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT min(\"sum\"), max(\"sum\"), mean(\"sum\"), stddev(\"sum\") FROM (SELECT sum(\"value\") FROM \"runtime_seconds\" WHERE (\"benchmark_type\" = '' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/ AND \"destination_name\" =~ /$selected_destination_name/) AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"history_name\") GROUP BY destination_name, workflow_name", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "sum" - } - ] - ], - "tags": [ - { - "key": "run_type", - "operator": "=", - "value": "warm" - }, - { - "condition": "AND", - "key": "benchmark_type", - "operator": "=", - "value": "" - } - ] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Average Runtime per Destination and Workflow", - "transform": "table", - "type": "table" - }, - { - "aliasColors": {}, - "bars": true, - "dashLength": 10, - "dashes": false, - "datasource": "glx_benchmarker", - "fill": 1, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 10 - }, - "id": 19, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "max": false, - "min": false, - "rightSide": false, - "show": false, - "total": false, - "values": false - }, - "lines": false, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": {}, - "paceLength": 10, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "alias": "$tag_destination_name: $tag_workflow_name", - "groupBy": [ - { - "params": [ - "$__interval" - ], - "type": "time" - }, - { - "params": [ - "null" - ], - "type": "fill" - } - ], - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT mean(\"sum\") FROM (SELECT sum(\"value\") FROM \"runtime_seconds\" WHERE (\"benchmark_type\" = '' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/ AND \"destination_name\" =~ /$selected_destination_name/) AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"history_name\") WHERE $timeFilter GROUP BY destination_name, workflow_name", - "rawQuery": true, - "refId": "A", - "resultFormat": "time_series", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "mean" - } - ] - ], - "tags": [] - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Average Runtime per Destination and Workflow", - "tooltip": { - "shared": false, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "series", - "name": null, - "show": true, - "values": [ - "total" - ] - }, - "yaxes": [ - { - "format": "s", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "columns": [], - "datasource": "glx_benchmarker", - "fontSize": "100%", - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 18 - }, - "id": 30, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "hidden" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "s" - } - ], - "targets": [ - { - "alias": "CPU-Time per Workflow", - "groupBy": [ - { - "params": [ - "benchmark_uid" - ], - "type": "tag" - }, - { - "params": [ - "destination_name" - ], - "type": "tag" - }, - { - "params": [ - "workflow_name" - ], - "type": "tag" - } - ], - "measurement": "staging_time", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT value FROM \"total_workflow_runtime\" WHERE (\"benchmark_type\" = '' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/ AND \"destination_name\" =~ /$selected_destination_name/) AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\"", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "sum" - } - ] - ], - "tags": [ - { - "key": "run_type", - "operator": "=", - "value": "warm" - }, - { - "condition": "AND", - "key": "benchmark_type", - "operator": "=", - "value": "" - } - ] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Total Workflow Runtime", - "transform": "table", - "type": "table" - }, - { - "columns": [], - "datasource": "glx_benchmarker", - "fontSize": "100%", - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 18 - }, - "id": 31, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "hidden" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "s" - } - ], - "targets": [ - { - "alias": "CPU-Time per Workflow", - "groupBy": [ - { - "params": [ - "benchmark_uid" - ], - "type": "tag" - }, - { - "params": [ - "destination_name" - ], - "type": "tag" - }, - { - "params": [ - "workflow_name" - ], - "type": "tag" - } - ], - "measurement": "staging_time", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT min(\"value\"), max(\"value\"), mean(\"value\"), stddev(\"sum\") FROM (SELECT value FROM \"total_workflow_runtime\" WHERE (\"benchmark_type\" = '' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/ AND \"destination_name\" =~ /$selected_destination_name/) AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\") GROUP BY \"benchmark_uuid\", \"destination_name\", \"workflow_name\"", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "sum" - } - ] - ], - "tags": [ - { - "key": "run_type", - "operator": "=", - "value": "warm" - }, - { - "condition": "AND", - "key": "benchmark_type", - "operator": "=", - "value": "" - } - ] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Average Total Workflow Runtime per Benchmark, Destination and Workflow", - "transform": "table", - "type": "table" - }, - { - "columns": [], - "datasource": "glx_benchmarker", - "fontSize": "100%", - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 26 - }, - "id": 43, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "hidden" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "s" - } - ], - "targets": [ - { - "alias": "CPU-Time per Workflow", - "groupBy": [ - { - "params": [ - "benchmark_uid" - ], - "type": "tag" - }, - { - "params": [ - "destination_name" - ], - "type": "tag" - }, - { - "params": [ - "workflow_name" - ], - "type": "tag" - } - ], - "measurement": "staging_time", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT min(\"value\"), max(\"value\"), mean(\"value\"), stddev(\"sum\") FROM (SELECT value FROM \"total_workflow_runtime\" WHERE (\"benchmark_type\" = '' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/ AND \"destination_name\" =~ /$selected_destination_name/) AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"history_name\") GROUP BY \"destination_name\", \"workflow_name\"", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "sum" - } - ] - ], - "tags": [ - { - "key": "run_type", - "operator": "=", - "value": "warm" - }, - { - "condition": "AND", - "key": "benchmark_type", - "operator": "=", - "value": "" - } - ] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Average Total Workflow Runtime per Destination and Workflow", - "transform": "table", - "type": "table" - }, - { - "aliasColors": {}, - "bars": true, - "dashLength": 10, - "dashes": false, - "datasource": "glx_benchmarker", - "fill": 1, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 26 - }, - "id": 32, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "max": false, - "min": false, - "rightSide": false, - "show": false, - "total": false, - "values": false - }, - "lines": false, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": {}, - "paceLength": 10, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "alias": "$tag_destination_name: $tag_workflow_name", - "groupBy": [ - { - "params": [ - "$__interval" - ], - "type": "time" - }, - { - "params": [ - "null" - ], - "type": "fill" - } - ], - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT mean(\"value\") FROM (SELECT value FROM \"total_workflow_runtime\" WHERE (\"benchmark_type\" = '' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/ AND \"destination_name\" =~ /$selected_destination_name/) AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\") WHERE $timeFilter GROUP BY \"benchmark_uuid\", \"destination_name\", \"workflow_name\"", - "rawQuery": true, - "refId": "A", - "resultFormat": "time_series", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "mean" - } - ] - ], - "tags": [] - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Average Total Workflow Runtime per Destination and Workflow", - "tooltip": { - "shared": false, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "series", - "name": null, - "show": true, - "values": [ - "total" - ] - }, - "yaxes": [ - { - "format": "s", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 34 - }, - "id": 23, - "panels": [], - "title": "CPU Time", - "type": "row" - }, - { - "columns": [], - "datasource": "glx_benchmarker", - "fontSize": "100%", - "gridPos": { - "h": 7, - "w": 12, - "x": 0, - "y": 35 - }, - "id": 5, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "hidden" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "ns" - } - ], - "targets": [ - { - "alias": "CPU-Time per Workflow", - "groupBy": [ - { - "params": [ - "benchmark_uid" - ], - "type": "tag" - }, - { - "params": [ - "destination_name" - ], - "type": "tag" - }, - { - "params": [ - "workflow_name" - ], - "type": "tag" - } - ], - "measurement": "cpuacct.usage", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT sum(\"value\") FROM \"cpuacct.usage\" WHERE (\"benchmark_type\" = '' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/ AND \"destination_name\" =~ /$selected_destination_name/) AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\"", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "sum" - } - ] - ], - "tags": [ - { - "key": "run_type", - "operator": "=", - "value": "warm" - }, - { - "condition": "AND", - "key": "benchmark_type", - "operator": "=", - "value": "" - } - ] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "CPU-Usage", - "transform": "table", - "type": "table" - }, - { - "columns": [], - "datasource": "glx_benchmarker", - "fontSize": "100%", - "gridPos": { - "h": 7, - "w": 12, - "x": 12, - "y": 35 - }, - "id": 15, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "hidden" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "ns" - } - ], - "targets": [ - { - "alias": "CPU-Time per Workflow", - "groupBy": [ - { - "params": [ - "benchmark_uid" - ], - "type": "tag" - }, - { - "params": [ - "destination_name" - ], - "type": "tag" - }, - { - "params": [ - "workflow_name" - ], - "type": "tag" - } - ], - "measurement": "cpuacct.usage", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT min(\"sum\"), max(\"sum\"), mean(\"sum\"), stddev(\"sum\") FROM (SELECT sum(\"value\") FROM \"cpuacct.usage\" WHERE (\"benchmark_type\" = '' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/ AND \"destination_name\" =~ /$selected_destination_name/) AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\") GROUP BY \"benchmark_uuid\", destination_name, workflow_name", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "sum" - } - ] - ], - "tags": [ - { - "key": "run_type", - "operator": "=", - "value": "warm" - }, - { - "condition": "AND", - "key": "benchmark_type", - "operator": "=", - "value": "" - } - ] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Average CPU-Usage per Benchmark, Destination and Workflow", - "transform": "table", - "type": "table" - }, - { - "columns": [], - "datasource": "glx_benchmarker", - "fontSize": "100%", - "gridPos": { - "h": 6, - "w": 12, - "x": 0, - "y": 42 - }, - "id": 42, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "hidden" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "ns" - } - ], - "targets": [ - { - "alias": "CPU-Time per Workflow", - "groupBy": [ - { - "params": [ - "benchmark_uid" - ], - "type": "tag" - }, - { - "params": [ - "destination_name" - ], - "type": "tag" - }, - { - "params": [ - "workflow_name" - ], - "type": "tag" - } - ], - "measurement": "cpuacct.usage", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT mean(\"sum\") FROM (SELECT sum(\"value\") FROM \"cpuacct.usage\" WHERE (\"benchmark_type\" = '' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/ AND \"destination_name\" =~ /$selected_destination_name/) AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"history_name\") WHERE $timeFilter GROUP BY destination_name, workflow_name", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "sum" - } - ] - ], - "tags": [ - { - "key": "run_type", - "operator": "=", - "value": "warm" - }, - { - "condition": "AND", - "key": "benchmark_type", - "operator": "=", - "value": "" - } - ] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Average CPU-Usage per Destination and Workflow", - "transform": "table", - "type": "table" - }, - { - "aliasColors": {}, - "bars": true, - "dashLength": 10, - "dashes": false, - "datasource": "glx_benchmarker", - "fill": 1, - "gridPos": { - "h": 6, - "w": 12, - "x": 12, - "y": 42 - }, - "id": 26, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "max": false, - "min": false, - "rightSide": false, - "show": false, - "total": false, - "values": false - }, - "lines": false, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": {}, - "paceLength": 10, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "alias": "$tag_destination_name: $tag_workflow_name", - "groupBy": [ - { - "params": [ - "$__interval" - ], - "type": "time" - }, - { - "params": [ - "null" - ], - "type": "fill" - } - ], - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT mean(\"sum\") FROM (SELECT sum(\"value\") FROM \"cpuacct.usage\" WHERE (\"benchmark_type\" = '' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/ AND \"destination_name\" =~ /$selected_destination_name/) AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"history_name\") WHERE $timeFilter GROUP BY destination_name, workflow_name", - "rawQuery": true, - "refId": "A", - "resultFormat": "time_series", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "mean" - } - ] - ], - "tags": [] - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Average CPU-Time per Destination and Workflow", - "tooltip": { - "shared": false, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "series", - "name": null, - "show": true, - "values": [ - "total" - ] - }, - "yaxes": [ - { - "format": "ns", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 48 - }, - "id": 21, - "panels": [], - "title": "Staging Time", - "type": "row" - }, - { - "columns": [], - "datasource": "glx_benchmarker", - "fontSize": "100%", - "gridPos": { - "h": 6, - "w": 12, - "x": 0, - "y": 49 - }, - "id": 29, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "hidden" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "s" - } - ], - "targets": [ - { - "groupBy": [ - { - "params": [ - "$__interval" - ], - "type": "time" - }, - { - "params": [ - "null" - ], - "type": "fill" - } - ], - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT sum(\"value\") FROM \"staging_time\" WHERE (\"benchmark_type\" = '' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/ AND \"destination_name\" =~ /$selected_destination_name/) AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\"", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "mean" - } - ] - ], - "tags": [] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Staging-Time per Workflow-Run", - "transform": "table", - "type": "table" - }, - { - "columns": [], - "datasource": "glx_benchmarker", - "fontSize": "100%", - "gridPos": { - "h": 6, - "w": 12, - "x": 12, - "y": 49 - }, - "id": 12, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "hidden" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "s" - } - ], - "targets": [ - { - "alias": "CPU-Time per Workflow", - "groupBy": [ - { - "params": [ - "benchmark_uid" - ], - "type": "tag" - }, - { - "params": [ - "destination_name" - ], - "type": "tag" - }, - { - "params": [ - "workflow_name" - ], - "type": "tag" - } - ], - "measurement": "staging_time", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT min(\"sum\"), max(\"sum\"), mean(\"sum\"), stddev(\"sum\") FROM (SELECT sum(\"value\") FROM \"staging_time\" WHERE (\"benchmark_type\" = '' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/ AND \"destination_name\" =~ /$selected_destination_name/) AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\") GROUP BY benchmark_uuid, destination_name, workflow_name", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "sum" - } - ] - ], - "tags": [ - { - "key": "run_type", - "operator": "=", - "value": "warm" - }, - { - "condition": "AND", - "key": "benchmark_type", - "operator": "=", - "value": "" - } - ] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Average Staging-Time per Benchmark, Destination and Workflow", - "transform": "table", - "type": "table" - }, - { - "columns": [], - "datasource": "glx_benchmarker", - "fontSize": "100%", - "gridPos": { - "h": 7, - "w": 12, - "x": 0, - "y": 55 - }, - "id": 40, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "hidden" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "s" - } - ], - "targets": [ - { - "alias": "CPU-Time per Workflow", - "groupBy": [ - { - "params": [ - "benchmark_uid" - ], - "type": "tag" - }, - { - "params": [ - "destination_name" - ], - "type": "tag" - }, - { - "params": [ - "workflow_name" - ], - "type": "tag" - } - ], - "measurement": "staging_time", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT min(\"sum\"), max(\"sum\"), mean(\"sum\"), stddev(\"sum\") FROM (SELECT sum(\"value\") FROM \"staging_time\" WHERE (\"benchmark_type\" = '' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/ AND \"destination_name\" =~ /$selected_destination_name/) AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"history_name\") GROUP BY destination_name, workflow_name", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "sum" - } - ] - ], - "tags": [ - { - "key": "run_type", - "operator": "=", - "value": "warm" - }, - { - "condition": "AND", - "key": "benchmark_type", - "operator": "=", - "value": "" - } - ] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Average Staging-Time per Destination and Workflow", - "transform": "table", - "type": "table" - }, - { - "aliasColors": {}, - "bars": true, - "dashLength": 10, - "dashes": false, - "datasource": "glx_benchmarker", - "fill": 1, - "gridPos": { - "h": 7, - "w": 12, - "x": 12, - "y": 55 - }, - "id": 27, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "max": false, - "min": false, - "rightSide": false, - "show": false, - "total": false, - "values": false - }, - "lines": false, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": {}, - "paceLength": 10, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "alias": "$tag_destination_name: $tag_workflow_name", - "groupBy": [ - { - "params": [ - "$__interval" - ], - "type": "time" - }, - { - "params": [ - "null" - ], - "type": "fill" - } - ], - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT mean(\"sum\") FROM (SELECT sum(\"value\") FROM \"staging_time\" WHERE (\"benchmark_type\" = '' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/ AND \"destination_name\" =~ /$selected_destination_name/) AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"benchmark_uuid\", \"history_name\") WHERE $timeFilter GROUP BY benchmark_uuid, destination_name, workflow_name", - "rawQuery": true, - "refId": "A", - "resultFormat": "time_series", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "mean" - } - ] - ], - "tags": [] - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Average Staging-Time per Destination and Workflow", - "tooltip": { - "shared": false, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "series", - "name": null, - "show": true, - "values": [ - "total" - ] - }, - "yaxes": [ - { - "format": "s", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "columns": [], - "datasource": "glx_benchmarker", - "fontSize": "100%", - "gridPos": { - "h": 7, - "w": 12, - "x": 0, - "y": 62 - }, - "id": 44, - "links": [], - "options": {}, - "pageSize": null, - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "hidden" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "s" - } - ], - "targets": [ - { - "alias": "CPU-Time per Workflow", - "groupBy": [ - { - "params": [ - "benchmark_uid" - ], - "type": "tag" - }, - { - "params": [ - "destination_name" - ], - "type": "tag" - }, - { - "params": [ - "workflow_name" - ], - "type": "tag" - } - ], - "measurement": "staging_time", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT min(\"value\"), max(\"value\"), mean(\"value\"), stddev(\"value\") FROM (SELECT value FROM \"staging_time\" WHERE (\"benchmark_type\" = '' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/ AND \"destination_name\" =~ /$selected_destination_name/) AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"history_name\") GROUP BY destination_name, workflow_name", - "rawQuery": true, - "refId": "A", - "resultFormat": "table", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "sum" - } - ] - ], - "tags": [ - { - "key": "run_type", - "operator": "=", - "value": "warm" - }, - { - "condition": "AND", - "key": "benchmark_type", - "operator": "=", - "value": "" - } - ] - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Average Staging-Time per Destination and Job", - "transform": "table", - "type": "table" - }, - { - "aliasColors": {}, - "bars": true, - "dashLength": 10, - "dashes": false, - "datasource": "glx_benchmarker", - "fill": 1, - "gridPos": { - "h": 7, - "w": 12, - "x": 12, - "y": 62 - }, - "id": 45, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "max": false, - "min": false, - "rightSide": false, - "show": false, - "total": false, - "values": false - }, - "lines": false, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": {}, - "paceLength": 10, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "alias": "$tag_destination_name: $tag_workflow_name", - "groupBy": [ - { - "params": [ - "$__interval" - ], - "type": "time" - }, - { - "params": [ - "null" - ], - "type": "fill" - } - ], - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT mean(\"value\") FROM (SELECT value FROM \"staging_time\" WHERE (\"benchmark_type\" = '' AND \"benchmark_uuid\" =~ /$selected_benchmark_uuid/ AND \"workflow_name\" =~ /$selected_workflow_name/ AND \"destination_name\" =~ /$selected_destination_name/) AND $timeFilter GROUP BY \"destination_name\", \"workflow_name\", \"history_name\") WHERE $timeFilter GROUP BY destination_name, workflow_name", - "rawQuery": true, - "refId": "A", - "resultFormat": "time_series", - "select": [ - [ - { - "params": [ - "value" - ], - "type": "field" - }, - { - "params": [], - "type": "mean" - } - ] - ], - "tags": [] - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Average Staging-Time per Destination and Job", - "tooltip": { - "shared": false, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "series", - "name": null, - "show": true, - "values": [ - "total" - ] - }, - "yaxes": [ - { - "format": "s", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - } - ], - "refresh": "", - "schemaVersion": 18, - "style": "dark", - "tags": [], - "templating": { - "list": [ - { - "allValue": null, - "current": { - "text": "Docking_with_VinaTest_2019-07-23 09:29:52.930519", - "value": [ - "Docking_with_VinaTest_2019-07-23 09:29:52.930519" - ] - }, - "datasource": "glx_benchmarker", - "definition": "SELECT benchmark_uuid , value FROM \"runtime_seconds\" WHERE \"benchmark_type\" = ''", - "hide": 0, - "includeAll": true, - "label": "Benchmark UUID", - "multi": true, - "name": "selected_benchmark_uuid", - "options": [], - "query": "SELECT benchmark_uuid , value FROM \"runtime_seconds\" WHERE \"benchmark_type\" = ''", - "refresh": 2, - "regex": "", - "skipUrlSync": false, - "sort": 4, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "allValue": null, - "current": { - "text": "All", - "value": [ - "$__all" - ] - }, - "datasource": "glx_benchmarker", - "definition": "SELECT workflow_name, value FROM \"runtime_seconds\" WHERE \"benchmark_type\" = '' GROUP BY workflow_name", - "hide": 0, - "includeAll": true, - "label": "Workflow", - "multi": true, - "name": "selected_workflow_name", - "options": [], - "query": "SELECT workflow_name, value FROM \"runtime_seconds\" WHERE \"benchmark_type\" = '' GROUP BY workflow_name", - "refresh": 2, - "regex": "", - "skipUrlSync": false, - "sort": 1, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "allValue": null, - "current": { - "text": "All", - "value": [ - "$__all" - ] - }, - "datasource": "glx_benchmarker", - "definition": "SELECT destination_name, value FROM \"runtime_seconds\" WHERE \"benchmark_type\" = '' GROUP BY destination_name", - "hide": 0, - "includeAll": true, - "label": "Destination", - "multi": true, - "name": "selected_destination_name", - "options": [], - "query": "SELECT destination_name, value FROM \"runtime_seconds\" WHERE \"benchmark_type\" = '' GROUP BY destination_name", - "refresh": 2, - "regex": "", - "skipUrlSync": false, - "sort": 0, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "allValue": null, - "current": { - "text": "workflow_status", - "value": "workflow_status" - }, - "datasource": "glx_benchmarker", - "definition": "SHOW MEASUREMENTS ", - "hide": 0, - "includeAll": false, - "label": "Measurement", - "multi": false, - "name": "selected_measurement", - "options": [], - "query": "SHOW MEASUREMENTS ", - "refresh": 2, - "regex": "", - "skipUrlSync": false, - "sort": 0, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - } - ] - }, - "time": { - "from": "now-3h", - "to": "now" - }, - "timepicker": { - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ], - "time_options": [ - "5m", - "15m", - "1h", - "6h", - "12h", - "24h", - "2d", - "7d", - "30d" - ] - }, - "timezone": "", - "title": "Destination-Comparison Benchmarks", - "uid": "Ve6R3OnZz", - "version": 27 -} \ No newline at end of file diff --git a/grafana/provisioning/dashboards/all.yml b/grafana/provisioning/dashboards/all.yml deleted file mode 100644 index e23a61d1..00000000 --- a/grafana/provisioning/dashboards/all.yml +++ /dev/null @@ -1,6 +0,0 @@ -- name: 'default' - org_id: 1 - folder: '' - type: 'file' - options: - folder: '/var/lib/grafana/dashboards' \ No newline at end of file diff --git a/grafana/provisioning/datasources/all.yml b/grafana/provisioning/datasources/all.yml deleted file mode 100644 index 049d99a5..00000000 --- a/grafana/provisioning/datasources/all.yml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: 1 -datasources: - - name: glx_benchmarker - type: influxdb - access: proxy - url: http://influxdb:8086 - user: glx_benchmarker - password: glx_benchmarker - database: glx_benchmarker - isDefault: true \ No newline at end of file diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 00000000..036da981 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,2827 @@ +[[package]] +name = "aiohttp" +version = "3.8.1" +description = "Async http client/server framework (asyncio)" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +aiosignal = ">=1.1.2" +async-timeout = ">=4.0.0a3,<5.0" +attrs = ">=17.3.0" +charset-normalizer = ">=2.0,<3.0" +frozenlist = ">=1.1.1" +multidict = ">=4.5,<7.0" +yarl = ">=1.0,<2.0" + +[package.extras] +speedups = ["aiodns", "brotli", "cchardet"] + +[[package]] +name = "aiosignal" +version = "1.2.0" +description = "aiosignal: a list of registered asynchronous callbacks" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +frozenlist = ">=1.1.0" + +[[package]] +name = "allure-python-commons" +version = "2.9.45" +description = "Common module for integrate allure with python-based frameworks" +category = "main" +optional = false +python-versions = ">=3.5" + +[package.dependencies] +attrs = ">=16.0.0" +pluggy = ">=0.4.0" +six = ">=1.9.0" + +[[package]] +name = "ansible" +version = "5.7.1" +description = "Radically simple IT automation" +category = "main" +optional = false +python-versions = ">=3.8" + +[package.dependencies] +ansible-core = ">=2.12.5,<2.13.0" + +[[package]] +name = "ansible-core" +version = "2.12.5" +description = "Radically simple IT automation" +category = "main" +optional = false +python-versions = ">=3.8" + +[package.dependencies] +cryptography = "*" +jinja2 = "*" +packaging = "*" +PyYAML = "*" +resolvelib = ">=0.5.3,<0.6.0" + +[[package]] +name = "appdirs" +version = "1.4.4" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "argcomplete" +version = "2.0.0" +description = "Bash tab completion for argparse" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.extras] +test = ["coverage", "flake8", "pexpect", "wheel"] + +[[package]] +name = "async-timeout" +version = "4.0.2" +description = "Timeout context manager for asyncio programs" +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "attrs" +version = "21.4.0" +description = "Classes Without Boilerplate" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.extras] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] +docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] + +[[package]] +name = "autopage" +version = "0.5.0" +description = "A library to provide automatic paging for console output" +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "bagit" +version = "1.8.1" +description = "Create and validate BagIt packages" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "bcrypt" +version = "3.2.2" +description = "Modern password hashing for your software and your servers" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +cffi = ">=1.1" + +[package.extras] +tests = ["pytest (>=3.2.1,!=3.3.0)"] +typecheck = ["mypy"] + +[[package]] +name = "beautifulsoup4" +version = "4.11.1" +description = "Screen-scraping library" +category = "main" +optional = false +python-versions = ">=3.6.0" + +[package.dependencies] +soupsieve = ">1.2" + +[package.extras] +html5lib = ["html5lib"] +lxml = ["lxml"] + +[[package]] +name = "bioblend" +version = "0.18.0" +description = "Galaxy and CloudMan API library" +category = "main" +optional = false +python-versions = ">=3.7" +develop = false + +[package.dependencies] +boto = ">=2.9.7" +pyyaml = "*" +requests = ">=2.20.0" +requests-toolbelt = ">=0.5.1,<0.9.0 || >0.9.0" +tuspy = "*" +typing-extensions = "*" + +[package.extras] +testing = ["pytest"] + +[package.source] +type = "git" +url = "https://github.com/galaxyproject/bioblend.git" +reference = "main" +resolved_reference = "17c5e32f3c31416319337fe173569887cccd537e" + +[[package]] +name = "black" +version = "22.3.0" +description = "The uncompromising code formatter." +category = "dev" +optional = false +python-versions = ">=3.6.2" + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "bleach" +version = "5.0.0" +description = "An easy safelist-based HTML-sanitizing tool." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +six = ">=1.9.0" +webencodings = "*" + +[package.extras] +css = ["tinycss2 (>=1.1.0)"] +dev = ["pip-tools (==6.5.1)", "pytest (==7.1.1)", "flake8 (==4.0.1)", "tox (==3.24.5)", "sphinx (==4.3.2)", "twine (==4.0.0)", "wheel (==0.37.1)", "hashin (==0.17.0)", "black (==22.3.0)", "mypy (==0.942)"] + +[[package]] +name = "boltons" +version = "21.0.0" +description = "When they're not builtins, they're boltons." +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "boto" +version = "2.49.0" +description = "Amazon Web Services Library" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "boto3" +version = "1.24.38" +description = "The AWS SDK for Python" +category = "main" +optional = false +python-versions = ">= 3.7" + +[package.dependencies] +botocore = ">=1.27.38,<1.28.0" +jmespath = ">=0.7.1,<2.0.0" +s3transfer = ">=0.6.0,<0.7.0" + +[package.extras] +crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] + +[[package]] +name = "botocore" +version = "1.27.38" +description = "Low-level, data-driven core of boto 3." +category = "main" +optional = false +python-versions = ">= 3.7" + +[package.dependencies] +jmespath = ">=0.7.1,<2.0.0" +python-dateutil = ">=2.1,<3.0.0" +urllib3 = ">=1.25.4,<1.27" + +[package.extras] +crt = ["awscrt (==0.13.8)"] + +[[package]] +name = "cachecontrol" +version = "0.12.11" +description = "httplib2 caching for requests" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +msgpack = ">=0.5.2" +requests = "*" + +[package.extras] +filecache = ["lockfile (>=0.9)"] +redis = ["redis (>=2.10.5)"] + +[[package]] +name = "certifi" +version = "2021.10.8" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "cffi" +version = "1.15.0" +description = "Foreign Function Interface for Python calling C code." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "charset-normalizer" +version = "2.0.12" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "main" +optional = false +python-versions = ">=3.5.0" + +[package.extras] +unicode_backport = ["unicodedata2"] + +[[package]] +name = "click" +version = "8.1.3" +description = "Composable command line interface toolkit" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "cliff" +version = "3.10.1" +description = "Command Line Interface Formulation Framework" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +autopage = ">=0.4.0" +cmd2 = ">=1.0.0" +pbr = ">=2.0.0,<2.1.0 || >2.1.0" +PrettyTable = ">=0.7.2" +pyparsing = ">=2.1.0" +PyYAML = ">=3.12" +stevedore = ">=2.0.1" + +[[package]] +name = "cmd2" +version = "2.4.1" +description = "cmd2 - quickly build feature-rich and user-friendly interactive command line applications in Python" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +attrs = ">=16.3.0" +pyperclip = ">=1.6" +pyreadline3 = {version = "*", markers = "sys_platform == \"win32\""} +wcwidth = ">=0.1.7" + +[package.extras] +dev = ["codecov", "doc8", "flake8", "invoke", "mypy (==0.902)", "nox", "pytest (>=4.6)", "pytest-cov", "pytest-mock", "sphinx", "sphinx-rtd-theme", "sphinx-autobuild", "twine (>=1.11)"] +test = ["codecov", "coverage", "pytest (>=4.6)", "pytest-cov", "pytest-mock", "gnureadline"] +validate = ["flake8", "mypy (==0.902)", "types-pkg-resources"] + +[[package]] +name = "colorama" +version = "0.4.4" +description = "Cross-platform colored terminal text." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "coloredlogs" +version = "15.0.1" +description = "Colored terminal output for Python's logging module" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.dependencies] +humanfriendly = ">=9.1" + +[package.extras] +cron = ["capturer (>=2.4)"] + +[[package]] +name = "configparser" +version = "5.2.0" +description = "Updated configparser from Python 3.8 for Python 2.6+." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "jaraco.tidelift (>=1.4)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "types-backports", "pytest-black (>=0.3.7)", "pytest-mypy"] + +[[package]] +name = "cryptography" +version = "37.0.2" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +cffi = ">=1.12" + +[package.extras] +docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"] +docstest = ["pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"] +pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"] +sdist = ["setuptools_rust (>=0.11.4)"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"] + +[[package]] +name = "cwltool" +version = "3.1.20220502060230" +description = "Common workflow language reference implementation" +category = "main" +optional = false +python-versions = ">=3.6, <4" + +[package.dependencies] +argcomplete = "*" +bagit = ">=1.6.4" +coloredlogs = "*" +mypy-extensions = "*" +prov = "1.5.1" +psutil = ">=5.6.6" +pydot = ">=1.4.1" +pyparsing = "!=3.0.2" +rdflib = ">=4.2.2,<6.2.0" +requests = ">=2.6.1" +"ruamel.yaml" = ">=0.15,<0.17.22" +schema-salad = ">=8.2.20211104054942,<9" +shellescape = ">=3.4.1,<3.9" +typing-extensions = "*" + +[package.extras] +deps = ["galaxy-tool-util (>=21.1.0)"] + +[[package]] +name = "debtcollector" +version = "2.5.0" +description = "A collection of Python deprecation patterns and strategies that help you collect your technical debt in a non-destructive manner." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +wrapt = ">=1.7.0" + +[[package]] +name = "decorator" +version = "5.1.1" +description = "Decorators for Humans" +category = "main" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "distlib" +version = "0.3.4" +description = "Distribution utilities" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "docutils" +version = "0.18.1" +description = "Docutils -- Python Documentation Utilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "dogpile.cache" +version = "1.1.5" +description = "A caching front-end based on the Dogpile lock." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +decorator = ">=4.0.0" +stevedore = ">=3.0.0" + +[[package]] +name = "ephemeris" +version = "0.10.7" +description = "Ephemeris is an opinionated library and set of scripts for managing the bootstrapping of Galaxy project plugins - tools, index data, and workflows." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +bioblend = ">=0.10.0" +galaxy-tool-util = ">=20.9.1" +galaxy-util = ">=20.9.0" +Jinja2 = "*" +pysam = "*" +PyYAML = "*" +six = ">=1.9.0" + +[[package]] +name = "filelock" +version = "3.6.0" +description = "A platform independent file lock." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["furo (>=2021.8.17b43)", "sphinx (>=4.1)", "sphinx-autodoc-typehints (>=1.12)"] +testing = ["covdefaults (>=1.2.0)", "coverage (>=4)", "pytest (>=4)", "pytest-cov", "pytest-timeout (>=1.4.2)"] + +[[package]] +name = "frozenlist" +version = "1.3.0" +description = "A list-like structure which implements collections.abc.MutableSequence" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "future" +version = "0.18.2" +description = "Clean single-source support for Python 3 and 2" +category = "main" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "galaxy-containers" +version = "21.9.0" +description = "Galaxy Container Modeling and Interaction Abstractions" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +galaxy-util = "*" +requests = "*" + +[package.extras] +docker = ["docker"] + +[[package]] +name = "galaxy-tool-util" +version = "21.9.2" +description = "Galaxy Tool and Tool Dependency Utilities" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +galaxy-containers = "*" +galaxy-util = ">=20.1.0.dev0" +lxml = "*" +pydantic = "*" +sortedcontainers = "*" +typing-extensions = "*" + +[package.extras] +edam = ["edam-ontology"] +mulled = ["conda", "cytoolz", "jinja2", "whoosh"] + +[[package]] +name = "galaxy-util" +version = "21.9.0" +description = "Galaxy Generic Utilities" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +bleach = "*" +boltons = "*" +docutils = "*" +markupsafe = "*" +packaging = "*" +pycryptodome = "*" +pyyaml = "*" +requests = "*" +routes = "*" +six = ">=1.9.0" +zipstream-new = "*" + +[package.extras] +jstree = ["dictobj"] +template = ["future", "cheetah3"] + +[[package]] +name = "glob2" +version = "0.7" +description = "Version of the glob module that can capture patterns and supports recursive wildcards" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "gxformat2" +version = "0.15.0" +description = "Galaxy Workflow Format 2 Descriptions" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +bioblend = "*" +pyyaml = "*" +six = ">=1.9.0" + +[[package]] +name = "humanfriendly" +version = "10.0" +description = "Human friendly output for text interfaces using Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.dependencies] +pyreadline3 = {version = "*", markers = "sys_platform == \"win32\" and python_version >= \"3.8\""} + +[[package]] +name = "idna" +version = "3.3" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "iso8601" +version = "1.0.2" +description = "Simple module to parse ISO 8601 dates" +category = "main" +optional = false +python-versions = ">=3.6.2,<4.0" + +[[package]] +name = "isodate" +version = "0.6.1" +description = "An ISO 8601 date/time/duration parser and formatter" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +six = "*" + +[[package]] +name = "isort" +version = "5.10.1" +description = "A Python utility / library to sort Python imports." +category = "dev" +optional = false +python-versions = ">=3.6.1,<4.0" + +[package.extras] +pipfile_deprecated_finder = ["pipreqs", "requirementslib"] +requirements_deprecated_finder = ["pipreqs", "pip-api"] +colors = ["colorama (>=0.4.3,<0.5.0)"] +plugins = ["setuptools"] + +[[package]] +name = "jinja2" +version = "3.1.2" +description = "A very fast and expressive template engine." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "jmespath" +version = "1.0.0" +description = "JSON Matching Expressions" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "jsonpatch" +version = "1.32" +description = "Apply JSON-Patches (RFC 6902)" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.dependencies] +jsonpointer = ">=1.9" + +[[package]] +name = "jsonpointer" +version = "2.3" +description = "Identify specific nodes in a JSON document (RFC 6901)" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "keystoneauth1" +version = "4.5.0" +description = "Authentication Library for OpenStack Identity" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +iso8601 = ">=0.1.11" +os-service-types = ">=1.2.0" +pbr = ">=2.0.0,<2.1.0 || >2.1.0" +requests = ">=2.14.2" +six = ">=1.10.0" +stevedore = ">=1.20.0" + +[package.extras] +betamax = ["betamax (>=0.7.0)", "fixtures (>=3.0.0)", "mock (>=2.0.0)"] +kerberos = ["requests-kerberos (>=0.8.0)"] +oauth1 = ["oauthlib (>=0.6.2)"] +saml2 = ["lxml (>=4.2.0)"] +test = ["PyYAML (>=3.12)", "bandit (>=1.1.0,<1.6.0)", "betamax (>=0.7.0)", "coverage (>=4.0,!=4.4)", "fixtures (>=3.0.0)", "flake8-docstrings (==0.2.1.post1)", "flake8-import-order (>=0.17.1)", "hacking (>=3.0.1,<3.1.0)", "lxml (>=4.2.0)", "oauthlib (>=0.6.2)", "oslo.config (>=5.2.0)", "oslo.utils (>=3.33.0)", "oslotest (>=3.2.0)", "pycodestyle (>=2.0.0,<2.6.0)", "reno (>=3.1.0)", "requests-kerberos (>=0.8.0)", "requests-mock (>=1.2.0)", "stestr (>=1.0.0)", "testresources (>=2.0.0)", "testtools (>=2.2.0)"] + +[[package]] +name = "lockfile" +version = "0.12.2" +description = "Platform-independent file locking module" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "lxml" +version = "4.8.0" +description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" + +[package.extras] +cssselect = ["cssselect (>=0.7)"] +html5 = ["html5lib"] +htmlsoup = ["beautifulsoup4"] +source = ["Cython (>=0.29.7)"] + +[[package]] +name = "markupsafe" +version = "2.1.1" +description = "Safely add untrusted strings to HTML/XML markup." +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "mistune" +version = "0.8.4" +description = "The fastest markdown parser in pure Python" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "msgpack" +version = "1.0.3" +description = "MessagePack (de)serializer." +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "multidict" +version = "6.0.2" +description = "multidict implementation" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "munch" +version = "2.5.0" +description = "A dot-accessible dictionary (a la JavaScript objects)" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +six = "*" + +[package.extras] +testing = ["pytest", "coverage", "astroid (>=1.5.3,<1.6.0)", "pylint (>=1.7.2,<1.8.0)", "astroid (>=2.0)", "pylint (>=2.3.1,<2.4.0)"] +yaml = ["PyYAML (>=5.1.0)"] + +[[package]] +name = "mypy-extensions" +version = "0.4.3" +description = "Experimental type system extensions for programs checked with the mypy typechecker." +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "netaddr" +version = "0.8.0" +description = "A network address manipulation library for Python" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "netifaces" +version = "0.11.0" +description = "Portable network interface information." +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "networkx" +version = "2.8" +description = "Python package for creating and manipulating graphs and networks" +category = "main" +optional = false +python-versions = ">=3.8" + +[package.extras] +default = ["numpy (>=1.19)", "scipy (>=1.8)", "matplotlib (>=3.4)", "pandas (>=1.3)"] +developer = ["pre-commit (>=2.18)", "mypy (>=0.942)"] +doc = ["sphinx (>=4.5)", "pydata-sphinx-theme (>=0.8.1)", "sphinx-gallery (>=0.10)", "numpydoc (>=1.2)", "pillow (>=9.1)", "nb2plots (>=0.6)", "texext (>=0.6.6)"] +extra = ["lxml (>=4.6)", "pygraphviz (>=1.9)", "pydot (>=1.4.2)", "sympy (>=1.10)"] +test = ["pytest (>=7.1)", "pytest-cov (>=3.0)", "codecov (>=2.1)"] + +[[package]] +name = "openstacksdk" +version = "0.61.0" +description = "An SDK for building applications to work with OpenStack" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +appdirs = ">=1.3.0" +cryptography = ">=2.7" +decorator = ">=4.4.1" +"dogpile.cache" = ">=0.6.5" +iso8601 = ">=0.1.11" +jmespath = ">=0.9.0" +jsonpatch = ">=1.16,<1.20 || >1.20" +keystoneauth1 = ">=3.18.0" +munch = ">=2.1.0" +netifaces = ">=0.10.4" +os-service-types = ">=1.7.0" +pbr = ">=2.0.0,<2.1.0 || >2.1.0" +PyYAML = ">=3.13" +requestsexceptions = ">=1.2.0" + +[[package]] +name = "os-service-types" +version = "1.7.0" +description = "Python library for consuming OpenStack sevice-types-authority data" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +pbr = ">=2.0.0,<2.1.0 || >2.1.0" + +[[package]] +name = "osc-lib" +version = "2.5.0" +description = "OpenStackClient Library" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +cliff = ">=3.2.0" +keystoneauth1 = ">=3.14.0" +openstacksdk = ">=0.15.0" +"oslo.i18n" = ">=3.15.3" +"oslo.utils" = ">=3.33.0" +pbr = ">=2.0.0,<2.1.0 || >2.1.0" +simplejson = ">=3.5.1" +stevedore = ">=1.20.0" + +[[package]] +name = "oslo.config" +version = "8.8.0" +description = "Oslo Configuration API" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +debtcollector = ">=1.2.0" +netaddr = ">=0.7.18" +"oslo.i18n" = ">=3.15.3" +PyYAML = ">=5.1" +requests = ">=2.18.0" +rfc3986 = ">=1.2.0" +stevedore = ">=1.20.0" + +[package.extras] +rst_generator = ["rst2txt (>=1.1.0)", "sphinx (>=1.8.0,!=2.1.0)"] +test = ["bandit (>=1.6.0,<1.7.0)", "coverage (>=4.0,!=4.4)", "fixtures (>=3.0.0)", "hacking (>=3.0.1,<3.1.0)", "mypy (>=0.720)", "oslo.log (>=3.36.0)", "oslotest (>=3.2.0)", "pre-commit (>=2.6.0)", "requests-mock (>=1.5.0)", "stestr (>=2.1.0)", "testscenarios (>=0.4)", "testtools (>=2.2.0)"] + +[[package]] +name = "oslo.i18n" +version = "5.1.0" +description = "Oslo i18n library" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pbr = ">=2.0.0,<2.1.0 || >2.1.0" + +[[package]] +name = "oslo.serialization" +version = "4.3.0" +description = "Oslo Serialization library" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +msgpack = ">=0.5.2" +"oslo.utils" = ">=3.33.0" +pbr = ">=2.0.0,<2.1.0 || >2.1.0" +pytz = ">=2013.6" + +[[package]] +name = "oslo.utils" +version = "4.13.0" +description = "Oslo Utility library" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +debtcollector = ">=1.2.0" +iso8601 = ">=0.1.11" +netaddr = ">=0.7.18" +netifaces = ">=0.10.4" +"oslo.i18n" = ">=3.15.3" +packaging = ">=20.4" +pbr = ">=2.0.0,<2.1.0 || >2.1.0" +pyparsing = ">=2.1.0" +pytz = ">=2013.6" + +[[package]] +name = "oyaml" +version = "1.0" +description = "Ordered YAML: drop-in replacement for PyYAML which preserves dict ordering" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +pyyaml = "*" + +[[package]] +name = "packaging" +version = "21.3" +description = "Core utilities for Python packages" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" + +[[package]] +name = "paramiko" +version = "2.10.4" +description = "SSH2 protocol library" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +bcrypt = ">=3.1.3" +cryptography = ">=2.5" +pynacl = ">=1.0.1" +six = "*" + +[package.extras] +all = ["pyasn1 (>=0.1.7)", "pynacl (>=1.0.1)", "bcrypt (>=3.1.3)", "invoke (>=1.3)", "gssapi (>=1.4.1)", "pywin32 (>=2.1.8)"] +ed25519 = ["pynacl (>=1.0.1)", "bcrypt (>=3.1.3)"] +gssapi = ["pyasn1 (>=0.1.7)", "gssapi (>=1.4.1)", "pywin32 (>=2.1.8)"] +invoke = ["invoke (>=1.3)"] + +[[package]] +name = "pathspec" +version = "0.9.0" +description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" + +[[package]] +name = "pbr" +version = "5.9.0" +description = "Python Build Reasonableness" +category = "main" +optional = false +python-versions = ">=2.6" + +[[package]] +name = "planemo" +version = "0.74.9" +description = "Command-line utilities to assist in building tools for the Galaxy project (http://galaxyproject.org/)." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +allure-python-commons = "*" +BeautifulSoup4 = "*" +bioblend = ">=0.14.0" +click = "!=8.0.2" +configparser = "*" +cwltool = ">=1.0.20191225192155" +docutils = "*" +ephemeris = ">=0.10.3" +galaxy-containers = "*" +galaxy-tool-util = ">=21.1.0.dev4" +galaxy-util = ">=20.5.0" +glob2 = "*" +gxformat2 = ">=0.12.0" +jinja2 = "*" +lxml = "*" +oyaml = "*" +pyaml = "*" +pyyaml = "*" +six = ">=1.7.0" +tabulate = "*" +virtualenv = "*" + +[[package]] +name = "platformdirs" +version = "2.5.2" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)", "sphinx (>=4)"] +test = ["appdirs (==1.4.4)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)", "pytest (>=6)"] + +[[package]] +name = "pluggy" +version = "1.0.0" +description = "plugin and hook calling mechanisms for python" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "prettytable" +version = "3.3.0" +description = "A simple Python library for easily displaying tabular data in a visually appealing ASCII table format" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +wcwidth = "*" + +[package.extras] +tests = ["pytest", "pytest-cov", "pytest-lazy-fixture"] + +[[package]] +name = "prov" +version = "1.5.1" +description = "A library for W3C Provenance Data Model supporting PROV-JSON, PROV-XML and PROV-O (RDF)" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +lxml = "*" +networkx = "*" +python-dateutil = "*" +rdflib = ">=4.2.1" +six = ">=1.9.0" + +[package.extras] +dot = ["pydot (>=1.2.0)"] + +[[package]] +name = "psutil" +version = "5.9.0" +description = "Cross-platform lib for process and system monitoring in Python." +category = "main" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.extras] +test = ["ipaddress", "mock", "unittest2", "enum34", "pywin32", "wmi"] + +[[package]] +name = "pyaml" +version = "21.10.1" +description = "PyYAML-based module to produce pretty and readable YAML-serialized data" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +PyYAML = "*" + +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "pycryptodome" +version = "3.14.1" +description = "Cryptographic library for Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "pydantic" +version = "1.9.0" +description = "Data validation and settings management using python 3.6 type hinting" +category = "main" +optional = false +python-versions = ">=3.6.1" + +[package.dependencies] +typing-extensions = ">=3.7.4.3" + +[package.extras] +dotenv = ["python-dotenv (>=0.10.4)"] +email = ["email-validator (>=1.0.3)"] + +[[package]] +name = "pydot" +version = "1.4.2" +description = "Python interface to Graphviz's Dot" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.dependencies] +pyparsing = ">=2.1.4" + +[[package]] +name = "pynacl" +version = "1.5.0" +description = "Python binding to the Networking and Cryptography (NaCl) library" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +cffi = ">=1.4.1" + +[package.extras] +docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"] +tests = ["pytest (>=3.2.1,!=3.3.0)", "hypothesis (>=3.27.0)"] + +[[package]] +name = "pyparsing" +version = "3.0.8" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +category = "main" +optional = false +python-versions = ">=3.6.8" + +[package.extras] +diagrams = ["railroad-diagrams", "jinja2"] + +[[package]] +name = "pyperclip" +version = "1.8.2" +description = "A cross-platform clipboard module for Python. (Only handles plain text for now.)" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "pyreadline3" +version = "3.4.1" +description = "A python implementation of GNU readline." +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "pysam" +version = "0.19.0" +description = "pysam" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "pyserde" +version = "0.6.0" +description = "Yet another serialization library on top of dataclasses" +category = "main" +optional = false +python-versions = ">=3.6.1,<4.0.0" + +[package.dependencies] +jinja2 = "*" +stringcase = "*" +typing_inspect = ">=0.4.0" + +[package.extras] +msgpack = ["msgpack"] +all = ["msgpack", "toml", "pyyaml"] +toml = ["toml"] +yaml = ["pyyaml"] + +[[package]] +name = "python-cinderclient" +version = "8.3.0" +description = "OpenStack Block Storage API Client Library" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +keystoneauth1 = ">=4.3.1" +"oslo.i18n" = ">=5.0.1" +"oslo.utils" = ">=4.8.0" +pbr = ">=5.5.0" +PrettyTable = ">=0.7.2" +requests = ">=2.25.1" +simplejson = ">=3.5.1" +stevedore = ">=3.3.0" + +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-keystoneclient" +version = "4.4.0" +description = "Client Library for OpenStack Identity" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +debtcollector = ">=1.2.0" +keystoneauth1 = ">=3.4.0" +"oslo.config" = ">=5.2.0" +"oslo.i18n" = ">=3.15.3" +"oslo.serialization" = ">=2.18.0,<2.19.1 || >2.19.1" +"oslo.utils" = ">=3.33.0" +pbr = ">=2.0.0,<2.1.0 || >2.1.0" +requests = ">=2.14.2" +six = ">=1.10.0" +stevedore = ">=1.20.0" + +[[package]] +name = "python-novaclient" +version = "17.7.0" +description = "Client library for OpenStack Compute API" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +iso8601 = ">=0.1.11" +keystoneauth1 = ">=3.5.0" +"oslo.i18n" = ">=3.15.3" +"oslo.serialization" = ">=2.18.0,<2.19.1 || >2.19.1" +"oslo.utils" = ">=3.33.0" +pbr = ">=2.0.0,<2.1.0 || >2.1.0" +PrettyTable = ">=0.7.2" +stevedore = ">=2.0.1" + +[[package]] +name = "python-openstackclient" +version = "5.8.0" +description = "OpenStack Command-line Client" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +cliff = ">=3.5.0" +iso8601 = ">=0.1.11" +openstacksdk = ">=0.61.0" +osc-lib = ">=2.3.0" +"oslo.i18n" = ">=3.15.3" +"oslo.utils" = ">=3.33.0" +pbr = ">=2.0.0,<2.1.0 || >2.1.0" +python-cinderclient = ">=3.3.0" +python-keystoneclient = ">=3.22.0" +python-novaclient = ">=17.0.0" +stevedore = ">=2.0.1" + +[[package]] +name = "pytz" +version = "2022.1" +description = "World timezone definitions, modern and historical" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "pyyaml" +version = "6.0" +description = "YAML parser and emitter for Python" +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "rdflib" +version = "6.1.1" +description = "RDFLib is a Python library for working with RDF, a simple yet powerful language for representing information." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +isodate = "*" +pyparsing = "*" + +[package.extras] +docs = ["sphinx (<5)", "sphinxcontrib-apidoc"] +html = ["html5lib"] +tests = ["berkeleydb", "html5lib", "networkx", "pytest", "pytest-cov", "pytest-subtests"] + +[[package]] +name = "repoze.lru" +version = "0.7" +description = "A tiny LRU cache implementation and decorator" +category = "main" +optional = false +python-versions = "*" + +[package.extras] +docs = ["sphinx"] +testing = ["coverage", "nose"] + +[[package]] +name = "requests" +version = "2.27.1" +description = "Python HTTP for Humans." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} +idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] +use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] + +[[package]] +name = "requests-toolbelt" +version = "0.9.1" +description = "A utility belt for advanced users of python-requests" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +requests = ">=2.0.1,<3.0.0" + +[[package]] +name = "requestsexceptions" +version = "1.4.0" +description = "Import exceptions from potentially bundled packages in requests." +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "resolvelib" +version = "0.5.5" +description = "Resolve abstract dependencies into concrete ones" +category = "main" +optional = false +python-versions = "*" + +[package.extras] +examples = ["html5lib", "packaging", "pygraphviz", "requests"] +lint = ["black", "flake8"] +release = ["setl", "towncrier"] +test = ["commentjson", "packaging", "pytest"] + +[[package]] +name = "rfc3986" +version = "2.0.0" +description = "Validating URI References per RFC 3986" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +idna2008 = ["idna"] + +[[package]] +name = "routes" +version = "2.5.1" +description = "Routing Recognition and Generation Tools" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +"repoze.lru" = ">=0.3" +six = "*" + +[package.extras] +docs = ["sphinx", "webob"] +middleware = ["webob"] + +[[package]] +name = "ruamel.yaml" +version = "0.17.21" +description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" +category = "main" +optional = false +python-versions = ">=3" + +[package.dependencies] +"ruamel.yaml.clib" = {version = ">=0.2.6", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""} + +[package.extras] +docs = ["ryd"] +jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] + +[[package]] +name = "ruamel.yaml.clib" +version = "0.2.6" +description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" +category = "main" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "s3transfer" +version = "0.6.0" +description = "An Amazon S3 Transfer Manager" +category = "main" +optional = false +python-versions = ">= 3.7" + +[package.dependencies] +botocore = ">=1.12.36,<2.0a.0" + +[package.extras] +crt = ["botocore[crt] (>=1.20.29,<2.0a.0)"] + +[[package]] +name = "schema-salad" +version = "8.2.20220204150214" +description = "Schema Annotations for Linked Avro Data (SALAD)" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +CacheControl = ">=0.11.7,<0.13" +lockfile = ">=0.9" +mistune = ">=0.8.1,<0.9" +rdflib = ">=4.2.2,<7.0.0" +requests = ">=1.0" +"ruamel.yaml" = ">=0.12.4,<0.16.6 || >0.16.6,<0.18" + +[package.extras] +docs = ["sphinx (>=2.2)", "sphinx-rtd-theme", "pytest (<7)"] +pycodegen = ["black"] + +[[package]] +name = "shellescape" +version = "3.8.1" +description = "Shell escape a string to safely use it as a token in a shell command (backport of cPython shlex.quote for Python versions 2.x & < 3.3)" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "simplejson" +version = "3.17.6" +description = "Simple, fast, extensible JSON encoder/decoder for Python" +category = "main" +optional = false +python-versions = ">=2.5, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "sortedcontainers" +version = "2.4.0" +description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "soupsieve" +version = "2.3.2.post1" +description = "A modern CSS selector implementation for Beautiful Soup." +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "stevedore" +version = "3.5.0" +description = "Manage dynamic plugins for Python applications" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pbr = ">=2.0.0,<2.1.0 || >2.1.0" + +[[package]] +name = "stringcase" +version = "1.2.0" +description = "String case converter." +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "tabulate" +version = "0.8.9" +description = "Pretty-print tabular data" +category = "main" +optional = false +python-versions = "*" + +[package.extras] +widechars = ["wcwidth"] + +[[package]] +name = "tinydb" +version = "4.7.0" +description = "TinyDB is a tiny, document oriented database optimized for your happiness :)" +category = "main" +optional = false +python-versions = ">=3.6,<4.0" + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "tuspy" +version = "1.0.0" +description = "A Python client for the tus resumable upload protocol -> http://tus.io" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +aiohttp = ">=3.6.2" +future = ">=0.16.0" +requests = ">=2.18.4" +six = ">=1.11.0" +tinydb = ">=3.5.0" + +[package.extras] +dev = ["Sphinx (==1.7.1)", "sphinx-autobuild (==2021.3.14)", "tox (>=2.3.1)"] +test = ["aioresponses (>=0.6.2)", "coverage (>=4.2)", "pytest-cov (>=2.3.1,<2.6)", "pytest (>=3.0.3)", "responses (>=0.5.1)"] + +[[package]] +name = "types-cryptography" +version = "3.3.21" +description = "Typing stubs for cryptography" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "types-paramiko" +version = "2.10.0" +description = "Typing stubs for paramiko" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +types-cryptography = "*" + +[[package]] +name = "types-requests" +version = "2.27.25" +description = "Typing stubs for requests" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +types-urllib3 = "<1.27" + +[[package]] +name = "types-urllib3" +version = "1.26.14" +description = "Typing stubs for urllib3" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "typing-extensions" +version = "4.2.0" +description = "Backported and Experimental Type Hints for Python 3.7+" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "typing-inspect" +version = "0.7.1" +description = "Runtime inspection utilities for typing module." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +mypy-extensions = ">=0.3.0" +typing-extensions = ">=3.7.4" + +[[package]] +name = "urllib3" +version = "1.26.9" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" + +[package.extras] +brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"] +secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[[package]] +name = "virtualenv" +version = "20.14.1" +description = "Virtual Python Environment builder" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" + +[package.dependencies] +distlib = ">=0.3.1,<1" +filelock = ">=3.2,<4" +platformdirs = ">=2,<3" +six = ">=1.9.0,<2" + +[package.extras] +docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=21.3)"] +testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)"] + +[[package]] +name = "wcwidth" +version = "0.2.5" +description = "Measures the displayed width of unicode strings in a terminal" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "webencodings" +version = "0.5.1" +description = "Character encoding aliases for legacy web content" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "wrapt" +version = "1.14.1" +description = "Module for decorators, wrappers and monkey patching." +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" + +[[package]] +name = "yarl" +version = "1.7.2" +description = "Yet another URL library" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" + +[[package]] +name = "zipstream-new" +version = "1.1.8" +description = "Zipfile generator that takes input files as well as streams" +category = "main" +optional = false +python-versions = "*" + +[metadata] +lock-version = "1.1" +python-versions = "^3.10" +content-hash = "7011e476e1c8fd579aee0c55c89afb64bd00084548eef8e71ad94f0cb646932f" + +[metadata.files] +aiohttp = [] +aiosignal = [] +allure-python-commons = [ + {file = "allure-python-commons-2.9.45.tar.gz", hash = "sha256:c238d28aeac35e8c7c517d8a2327e25ae5bbf2c30b5e2313d20ef11d75f5549d"}, + {file = "allure_python_commons-2.9.45-py3-none-any.whl", hash = "sha256:3572f0526db3946fb14470c58b0b41d343483aad91d37d414e4641815e13691a"}, +] +ansible = [ + {file = "ansible-5.7.1.tar.gz", hash = "sha256:90a09a34510d194d63556895a225a89e4a8b748b9ae215b55e517a37de063a61"}, +] +ansible-core = [ + {file = "ansible-core-2.12.5.tar.gz", hash = "sha256:1ccc9944f101331adad1ed40d5ac66a8149133052ae34db0267a74aa73beebb3"}, +] +appdirs = [ + {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, + {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, +] +argcomplete = [ + {file = "argcomplete-2.0.0-py2.py3-none-any.whl", hash = "sha256:cffa11ea77999bb0dd27bb25ff6dc142a6796142f68d45b1a26b11f58724561e"}, + {file = "argcomplete-2.0.0.tar.gz", hash = "sha256:6372ad78c89d662035101418ae253668445b391755cfe94ea52f1b9d22425b20"}, +] +async-timeout = [ + {file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"}, + {file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"}, +] +attrs = [ + {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, + {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, +] +autopage = [ + {file = "autopage-0.5.0-py3-none-any.whl", hash = "sha256:57232860f28a1867cdd54b5ea510e292c53d6dfb613f781c5120200666550b06"}, + {file = "autopage-0.5.0.tar.gz", hash = "sha256:5305b43cc0798170d7124e5a2feecf969e45f4a0baf75cb351138114eaf76b83"}, +] +bagit = [ + {file = "bagit-1.8.1-py2.py3-none-any.whl", hash = "sha256:d14dd7e373dd24d41f6748c42f123f7db77098dfa4a0125dbacb4c8bdf767c09"}, + {file = "bagit-1.8.1.tar.gz", hash = "sha256:37df1330d2e8640c8dee8ab6d0073ac701f0614d25f5252f9e05263409cee60c"}, +] +bcrypt = [ + {file = "bcrypt-3.2.2-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:7180d98a96f00b1050e93f5b0f556e658605dd9f524d0b0e68ae7944673f525e"}, + {file = "bcrypt-3.2.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:61bae49580dce88095d669226d5076d0b9d927754cedbdf76c6c9f5099ad6f26"}, + {file = "bcrypt-3.2.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88273d806ab3a50d06bc6a2fc7c87d737dd669b76ad955f449c43095389bc8fb"}, + {file = "bcrypt-3.2.2-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:6d2cb9d969bfca5bc08e45864137276e4c3d3d7de2b162171def3d188bf9d34a"}, + {file = "bcrypt-3.2.2-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b02d6bfc6336d1094276f3f588aa1225a598e27f8e3388f4db9948cb707b521"}, + {file = "bcrypt-3.2.2-cp36-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a2c46100e315c3a5b90fdc53e429c006c5f962529bc27e1dfd656292c20ccc40"}, + {file = "bcrypt-3.2.2-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:7d9ba2e41e330d2af4af6b1b6ec9e6128e91343d0b4afb9282e54e5508f31baa"}, + {file = "bcrypt-3.2.2-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:cd43303d6b8a165c29ec6756afd169faba9396a9472cdff753fe9f19b96ce2fa"}, + {file = "bcrypt-3.2.2-cp36-abi3-win32.whl", hash = "sha256:4e029cef560967fb0cf4a802bcf4d562d3d6b4b1bf81de5ec1abbe0f1adb027e"}, + {file = "bcrypt-3.2.2-cp36-abi3-win_amd64.whl", hash = "sha256:7ff2069240c6bbe49109fe84ca80508773a904f5a8cb960e02a977f7f519b129"}, + {file = "bcrypt-3.2.2.tar.gz", hash = "sha256:433c410c2177057705da2a9f2cd01dd157493b2a7ac14c8593a16b3dab6b6bfb"}, +] +beautifulsoup4 = [ + {file = "beautifulsoup4-4.11.1-py3-none-any.whl", hash = "sha256:58d5c3d29f5a36ffeb94f02f0d786cd53014cf9b3b3951d42e0080d8a9498d30"}, + {file = "beautifulsoup4-4.11.1.tar.gz", hash = "sha256:ad9aa55b65ef2808eb405f46cf74df7fcb7044d5cbc26487f96eb2ef2e436693"}, +] +bioblend = [] +black = [ + {file = "black-22.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2497f9c2386572e28921fa8bec7be3e51de6801f7459dffd6e62492531c47e09"}, + {file = "black-22.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5795a0375eb87bfe902e80e0c8cfaedf8af4d49694d69161e5bd3206c18618bb"}, + {file = "black-22.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e3556168e2e5c49629f7b0f377070240bd5511e45e25a4497bb0073d9dda776a"}, + {file = "black-22.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67c8301ec94e3bcc8906740fe071391bce40a862b7be0b86fb5382beefecd968"}, + {file = "black-22.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:fd57160949179ec517d32ac2ac898b5f20d68ed1a9c977346efbac9c2f1e779d"}, + {file = "black-22.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cc1e1de68c8e5444e8f94c3670bb48a2beef0e91dddfd4fcc29595ebd90bb9ce"}, + {file = "black-22.3.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2fc92002d44746d3e7db7cf9313cf4452f43e9ea77a2c939defce3b10b5c82"}, + {file = "black-22.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:a6342964b43a99dbc72f72812bf88cad8f0217ae9acb47c0d4f141a6416d2d7b"}, + {file = "black-22.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:328efc0cc70ccb23429d6be184a15ce613f676bdfc85e5fe8ea2a9354b4e9015"}, + {file = "black-22.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06f9d8846f2340dfac80ceb20200ea5d1b3f181dd0556b47af4e8e0b24fa0a6b"}, + {file = "black-22.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4efa5fad66b903b4a5f96d91461d90b9507a812b3c5de657d544215bb7877a"}, + {file = "black-22.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8477ec6bbfe0312c128e74644ac8a02ca06bcdb8982d4ee06f209be28cdf163"}, + {file = "black-22.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:637a4014c63fbf42a692d22b55d8ad6968a946b4a6ebc385c5505d9625b6a464"}, + {file = "black-22.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:863714200ada56cbc366dc9ae5291ceb936573155f8bf8e9de92aef51f3ad0f0"}, + {file = "black-22.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10dbe6e6d2988049b4655b2b739f98785a884d4d6b85bc35133a8fb9a2233176"}, + {file = "black-22.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:cee3e11161dde1b2a33a904b850b0899e0424cc331b7295f2a9698e79f9a69a0"}, + {file = "black-22.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5891ef8abc06576985de8fa88e95ab70641de6c1fca97e2a15820a9b69e51b20"}, + {file = "black-22.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:30d78ba6bf080eeaf0b7b875d924b15cd46fec5fd044ddfbad38c8ea9171043a"}, + {file = "black-22.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ee8f1f7228cce7dffc2b464f07ce769f478968bfb3dd1254a4c2eeed84928aad"}, + {file = "black-22.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ee227b696ca60dd1c507be80a6bc849a5a6ab57ac7352aad1ffec9e8b805f21"}, + {file = "black-22.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:9b542ced1ec0ceeff5b37d69838106a6348e60db7b8fdd245294dc1d26136265"}, + {file = "black-22.3.0-py3-none-any.whl", hash = "sha256:bc58025940a896d7e5356952228b68f793cf5fcb342be703c3a2669a1488cb72"}, + {file = "black-22.3.0.tar.gz", hash = "sha256:35020b8886c022ced9282b51b5a875b6d1ab0c387b31a065b84db7c33085ca79"}, +] +bleach = [ + {file = "bleach-5.0.0-py3-none-any.whl", hash = "sha256:08a1fe86d253b5c88c92cc3d810fd8048a16d15762e1e5b74d502256e5926aa1"}, + {file = "bleach-5.0.0.tar.gz", hash = "sha256:c6d6cc054bdc9c83b48b8083e236e5f00f238428666d2ce2e083eaa5fd568565"}, +] +boltons = [ + {file = "boltons-21.0.0-py2.py3-none-any.whl", hash = "sha256:b9bb7b58b2b420bbe11a6025fdef6d3e5edc9f76a42fb467afe7ca212ef9948b"}, + {file = "boltons-21.0.0.tar.gz", hash = "sha256:65e70a79a731a7fe6e98592ecfb5ccf2115873d01dbc576079874629e5c90f13"}, +] +boto = [ + {file = "boto-2.49.0-py2.py3-none-any.whl", hash = "sha256:147758d41ae7240dc989f0039f27da8ca0d53734be0eb869ef16e3adcfa462e8"}, + {file = "boto-2.49.0.tar.gz", hash = "sha256:ea0d3b40a2d852767be77ca343b58a9e3a4b00d9db440efb8da74b4e58025e5a"}, +] +boto3 = [] +botocore = [] +cachecontrol = [ + {file = "CacheControl-0.12.11-py2.py3-none-any.whl", hash = "sha256:2c75d6a8938cb1933c75c50184549ad42728a27e9f6b92fd677c3151aa72555b"}, + {file = "CacheControl-0.12.11.tar.gz", hash = "sha256:a5b9fcc986b184db101aa280b42ecdcdfc524892596f606858e0b7a8b4d9e144"}, +] +certifi = [ + {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, + {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, +] +cffi = [ + {file = "cffi-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962"}, + {file = "cffi-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0"}, + {file = "cffi-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14"}, + {file = "cffi-1.15.0-cp27-cp27m-win32.whl", hash = "sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474"}, + {file = "cffi-1.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6"}, + {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27"}, + {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023"}, + {file = "cffi-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2"}, + {file = "cffi-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382"}, + {file = "cffi-1.15.0-cp310-cp310-win32.whl", hash = "sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55"}, + {file = "cffi-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0"}, + {file = "cffi-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605"}, + {file = "cffi-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e"}, + {file = "cffi-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc"}, + {file = "cffi-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7"}, + {file = "cffi-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66"}, + {file = "cffi-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029"}, + {file = "cffi-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6"}, + {file = "cffi-1.15.0-cp38-cp38-win32.whl", hash = "sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c"}, + {file = "cffi-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443"}, + {file = "cffi-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a"}, + {file = "cffi-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8"}, + {file = "cffi-1.15.0-cp39-cp39-win32.whl", hash = "sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a"}, + {file = "cffi-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139"}, + {file = "cffi-1.15.0.tar.gz", hash = "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954"}, +] +charset-normalizer = [ + {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"}, + {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"}, +] +click = [ + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, +] +cliff = [ + {file = "cliff-3.10.1-py3-none-any.whl", hash = "sha256:a21da482714b9f0b0e9bafaaf2f6a8b3b14161bb47f62e10e28d2fe4ff4b1626"}, + {file = "cliff-3.10.1.tar.gz", hash = "sha256:045aee3f3c64471965d7ad507ce8474a4e2f20815fbb5405a770f8596a2a00a0"}, +] +cmd2 = [ + {file = "cmd2-2.4.1-py3-none-any.whl", hash = "sha256:e6f49b0854b6aec2f20073bae99f1deede16c24b36fde682045d73c80c4cfb51"}, + {file = "cmd2-2.4.1.tar.gz", hash = "sha256:f3b0467daca18fca0dc7838de7726a72ab64127a018a377a86a6ed8ebfdbb25f"}, +] +colorama = [ + {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, + {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, +] +coloredlogs = [ + {file = "coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934"}, + {file = "coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0"}, +] +configparser = [ + {file = "configparser-5.2.0-py3-none-any.whl", hash = "sha256:e8b39238fb6f0153a069aa253d349467c3c4737934f253ef6abac5fe0eca1e5d"}, + {file = "configparser-5.2.0.tar.gz", hash = "sha256:1b35798fdf1713f1c3139016cfcbc461f09edbf099d1fb658d4b7479fcaa3daa"}, +] +cryptography = [ + {file = "cryptography-37.0.2-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:ef15c2df7656763b4ff20a9bc4381d8352e6640cfeb95c2972c38ef508e75181"}, + {file = "cryptography-37.0.2-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:3c81599befb4d4f3d7648ed3217e00d21a9341a9a688ecdd615ff72ffbed7336"}, + {file = "cryptography-37.0.2-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2bd1096476aaac820426239ab534b636c77d71af66c547b9ddcd76eb9c79e004"}, + {file = "cryptography-37.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:31fe38d14d2e5f787e0aecef831457da6cec68e0bb09a35835b0b44ae8b988fe"}, + {file = "cryptography-37.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:093cb351031656d3ee2f4fa1be579a8c69c754cf874206be1d4cf3b542042804"}, + {file = "cryptography-37.0.2-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59b281eab51e1b6b6afa525af2bd93c16d49358404f814fe2c2410058623928c"}, + {file = "cryptography-37.0.2-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:0cc20f655157d4cfc7bada909dc5cc228211b075ba8407c46467f63597c78178"}, + {file = "cryptography-37.0.2-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:f8ec91983e638a9bcd75b39f1396e5c0dc2330cbd9ce4accefe68717e6779e0a"}, + {file = "cryptography-37.0.2-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:46f4c544f6557a2fefa7ac8ac7d1b17bf9b647bd20b16decc8fbcab7117fbc15"}, + {file = "cryptography-37.0.2-cp36-abi3-win32.whl", hash = "sha256:731c8abd27693323b348518ed0e0705713a36d79fdbd969ad968fbef0979a7e0"}, + {file = "cryptography-37.0.2-cp36-abi3-win_amd64.whl", hash = "sha256:471e0d70201c069f74c837983189949aa0d24bb2d751b57e26e3761f2f782b8d"}, + {file = "cryptography-37.0.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a68254dd88021f24a68b613d8c51d5c5e74d735878b9e32cc0adf19d1f10aaf9"}, + {file = "cryptography-37.0.2-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:a7d5137e556cc0ea418dca6186deabe9129cee318618eb1ffecbd35bee55ddc1"}, + {file = "cryptography-37.0.2-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:aeaba7b5e756ea52c8861c133c596afe93dd716cbcacae23b80bc238202dc023"}, + {file = "cryptography-37.0.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95e590dd70642eb2079d280420a888190aa040ad20f19ec8c6e097e38aa29e06"}, + {file = "cryptography-37.0.2-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:1b9362d34363f2c71b7853f6251219298124aa4cc2075ae2932e64c91a3e2717"}, + {file = "cryptography-37.0.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e53258e69874a306fcecb88b7534d61820db8a98655662a3dd2ec7f1afd9132f"}, + {file = "cryptography-37.0.2-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:1f3bfbd611db5cb58ca82f3deb35e83af34bb8cf06043fa61500157d50a70982"}, + {file = "cryptography-37.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:419c57d7b63f5ec38b1199a9521d77d7d1754eb97827bbb773162073ccd8c8d4"}, + {file = "cryptography-37.0.2-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:dc26bb134452081859aa21d4990474ddb7e863aa39e60d1592800a8865a702de"}, + {file = "cryptography-37.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3b8398b3d0efc420e777c40c16764d6870bcef2eb383df9c6dbb9ffe12c64452"}, + {file = "cryptography-37.0.2.tar.gz", hash = "sha256:f224ad253cc9cea7568f49077007d2263efa57396a2f2f78114066fd54b5c68e"}, +] +cwltool = [ + {file = "cwltool-3.1.20220502060230-py3-none-any.whl", hash = "sha256:7577c55fbb9be139e1502a25dec7d71951089200daed599a2c5dbd0f88ad47db"}, + {file = "cwltool-3.1.20220502060230.tar.gz", hash = "sha256:c8df57394d2a9b0581444cf6dfa4754047ec20fdb3073c57154f46b205b85758"}, +] +debtcollector = [ + {file = "debtcollector-2.5.0-py3-none-any.whl", hash = "sha256:1393a527d2c72f143ffa6a629e9c33face6642634eece475b48cab7b04ba61f3"}, + {file = "debtcollector-2.5.0.tar.gz", hash = "sha256:dc9d1ad3f745c43f4bbedbca30f9ffe8905a8c028c9926e61077847d5ea257ab"}, +] +decorator = [ + {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, + {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, +] +distlib = [ + {file = "distlib-0.3.4-py2.py3-none-any.whl", hash = "sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b"}, + {file = "distlib-0.3.4.zip", hash = "sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579"}, +] +docutils = [ + {file = "docutils-0.18.1-py2.py3-none-any.whl", hash = "sha256:23010f129180089fbcd3bc08cfefccb3b890b0050e1ca00c867036e9d161b98c"}, + {file = "docutils-0.18.1.tar.gz", hash = "sha256:679987caf361a7539d76e584cbeddc311e3aee937877c87346f31debc63e9d06"}, +] +"dogpile.cache" = [ + {file = "dogpile.cache-1.1.5-py3-none-any.whl", hash = "sha256:5f9dcf99087240c7733fad5539b0806b52555917dccad1ef43499eaca8b459d9"}, + {file = "dogpile.cache-1.1.5.tar.gz", hash = "sha256:0f01bdc329329a8289af9705ff40fadb1f82a28c336f3174e12142b70d31c756"}, +] +ephemeris = [ + {file = "ephemeris-0.10.7-py2.py3-none-any.whl", hash = "sha256:964c3ef2ce0c0365b5099604eb467db4969f0bd1aaeac0df9579a0e3c8cdbcf4"}, + {file = "ephemeris-0.10.7-py3.7.egg", hash = "sha256:3ed4700655b19d7141b1c3367b741a40bd7a376f08eca0512f58bd8407cf06e7"}, + {file = "ephemeris-0.10.7.tar.gz", hash = "sha256:dd7309159ba03a4446ce7e48da9145c706be5822514c72b70e38b094d8d7f89f"}, +] +filelock = [ + {file = "filelock-3.6.0-py3-none-any.whl", hash = "sha256:f8314284bfffbdcfa0ff3d7992b023d4c628ced6feb957351d4c48d059f56bc0"}, + {file = "filelock-3.6.0.tar.gz", hash = "sha256:9cd540a9352e432c7246a48fe4e8712b10acb1df2ad1f30e8c070b82ae1fed85"}, +] +frozenlist = [] +future = [] +galaxy-containers = [ + {file = "galaxy-containers-21.9.0.tar.gz", hash = "sha256:23fb30f7d90451f037d8b30977640e766c11d2421bab1273624c62c09e9f8a57"}, + {file = "galaxy_containers-21.9.0-py2.py3-none-any.whl", hash = "sha256:befcda1c7d1bf7ad641fd2c871c602c0cd23deb892114b9b25e199972fa40f58"}, +] +galaxy-tool-util = [ + {file = "galaxy-tool-util-21.9.2.tar.gz", hash = "sha256:d14d472466ea227d8b69b58856fa86f6641f1b59326da6b488c9dd94e6e0c752"}, + {file = "galaxy_tool_util-21.9.2-py2.py3-none-any.whl", hash = "sha256:5fa7670f51f7e9c3cdea07f424f3789264f4b70ea3e5d69e6c752ed9e9fc1495"}, +] +galaxy-util = [ + {file = "galaxy-util-21.9.0.tar.gz", hash = "sha256:604ab29115f95b5a671c3ee047f12eb10ea77342a8d2aed0c289224694a493ba"}, + {file = "galaxy_util-21.9.0-py2.py3-none-any.whl", hash = "sha256:dabc821ee33323408a345f96f8a75f0963facbb86f295128c0c4e3131339a093"}, +] +glob2 = [ + {file = "glob2-0.7.tar.gz", hash = "sha256:85c3dbd07c8aa26d63d7aacee34fa86e9a91a3873bc30bf62ec46e531f92ab8c"}, +] +gxformat2 = [ + {file = "gxformat2-0.15.0-py2.py3-none-any.whl", hash = "sha256:e60997d25dc745408b1f4a453fdbaf28d1486de201cb7abf271e261f2d157728"}, + {file = "gxformat2-0.15.0-py3.6.egg", hash = "sha256:90810384151ac5b7bfebceaec10648bd68c12730980b476a0a672b1b16c66f8f"}, + {file = "gxformat2-0.15.0.tar.gz", hash = "sha256:a6c399d3deaae535b66fac9ac1e31e19a68570a6730fe5e2f27086b8980545d5"}, +] +humanfriendly = [ + {file = "humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477"}, + {file = "humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc"}, +] +idna = [ + {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, + {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, +] +iso8601 = [ + {file = "iso8601-1.0.2-py3-none-any.whl", hash = "sha256:d7bc01b1c2a43b259570bb307f057abc578786ea734ba2b87b836c5efc5bd443"}, + {file = "iso8601-1.0.2.tar.gz", hash = "sha256:27f503220e6845d9db954fb212b95b0362d8b7e6c1b2326a87061c3de93594b1"}, +] +isodate = [ + {file = "isodate-0.6.1-py2.py3-none-any.whl", hash = "sha256:0751eece944162659049d35f4f549ed815792b38793f07cf73381c1c87cbed96"}, + {file = "isodate-0.6.1.tar.gz", hash = "sha256:48c5881de7e8b0a0d648cb024c8062dc84e7b840ed81e864c7614fd3c127bde9"}, +] +isort = [ + {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, + {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, +] +jinja2 = [ + {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, + {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, +] +jmespath = [ + {file = "jmespath-1.0.0-py3-none-any.whl", hash = "sha256:e8dcd576ed616f14ec02eed0005c85973b5890083313860136657e24784e4c04"}, + {file = "jmespath-1.0.0.tar.gz", hash = "sha256:a490e280edd1f57d6de88636992d05b71e97d69a26a19f058ecf7d304474bf5e"}, +] +jsonpatch = [ + {file = "jsonpatch-1.32-py2.py3-none-any.whl", hash = "sha256:26ac385719ac9f54df8a2f0827bb8253aa3ea8ab7b3368457bcdb8c14595a397"}, + {file = "jsonpatch-1.32.tar.gz", hash = "sha256:b6ddfe6c3db30d81a96aaeceb6baf916094ffa23d7dd5fa2c13e13f8b6e600c2"}, +] +jsonpointer = [ + {file = "jsonpointer-2.3-py2.py3-none-any.whl", hash = "sha256:51801e558539b4e9cd268638c078c6c5746c9ac96bc38152d443400e4f3793e9"}, + {file = "jsonpointer-2.3.tar.gz", hash = "sha256:97cba51526c829282218feb99dab1b1e6bdf8efd1c43dc9d57be093c0d69c99a"}, +] +keystoneauth1 = [ + {file = "keystoneauth1-4.5.0-py3-none-any.whl", hash = "sha256:47b526d2e813482bd1018916a1c768a5ac6d83c0865a4dc904cb8d6ffd530f1c"}, + {file = "keystoneauth1-4.5.0.tar.gz", hash = "sha256:49b3488966a43eeb0200ea511b997e6403c25d563a984c6330e82a0ebfc4540c"}, +] +lockfile = [ + {file = "lockfile-0.12.2-py2.py3-none-any.whl", hash = "sha256:6c3cb24f344923d30b2785d5ad75182c8ea7ac1b6171b08657258ec7429d50fa"}, + {file = "lockfile-0.12.2.tar.gz", hash = "sha256:6aed02de03cba24efabcd600b30540140634fc06cfa603822d508d5361e9f799"}, +] +lxml = [ + {file = "lxml-4.8.0-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:e1ab2fac607842ac36864e358c42feb0960ae62c34aa4caaf12ada0a1fb5d99b"}, + {file = "lxml-4.8.0-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28d1af847786f68bec57961f31221125c29d6f52d9187c01cd34dc14e2b29430"}, + {file = "lxml-4.8.0-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b92d40121dcbd74831b690a75533da703750f7041b4bf951befc657c37e5695a"}, + {file = "lxml-4.8.0-cp27-cp27m-win32.whl", hash = "sha256:e01f9531ba5420838c801c21c1b0f45dbc9607cb22ea2cf132844453bec863a5"}, + {file = "lxml-4.8.0-cp27-cp27m-win_amd64.whl", hash = "sha256:6259b511b0f2527e6d55ad87acc1c07b3cbffc3d5e050d7e7bcfa151b8202df9"}, + {file = "lxml-4.8.0-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1010042bfcac2b2dc6098260a2ed022968dbdfaf285fc65a3acf8e4eb1ffd1bc"}, + {file = "lxml-4.8.0-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:fa56bb08b3dd8eac3a8c5b7d075c94e74f755fd9d8a04543ae8d37b1612dd170"}, + {file = "lxml-4.8.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:31ba2cbc64516dcdd6c24418daa7abff989ddf3ba6d3ea6f6ce6f2ed6e754ec9"}, + {file = "lxml-4.8.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:31499847fc5f73ee17dbe1b8e24c6dafc4e8d5b48803d17d22988976b0171f03"}, + {file = "lxml-4.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:5f7d7d9afc7b293147e2d506a4596641d60181a35279ef3aa5778d0d9d9123fe"}, + {file = "lxml-4.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:a3c5f1a719aa11866ffc530d54ad965063a8cbbecae6515acbd5f0fae8f48eaa"}, + {file = "lxml-4.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6268e27873a3d191849204d00d03f65c0e343b3bcb518a6eaae05677c95621d1"}, + {file = "lxml-4.8.0-cp310-cp310-win32.whl", hash = "sha256:330bff92c26d4aee79c5bc4d9967858bdbe73fdbdbacb5daf623a03a914fe05b"}, + {file = "lxml-4.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:b2582b238e1658c4061ebe1b4df53c435190d22457642377fd0cb30685cdfb76"}, + {file = "lxml-4.8.0-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a2bfc7e2a0601b475477c954bf167dee6d0f55cb167e3f3e7cefad906e7759f6"}, + {file = "lxml-4.8.0-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a1547ff4b8a833511eeaceacbcd17b043214fcdb385148f9c1bc5556ca9623e2"}, + {file = "lxml-4.8.0-cp35-cp35m-win32.whl", hash = "sha256:a9f1c3489736ff8e1c7652e9dc39f80cff820f23624f23d9eab6e122ac99b150"}, + {file = "lxml-4.8.0-cp35-cp35m-win_amd64.whl", hash = "sha256:530f278849031b0eb12f46cca0e5db01cfe5177ab13bd6878c6e739319bae654"}, + {file = "lxml-4.8.0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:078306d19a33920004addeb5f4630781aaeabb6a8d01398045fcde085091a169"}, + {file = "lxml-4.8.0-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:86545e351e879d0b72b620db6a3b96346921fa87b3d366d6c074e5a9a0b8dadb"}, + {file = "lxml-4.8.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24f5c5ae618395ed871b3d8ebfcbb36e3f1091fd847bf54c4de623f9107942f3"}, + {file = "lxml-4.8.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:bbab6faf6568484707acc052f4dfc3802bdb0cafe079383fbaa23f1cdae9ecd4"}, + {file = "lxml-4.8.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7993232bd4044392c47779a3c7e8889fea6883be46281d45a81451acfd704d7e"}, + {file = "lxml-4.8.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6d6483b1229470e1d8835e52e0ff3c6973b9b97b24cd1c116dca90b57a2cc613"}, + {file = "lxml-4.8.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:ad4332a532e2d5acb231a2e5d33f943750091ee435daffca3fec0a53224e7e33"}, + {file = "lxml-4.8.0-cp36-cp36m-win32.whl", hash = "sha256:db3535733f59e5605a88a706824dfcb9bd06725e709ecb017e165fc1d6e7d429"}, + {file = "lxml-4.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:5f148b0c6133fb928503cfcdfdba395010f997aa44bcf6474fcdd0c5398d9b63"}, + {file = "lxml-4.8.0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:8a31f24e2a0b6317f33aafbb2f0895c0bce772980ae60c2c640d82caac49628a"}, + {file = "lxml-4.8.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:719544565c2937c21a6f76d520e6e52b726d132815adb3447ccffbe9f44203c4"}, + {file = "lxml-4.8.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:c0b88ed1ae66777a798dc54f627e32d3b81c8009967c63993c450ee4cbcbec15"}, + {file = "lxml-4.8.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fa9b7c450be85bfc6cd39f6df8c5b8cbd76b5d6fc1f69efec80203f9894b885f"}, + {file = "lxml-4.8.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e9f84ed9f4d50b74fbc77298ee5c870f67cb7e91dcdc1a6915cb1ff6a317476c"}, + {file = "lxml-4.8.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1d650812b52d98679ed6c6b3b55cbb8fe5a5460a0aef29aeb08dc0b44577df85"}, + {file = "lxml-4.8.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:80bbaddf2baab7e6de4bc47405e34948e694a9efe0861c61cdc23aa774fcb141"}, + {file = "lxml-4.8.0-cp37-cp37m-win32.whl", hash = "sha256:6f7b82934c08e28a2d537d870293236b1000d94d0b4583825ab9649aef7ddf63"}, + {file = "lxml-4.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e1fd7d2fe11f1cb63d3336d147c852f6d07de0d0020d704c6031b46a30b02ca8"}, + {file = "lxml-4.8.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:5045ee1ccd45a89c4daec1160217d363fcd23811e26734688007c26f28c9e9e7"}, + {file = "lxml-4.8.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:0c1978ff1fd81ed9dcbba4f91cf09faf1f8082c9d72eb122e92294716c605428"}, + {file = "lxml-4.8.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cbf2ff155b19dc4d4100f7442f6a697938bf4493f8d3b0c51d45568d5666b5"}, + {file = "lxml-4.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:ce13d6291a5f47c1c8dbd375baa78551053bc6b5e5c0e9bb8e39c0a8359fd52f"}, + {file = "lxml-4.8.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e11527dc23d5ef44d76fef11213215c34f36af1608074561fcc561d983aeb870"}, + {file = "lxml-4.8.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:60d2f60bd5a2a979df28ab309352cdcf8181bda0cca4529769a945f09aba06f9"}, + {file = "lxml-4.8.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:62f93eac69ec0f4be98d1b96f4d6b964855b8255c345c17ff12c20b93f247b68"}, + {file = "lxml-4.8.0-cp38-cp38-win32.whl", hash = "sha256:20b8a746a026017acf07da39fdb10aa80ad9877046c9182442bf80c84a1c4696"}, + {file = "lxml-4.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:891dc8f522d7059ff0024cd3ae79fd224752676447f9c678f2a5c14b84d9a939"}, + {file = "lxml-4.8.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:b6fc2e2fb6f532cf48b5fed57567ef286addcef38c28874458a41b7837a57807"}, + {file = "lxml-4.8.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:74eb65ec61e3c7c019d7169387d1b6ffcfea1b9ec5894d116a9a903636e4a0b1"}, + {file = "lxml-4.8.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:627e79894770783c129cc5e89b947e52aa26e8e0557c7e205368a809da4b7939"}, + {file = "lxml-4.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:545bd39c9481f2e3f2727c78c169425efbfb3fbba6e7db4f46a80ebb249819ca"}, + {file = "lxml-4.8.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5a58d0b12f5053e270510bf12f753a76aaf3d74c453c00942ed7d2c804ca845c"}, + {file = "lxml-4.8.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ec4b4e75fc68da9dc0ed73dcdb431c25c57775383fec325d23a770a64e7ebc87"}, + {file = "lxml-4.8.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5804e04feb4e61babf3911c2a974a5b86f66ee227cc5006230b00ac6d285b3a9"}, + {file = "lxml-4.8.0-cp39-cp39-win32.whl", hash = "sha256:aa0cf4922da7a3c905d000b35065df6184c0dc1d866dd3b86fd961905bbad2ea"}, + {file = "lxml-4.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:dd10383f1d6b7edf247d0960a3db274c07e96cf3a3fc7c41c8448f93eac3fb1c"}, + {file = "lxml-4.8.0-pp37-pypy37_pp73-macosx_10_14_x86_64.whl", hash = "sha256:2403a6d6fb61c285969b71f4a3527873fe93fd0abe0832d858a17fe68c8fa507"}, + {file = "lxml-4.8.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:986b7a96228c9b4942ec420eff37556c5777bfba6758edcb95421e4a614b57f9"}, + {file = "lxml-4.8.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:6fe4ef4402df0250b75ba876c3795510d782def5c1e63890bde02d622570d39e"}, + {file = "lxml-4.8.0-pp38-pypy38_pp73-macosx_10_14_x86_64.whl", hash = "sha256:f10ce66fcdeb3543df51d423ede7e238be98412232fca5daec3e54bcd16b8da0"}, + {file = "lxml-4.8.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:730766072fd5dcb219dd2b95c4c49752a54f00157f322bc6d71f7d2a31fecd79"}, + {file = "lxml-4.8.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:8b99ec73073b37f9ebe8caf399001848fced9c08064effdbfc4da2b5a8d07b93"}, + {file = "lxml-4.8.0.tar.gz", hash = "sha256:f63f62fc60e6228a4ca9abae28228f35e1bd3ce675013d1dfb828688d50c6e23"}, +] +markupsafe = [ + {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, + {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, +] +mistune = [ + {file = "mistune-0.8.4-py2.py3-none-any.whl", hash = "sha256:88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4"}, + {file = "mistune-0.8.4.tar.gz", hash = "sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e"}, +] +msgpack = [ + {file = "msgpack-1.0.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:96acc674bb9c9be63fa8b6dabc3248fdc575c4adc005c440ad02f87ca7edd079"}, + {file = "msgpack-1.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2c3ca57c96c8e69c1a0d2926a6acf2d9a522b41dc4253a8945c4c6cd4981a4e3"}, + {file = "msgpack-1.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0a792c091bac433dfe0a70ac17fc2087d4595ab835b47b89defc8bbabcf5c73"}, + {file = "msgpack-1.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c58cdec1cb5fcea8c2f1771d7b5fec79307d056874f746690bd2bdd609ab147"}, + {file = "msgpack-1.0.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f97c0f35b3b096a330bb4a1a9247d0bd7e1f3a2eba7ab69795501504b1c2c39"}, + {file = "msgpack-1.0.3-cp310-cp310-win32.whl", hash = "sha256:36a64a10b16c2ab31dcd5f32d9787ed41fe68ab23dd66957ca2826c7f10d0b85"}, + {file = "msgpack-1.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c1ba333b4024c17c7591f0f372e2daa3c31db495a9b2af3cf664aef3c14354f7"}, + {file = "msgpack-1.0.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c2140cf7a3ec475ef0938edb6eb363fa704159e0bf71dde15d953bacc1cf9d7d"}, + {file = "msgpack-1.0.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f4c22717c74d44bcd7af353024ce71c6b55346dad5e2cc1ddc17ce8c4507c6b"}, + {file = "msgpack-1.0.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d733a15ade190540c703de209ffbc42a3367600421b62ac0c09fde594da6ec"}, + {file = "msgpack-1.0.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7e03b06f2982aa98d4ddd082a210c3db200471da523f9ac197f2828e80e7770"}, + {file = "msgpack-1.0.3-cp36-cp36m-win32.whl", hash = "sha256:3d875631ecab42f65f9dce6f55ce6d736696ced240f2634633188de2f5f21af9"}, + {file = "msgpack-1.0.3-cp36-cp36m-win_amd64.whl", hash = "sha256:40fb89b4625d12d6027a19f4df18a4de5c64f6f3314325049f219683e07e678a"}, + {file = "msgpack-1.0.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6eef0cf8db3857b2b556213d97dd82de76e28a6524853a9beb3264983391dc1a"}, + {file = "msgpack-1.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d8c332f53ffff01953ad25131272506500b14750c1d0ce8614b17d098252fbc"}, + {file = "msgpack-1.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c0903bd93cbd34653dd63bbfcb99d7539c372795201f39d16fdfde4418de43a"}, + {file = "msgpack-1.0.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bf1e6bfed4860d72106f4e0a1ab519546982b45689937b40257cfd820650b920"}, + {file = "msgpack-1.0.3-cp37-cp37m-win32.whl", hash = "sha256:d02cea2252abc3756b2ac31f781f7a98e89ff9759b2e7450a1c7a0d13302ff50"}, + {file = "msgpack-1.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:2f30dd0dc4dfe6231ad253b6f9f7128ac3202ae49edd3f10d311adc358772dba"}, + {file = "msgpack-1.0.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f201d34dc89342fabb2a10ed7c9a9aaaed9b7af0f16a5923f1ae562b31258dea"}, + {file = "msgpack-1.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bb87f23ae7d14b7b3c21009c4b1705ec107cb21ee71975992f6aca571fb4a42a"}, + {file = "msgpack-1.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a3a5c4b16e9d0edb823fe54b59b5660cc8d4782d7bf2c214cb4b91a1940a8ef"}, + {file = "msgpack-1.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f74da1e5fcf20ade12c6bf1baa17a2dc3604958922de8dc83cbe3eff22e8b611"}, + {file = "msgpack-1.0.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:73a80bd6eb6bcb338c1ec0da273f87420829c266379c8c82fa14c23fb586cfa1"}, + {file = "msgpack-1.0.3-cp38-cp38-win32.whl", hash = "sha256:9fce00156e79af37bb6db4e7587b30d11e7ac6a02cb5bac387f023808cd7d7f4"}, + {file = "msgpack-1.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:9b6f2d714c506e79cbead331de9aae6837c8dd36190d02da74cb409b36162e8a"}, + {file = "msgpack-1.0.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:89908aea5f46ee1474cc37fbc146677f8529ac99201bc2faf4ef8edc023c2bf3"}, + {file = "msgpack-1.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:973ad69fd7e31159eae8f580f3f707b718b61141838321c6fa4d891c4a2cca52"}, + {file = "msgpack-1.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da24375ab4c50e5b7486c115a3198d207954fe10aaa5708f7b65105df09109b2"}, + {file = "msgpack-1.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a598d0685e4ae07a0672b59792d2cc767d09d7a7f39fd9bd37ff84e060b1a996"}, + {file = "msgpack-1.0.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4c309a68cb5d6bbd0c50d5c71a25ae81f268c2dc675c6f4ea8ab2feec2ac4e2"}, + {file = "msgpack-1.0.3-cp39-cp39-win32.whl", hash = "sha256:494471d65b25a8751d19c83f1a482fd411d7ca7a3b9e17d25980a74075ba0e88"}, + {file = "msgpack-1.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:f01b26c2290cbd74316990ba84a14ac3d599af9cebefc543d241a66e785cf17d"}, + {file = "msgpack-1.0.3.tar.gz", hash = "sha256:51fdc7fb93615286428ee7758cecc2f374d5ff363bdd884c7ea622a7a327a81e"}, +] +multidict = [] +munch = [ + {file = "munch-2.5.0-py2.py3-none-any.whl", hash = "sha256:6f44af89a2ce4ed04ff8de41f70b226b984db10a91dcc7b9ac2efc1c77022fdd"}, + {file = "munch-2.5.0.tar.gz", hash = "sha256:2d735f6f24d4dba3417fa448cae40c6e896ec1fdab6cdb5e6510999758a4dbd2"}, +] +mypy-extensions = [ + {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, + {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, +] +netaddr = [ + {file = "netaddr-0.8.0-py2.py3-none-any.whl", hash = "sha256:9666d0232c32d2656e5e5f8d735f58fd6c7457ce52fc21c98d45f2af78f990ac"}, + {file = "netaddr-0.8.0.tar.gz", hash = "sha256:d6cc57c7a07b1d9d2e917aa8b36ae8ce61c35ba3fcd1b83ca31c5a0ee2b5a243"}, +] +netifaces = [ + {file = "netifaces-0.11.0-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eb4813b77d5df99903af4757ce980a98c4d702bbcb81f32a0b305a1537bdf0b1"}, + {file = "netifaces-0.11.0-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:5f9ca13babe4d845e400921973f6165a4c2f9f3379c7abfc7478160e25d196a4"}, + {file = "netifaces-0.11.0-cp27-cp27m-win32.whl", hash = "sha256:7dbb71ea26d304e78ccccf6faccef71bb27ea35e259fb883cfd7fd7b4f17ecb1"}, + {file = "netifaces-0.11.0-cp27-cp27m-win_amd64.whl", hash = "sha256:0f6133ac02521270d9f7c490f0c8c60638ff4aec8338efeff10a1b51506abe85"}, + {file = "netifaces-0.11.0-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:08e3f102a59f9eaef70948340aeb6c89bd09734e0dca0f3b82720305729f63ea"}, + {file = "netifaces-0.11.0-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:c03fb2d4ef4e393f2e6ffc6376410a22a3544f164b336b3a355226653e5efd89"}, + {file = "netifaces-0.11.0-cp34-cp34m-win32.whl", hash = "sha256:73ff21559675150d31deea8f1f8d7e9a9a7e4688732a94d71327082f517fc6b4"}, + {file = "netifaces-0.11.0-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:815eafdf8b8f2e61370afc6add6194bd5a7252ae44c667e96c4c1ecf418811e4"}, + {file = "netifaces-0.11.0-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:50721858c935a76b83dd0dd1ab472cad0a3ef540a1408057624604002fcfb45b"}, + {file = "netifaces-0.11.0-cp35-cp35m-win32.whl", hash = "sha256:c9a3a47cd3aaeb71e93e681d9816c56406ed755b9442e981b07e3618fb71d2ac"}, + {file = "netifaces-0.11.0-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:aab1dbfdc55086c789f0eb37affccf47b895b98d490738b81f3b2360100426be"}, + {file = "netifaces-0.11.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c37a1ca83825bc6f54dddf5277e9c65dec2f1b4d0ba44b8fd42bc30c91aa6ea1"}, + {file = "netifaces-0.11.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:28f4bf3a1361ab3ed93c5ef360c8b7d4a4ae060176a3529e72e5e4ffc4afd8b0"}, + {file = "netifaces-0.11.0-cp36-cp36m-win32.whl", hash = "sha256:2650beee182fed66617e18474b943e72e52f10a24dc8cac1db36c41ee9c041b7"}, + {file = "netifaces-0.11.0-cp36-cp36m-win_amd64.whl", hash = "sha256:cb925e1ca024d6f9b4f9b01d83215fd00fe69d095d0255ff3f64bffda74025c8"}, + {file = "netifaces-0.11.0-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:84e4d2e6973eccc52778735befc01638498781ce0e39aa2044ccfd2385c03246"}, + {file = "netifaces-0.11.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:18917fbbdcb2d4f897153c5ddbb56b31fa6dd7c3fa9608b7e3c3a663df8206b5"}, + {file = "netifaces-0.11.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:48324183af7f1bc44f5f197f3dad54a809ad1ef0c78baee2c88f16a5de02c4c9"}, + {file = "netifaces-0.11.0-cp37-cp37m-win32.whl", hash = "sha256:8f7da24eab0d4184715d96208b38d373fd15c37b0dafb74756c638bd619ba150"}, + {file = "netifaces-0.11.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2479bb4bb50968089a7c045f24d120f37026d7e802ec134c4490eae994c729b5"}, + {file = "netifaces-0.11.0-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:3ecb3f37c31d5d51d2a4d935cfa81c9bc956687c6f5237021b36d6fdc2815b2c"}, + {file = "netifaces-0.11.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:96c0fe9696398253f93482c84814f0e7290eee0bfec11563bd07d80d701280c3"}, + {file = "netifaces-0.11.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:c92ff9ac7c2282009fe0dcb67ee3cd17978cffbe0c8f4b471c00fe4325c9b4d4"}, + {file = "netifaces-0.11.0-cp38-cp38-win32.whl", hash = "sha256:d07b01c51b0b6ceb0f09fc48ec58debd99d2c8430b09e56651addeaf5de48048"}, + {file = "netifaces-0.11.0-cp38-cp38-win_amd64.whl", hash = "sha256:469fc61034f3daf095e02f9f1bbac07927b826c76b745207287bc594884cfd05"}, + {file = "netifaces-0.11.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:5be83986100ed1fdfa78f11ccff9e4757297735ac17391b95e17e74335c2047d"}, + {file = "netifaces-0.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:54ff6624eb95b8a07e79aa8817288659af174e954cca24cdb0daeeddfc03c4ff"}, + {file = "netifaces-0.11.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:841aa21110a20dc1621e3dd9f922c64ca64dd1eb213c47267a2c324d823f6c8f"}, + {file = "netifaces-0.11.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e76c7f351e0444721e85f975ae92718e21c1f361bda946d60a214061de1f00a1"}, + {file = "netifaces-0.11.0.tar.gz", hash = "sha256:043a79146eb2907edf439899f262b3dfe41717d34124298ed281139a8b93ca32"}, +] +networkx = [ + {file = "networkx-2.8-py3-none-any.whl", hash = "sha256:1a1e8fe052cc1b4e0339b998f6795099562a264a13a5af7a32cad45ab9d4e126"}, + {file = "networkx-2.8.tar.gz", hash = "sha256:4a52cf66aed221955420e11b3e2e05ca44196b4829aab9576d4d439212b0a14f"}, +] +openstacksdk = [ + {file = "openstacksdk-0.61.0-py3-none-any.whl", hash = "sha256:9894d3d510563dcfc50c4755287dbfbf98def1f37caf2cfc15e9d0e1fd5d9a41"}, + {file = "openstacksdk-0.61.0.tar.gz", hash = "sha256:3eed308871230f0c53a8f58b6c5a358b184080c6b2c6bc69ab088eea057aa127"}, +] +os-service-types = [ + {file = "os-service-types-1.7.0.tar.gz", hash = "sha256:31800299a82239363995b91f1ebf9106ac7758542a1e4ef6dc737a5932878c6c"}, + {file = "os_service_types-1.7.0-py2.py3-none-any.whl", hash = "sha256:0505c72205690910077fb72b88f2a1f07533c8d39f2fe75b29583481764965d6"}, +] +osc-lib = [ + {file = "osc-lib-2.5.0.tar.gz", hash = "sha256:d8f8a450fab2a1294e0aef8cdc9a255a1bcc5b58e1b2f6098e35e6db1e13e9d1"}, + {file = "osc_lib-2.5.0-py3-none-any.whl", hash = "sha256:d6ad4f39a4582bc0ac9fba93a73b29d5ac22cb36e325db646194039fed5fa33b"}, +] +"oslo.config" = [ + {file = "oslo.config-8.8.0-py3-none-any.whl", hash = "sha256:b1e2a398450ea35a8e5630d8b23057b8939838c4433cd25a20cc3a36d5df9e3b"}, + {file = "oslo.config-8.8.0.tar.gz", hash = "sha256:96933d3011dae15608a11616bfb00d947e22da3cb09b6ff37ddd7576abd4764c"}, +] +"oslo.i18n" = [ + {file = "oslo.i18n-5.1.0-py3-none-any.whl", hash = "sha256:75086cfd898819638ca741159f677e2073a78ca86a9c9be8d38b46800cdf2dc9"}, + {file = "oslo.i18n-5.1.0.tar.gz", hash = "sha256:6bf111a6357d5449640852de4640eae4159b5562bbba4c90febb0034abc095d0"}, +] +"oslo.serialization" = [ + {file = "oslo.serialization-4.3.0-py3-none-any.whl", hash = "sha256:6c1c483231c3827787af9b6ca4a45f4e45fe364772a24692b02de78fe48eafb1"}, + {file = "oslo.serialization-4.3.0.tar.gz", hash = "sha256:3aa472f434aee8bbcc0725312b7f409aa1fa54bbc134904124cf49b0e86b9115"}, +] +"oslo.utils" = [ + {file = "oslo.utils-4.13.0-py3-none-any.whl", hash = "sha256:dab26f205980a379fe7068dd4f9010809a2ae7dddcbecde53e18cf8fa4a251d9"}, + {file = "oslo.utils-4.13.0.tar.gz", hash = "sha256:45ba8aaa5ed056a8e8e46059ef93d5c2d7b9c99bc7480e361cf5783e47f28fba"}, +] +oyaml = [ + {file = "oyaml-1.0-py2.py3-none-any.whl", hash = "sha256:3a378747b7fb2425533d1ce41962d6921cda075d46bb480a158d45242d156323"}, + {file = "oyaml-1.0.tar.gz", hash = "sha256:ed8fc096811f4763e1907dce29c35895d6d5936c4d0400fe843a91133d4744ed"}, +] +packaging = [ + {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, + {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, +] +paramiko = [ + {file = "paramiko-2.10.4-py2.py3-none-any.whl", hash = "sha256:3c9ed6084f4b671ab66dc3c729092d32d96c3258f1426071301cb33654b09027"}, + {file = "paramiko-2.10.4.tar.gz", hash = "sha256:3d2e650b6812ce6d160abff701d6ef4434ec97934b13e95cf1ad3da70ffb5c58"}, +] +pathspec = [ + {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, + {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, +] +pbr = [ + {file = "pbr-5.9.0-py2.py3-none-any.whl", hash = "sha256:e547125940bcc052856ded43be8e101f63828c2d94239ffbe2b327ba3d5ccf0a"}, + {file = "pbr-5.9.0.tar.gz", hash = "sha256:e8dca2f4b43560edef58813969f52a56cef023146cbb8931626db80e6c1c4308"}, +] +planemo = [ + {file = "planemo-0.74.9-py2.py3-none-any.whl", hash = "sha256:3fac2eef24c35b43b1949d8ed8d7b3928edb5f47e820cf58defda825c70ccc1c"}, + {file = "planemo-0.74.9.tar.gz", hash = "sha256:4d8850c2e967810846f51a290f8f9adc9296c9e5531a23d8328fd3aea414155c"}, +] +platformdirs = [ + {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, + {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, +] +pluggy = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] +prettytable = [ + {file = "prettytable-3.3.0-py3-none-any.whl", hash = "sha256:d1c34d72ea2c0ffd6ce5958e71c428eb21a3d40bf3133afe319b24aeed5af407"}, + {file = "prettytable-3.3.0.tar.gz", hash = "sha256:118eb54fd2794049b810893653b20952349df6d3bc1764e7facd8a18064fa9b0"}, +] +prov = [ + {file = "prov-1.5.1-py2.py3-none-any.whl", hash = "sha256:5c930cbbd05424aa3066d336dc31d314dd9fa0280caeab064288e592ed716bea"}, + {file = "prov-1.5.1.tar.gz", hash = "sha256:7a2d72b0df43cd9c6e374d815c8ce3cd5ca371d54f98f837853ac9fcc98aee4c"}, +] +psutil = [ + {file = "psutil-5.9.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:55ce319452e3d139e25d6c3f85a1acf12d1607ddedea5e35fb47a552c051161b"}, + {file = "psutil-5.9.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:7336292a13a80eb93c21f36bde4328aa748a04b68c13d01dfddd67fc13fd0618"}, + {file = "psutil-5.9.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:cb8d10461c1ceee0c25a64f2dd54872b70b89c26419e147a05a10b753ad36ec2"}, + {file = "psutil-5.9.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:7641300de73e4909e5d148e90cc3142fb890079e1525a840cf0dfd39195239fd"}, + {file = "psutil-5.9.0-cp27-none-win32.whl", hash = "sha256:ea42d747c5f71b5ccaa6897b216a7dadb9f52c72a0fe2b872ef7d3e1eacf3ba3"}, + {file = "psutil-5.9.0-cp27-none-win_amd64.whl", hash = "sha256:ef216cc9feb60634bda2f341a9559ac594e2eeaadd0ba187a4c2eb5b5d40b91c"}, + {file = "psutil-5.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90a58b9fcae2dbfe4ba852b57bd4a1dded6b990a33d6428c7614b7d48eccb492"}, + {file = "psutil-5.9.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff0d41f8b3e9ebb6b6110057e40019a432e96aae2008951121ba4e56040b84f3"}, + {file = "psutil-5.9.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:742c34fff804f34f62659279ed5c5b723bb0195e9d7bd9907591de9f8f6558e2"}, + {file = "psutil-5.9.0-cp310-cp310-win32.whl", hash = "sha256:8293942e4ce0c5689821f65ce6522ce4786d02af57f13c0195b40e1edb1db61d"}, + {file = "psutil-5.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:9b51917c1af3fa35a3f2dabd7ba96a2a4f19df3dec911da73875e1edaf22a40b"}, + {file = "psutil-5.9.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e9805fed4f2a81de98ae5fe38b75a74c6e6ad2df8a5c479594c7629a1fe35f56"}, + {file = "psutil-5.9.0-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c51f1af02334e4b516ec221ee26b8fdf105032418ca5a5ab9737e8c87dafe203"}, + {file = "psutil-5.9.0-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32acf55cb9a8cbfb29167cd005951df81b567099295291bcfd1027365b36591d"}, + {file = "psutil-5.9.0-cp36-cp36m-win32.whl", hash = "sha256:e5c783d0b1ad6ca8a5d3e7b680468c9c926b804be83a3a8e95141b05c39c9f64"}, + {file = "psutil-5.9.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d62a2796e08dd024b8179bd441cb714e0f81226c352c802fca0fd3f89eeacd94"}, + {file = "psutil-5.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3d00a664e31921009a84367266b35ba0aac04a2a6cad09c550a89041034d19a0"}, + {file = "psutil-5.9.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7779be4025c540d1d65a2de3f30caeacc49ae7a2152108adeaf42c7534a115ce"}, + {file = "psutil-5.9.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:072664401ae6e7c1bfb878c65d7282d4b4391f1bc9a56d5e03b5a490403271b5"}, + {file = "psutil-5.9.0-cp37-cp37m-win32.whl", hash = "sha256:df2c8bd48fb83a8408c8390b143c6a6fa10cb1a674ca664954de193fdcab36a9"}, + {file = "psutil-5.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1d7b433519b9a38192dfda962dd8f44446668c009833e1429a52424624f408b4"}, + {file = "psutil-5.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c3400cae15bdb449d518545cbd5b649117de54e3596ded84aacabfbb3297ead2"}, + {file = "psutil-5.9.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2237f35c4bbae932ee98902a08050a27821f8f6dfa880a47195e5993af4702d"}, + {file = "psutil-5.9.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1070a9b287846a21a5d572d6dddd369517510b68710fca56b0e9e02fd24bed9a"}, + {file = "psutil-5.9.0-cp38-cp38-win32.whl", hash = "sha256:76cebf84aac1d6da5b63df11fe0d377b46b7b500d892284068bacccf12f20666"}, + {file = "psutil-5.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:3151a58f0fbd8942ba94f7c31c7e6b310d2989f4da74fcbf28b934374e9bf841"}, + {file = "psutil-5.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:539e429da49c5d27d5a58e3563886057f8fc3868a5547b4f1876d9c0f007bccf"}, + {file = "psutil-5.9.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58c7d923dc209225600aec73aa2c4ae8ea33b1ab31bc11ef8a5933b027476f07"}, + {file = "psutil-5.9.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3611e87eea393f779a35b192b46a164b1d01167c9d323dda9b1e527ea69d697d"}, + {file = "psutil-5.9.0-cp39-cp39-win32.whl", hash = "sha256:4e2fb92e3aeae3ec3b7b66c528981fd327fb93fd906a77215200404444ec1845"}, + {file = "psutil-5.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:7d190ee2eaef7831163f254dc58f6d2e2a22e27382b936aab51c835fc080c3d3"}, + {file = "psutil-5.9.0.tar.gz", hash = "sha256:869842dbd66bb80c3217158e629d6fceaecc3a3166d3d1faee515b05dd26ca25"}, +] +pyaml = [ + {file = "pyaml-21.10.1-py2.py3-none-any.whl", hash = "sha256:19985ed303c3a985de4cf8fd329b6d0a5a5b5c9035ea240eccc709ebacbaf4a0"}, + {file = "pyaml-21.10.1.tar.gz", hash = "sha256:c6519fee13bf06e3bb3f20cacdea8eba9140385a7c2546df5dbae4887f768383"}, +] +pycparser = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] +pycryptodome = [ + {file = "pycryptodome-3.14.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:75a3a364fee153e77ed889c957f6f94ec6d234b82e7195b117180dcc9fc16f96"}, + {file = "pycryptodome-3.14.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:aae395f79fa549fb1f6e3dc85cf277f0351e15a22e6547250056c7f0c990d6a5"}, + {file = "pycryptodome-3.14.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:f403a3e297a59d94121cb3ee4b1cf41f844332940a62d71f9e4a009cc3533493"}, + {file = "pycryptodome-3.14.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ce7a875694cd6ccd8682017a7c06c6483600f151d8916f2b25cf7a439e600263"}, + {file = "pycryptodome-3.14.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:a36ab51674b014ba03da7f98b675fcb8eabd709a2d8e18219f784aba2db73b72"}, + {file = "pycryptodome-3.14.1-cp27-cp27m-manylinux2014_aarch64.whl", hash = "sha256:50a5346af703330944bea503106cd50c9c2212174cfcb9939db4deb5305a8367"}, + {file = "pycryptodome-3.14.1-cp27-cp27m-win32.whl", hash = "sha256:36e3242c4792e54ed906c53f5d840712793dc68b726ec6baefd8d978c5282d30"}, + {file = "pycryptodome-3.14.1-cp27-cp27m-win_amd64.whl", hash = "sha256:c880a98376939165b7dc504559f60abe234b99e294523a273847f9e7756f4132"}, + {file = "pycryptodome-3.14.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:dcd65355acba9a1d0fc9b923875da35ed50506e339b35436277703d7ace3e222"}, + {file = "pycryptodome-3.14.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:766a8e9832128c70012e0c2b263049506cbf334fb21ff7224e2704102b6ef59e"}, + {file = "pycryptodome-3.14.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:2562de213960693b6d657098505fd4493c45f3429304da67efcbeb61f0edfe89"}, + {file = "pycryptodome-3.14.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:d1b7739b68a032ad14c5e51f7e4e1a5f92f3628bba024a2bda1f30c481fc85d8"}, + {file = "pycryptodome-3.14.1-cp27-cp27mu-manylinux2014_aarch64.whl", hash = "sha256:27e92c1293afcb8d2639baf7eb43f4baada86e4de0f1fb22312bfc989b95dae2"}, + {file = "pycryptodome-3.14.1-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:f2772af1c3ef8025c85335f8b828d0193fa1e43256621f613280e2c81bfad423"}, + {file = "pycryptodome-3.14.1-cp35-abi3-manylinux1_i686.whl", hash = "sha256:9ec761a35dbac4a99dcbc5cd557e6e57432ddf3e17af8c3c86b44af9da0189c0"}, + {file = "pycryptodome-3.14.1-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:e64738207a02a83590df35f59d708bf1e7ea0d6adce712a777be2967e5f7043c"}, + {file = "pycryptodome-3.14.1-cp35-abi3-manylinux2010_i686.whl", hash = "sha256:e24d4ec4b029611359566c52f31af45c5aecde7ef90bf8f31620fd44c438efe7"}, + {file = "pycryptodome-3.14.1-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:8b5c28058102e2974b9868d72ae5144128485d466ba8739abd674b77971454cc"}, + {file = "pycryptodome-3.14.1-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:924b6aad5386fb54f2645f22658cb0398b1f25bc1e714a6d1522c75d527deaa5"}, + {file = "pycryptodome-3.14.1-cp35-abi3-win32.whl", hash = "sha256:53dedbd2a6a0b02924718b520a723e88bcf22e37076191eb9b91b79934fb2192"}, + {file = "pycryptodome-3.14.1-cp35-abi3-win_amd64.whl", hash = "sha256:ea56a35fd0d13121417d39a83f291017551fa2c62d6daa6b04af6ece7ed30d84"}, + {file = "pycryptodome-3.14.1-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:028dcbf62d128b4335b61c9fbb7dd8c376594db607ef36d5721ee659719935d5"}, + {file = "pycryptodome-3.14.1-pp27-pypy_73-manylinux1_x86_64.whl", hash = "sha256:69f05aaa90c99ac2f2af72d8d7f185f729721ad7c4be89e9e3d0ab101b0ee875"}, + {file = "pycryptodome-3.14.1-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:12ef157eb1e01a157ca43eda275fa68f8db0dd2792bc4fe00479ab8f0e6ae075"}, + {file = "pycryptodome-3.14.1-pp27-pypy_73-win32.whl", hash = "sha256:f572a3ff7b6029dd9b904d6be4e0ce9e309dcb847b03e3ac8698d9d23bb36525"}, + {file = "pycryptodome-3.14.1-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9924248d6920b59c260adcae3ee231cd5af404ac706ad30aa4cd87051bf09c50"}, + {file = "pycryptodome-3.14.1-pp36-pypy36_pp73-manylinux1_x86_64.whl", hash = "sha256:e0c04c41e9ade19fbc0eff6aacea40b831bfcb2c91c266137bcdfd0d7b2f33ba"}, + {file = "pycryptodome-3.14.1-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:893f32210de74b9f8ac869ed66c97d04e7d351182d6d39ebd3b36d3db8bda65d"}, + {file = "pycryptodome-3.14.1-pp36-pypy36_pp73-win32.whl", hash = "sha256:7fb90a5000cc9c9ff34b4d99f7f039e9c3477700e309ff234eafca7b7471afc0"}, + {file = "pycryptodome-3.14.1.tar.gz", hash = "sha256:e04e40a7f8c1669195536a37979dd87da2c32dbdc73d6fe35f0077b0c17c803b"}, +] +pydantic = [ + {file = "pydantic-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cb23bcc093697cdea2708baae4f9ba0e972960a835af22560f6ae4e7e47d33f5"}, + {file = "pydantic-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1d5278bd9f0eee04a44c712982343103bba63507480bfd2fc2790fa70cd64cf4"}, + {file = "pydantic-1.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab624700dc145aa809e6f3ec93fb8e7d0f99d9023b713f6a953637429b437d37"}, + {file = "pydantic-1.9.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c8d7da6f1c1049eefb718d43d99ad73100c958a5367d30b9321b092771e96c25"}, + {file = "pydantic-1.9.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3c3b035103bd4e2e4a28da9da7ef2fa47b00ee4a9cf4f1a735214c1bcd05e0f6"}, + {file = "pydantic-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3011b975c973819883842c5ab925a4e4298dffccf7782c55ec3580ed17dc464c"}, + {file = "pydantic-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:086254884d10d3ba16da0588604ffdc5aab3f7f09557b998373e885c690dd398"}, + {file = "pydantic-1.9.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0fe476769acaa7fcddd17cadd172b156b53546ec3614a4d880e5d29ea5fbce65"}, + {file = "pydantic-1.9.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8e9dcf1ac499679aceedac7e7ca6d8641f0193c591a2d090282aaf8e9445a46"}, + {file = "pydantic-1.9.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1e4c28f30e767fd07f2ddc6f74f41f034d1dd6bc526cd59e63a82fe8bb9ef4c"}, + {file = "pydantic-1.9.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:c86229333cabaaa8c51cf971496f10318c4734cf7b641f08af0a6fbf17ca3054"}, + {file = "pydantic-1.9.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:c0727bda6e38144d464daec31dff936a82917f431d9c39c39c60a26567eae3ed"}, + {file = "pydantic-1.9.0-cp36-cp36m-win_amd64.whl", hash = "sha256:dee5ef83a76ac31ab0c78c10bd7d5437bfdb6358c95b91f1ba7ff7b76f9996a1"}, + {file = "pydantic-1.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d9c9bdb3af48e242838f9f6e6127de9be7063aad17b32215ccc36a09c5cf1070"}, + {file = "pydantic-1.9.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ee7e3209db1e468341ef41fe263eb655f67f5c5a76c924044314e139a1103a2"}, + {file = "pydantic-1.9.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b6037175234850ffd094ca77bf60fb54b08b5b22bc85865331dd3bda7a02fa1"}, + {file = "pydantic-1.9.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b2571db88c636d862b35090ccf92bf24004393f85c8870a37f42d9f23d13e032"}, + {file = "pydantic-1.9.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8b5ac0f1c83d31b324e57a273da59197c83d1bb18171e512908fe5dc7278a1d6"}, + {file = "pydantic-1.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:bbbc94d0c94dd80b3340fc4f04fd4d701f4b038ebad72c39693c794fd3bc2d9d"}, + {file = "pydantic-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e0896200b6a40197405af18828da49f067c2fa1f821491bc8f5bde241ef3f7d7"}, + {file = "pydantic-1.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7bdfdadb5994b44bd5579cfa7c9b0e1b0e540c952d56f627eb227851cda9db77"}, + {file = "pydantic-1.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:574936363cd4b9eed8acdd6b80d0143162f2eb654d96cb3a8ee91d3e64bf4cf9"}, + {file = "pydantic-1.9.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c556695b699f648c58373b542534308922c46a1cda06ea47bc9ca45ef5b39ae6"}, + {file = "pydantic-1.9.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f947352c3434e8b937e3aa8f96f47bdfe6d92779e44bb3f41e4c213ba6a32145"}, + {file = "pydantic-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5e48ef4a8b8c066c4a31409d91d7ca372a774d0212da2787c0d32f8045b1e034"}, + {file = "pydantic-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:96f240bce182ca7fe045c76bcebfa0b0534a1bf402ed05914a6f1dadff91877f"}, + {file = "pydantic-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:815ddebb2792efd4bba5488bc8fde09c29e8ca3227d27cf1c6990fc830fd292b"}, + {file = "pydantic-1.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6c5b77947b9e85a54848343928b597b4f74fc364b70926b3c4441ff52620640c"}, + {file = "pydantic-1.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c68c3bc88dbda2a6805e9a142ce84782d3930f8fdd9655430d8576315ad97ce"}, + {file = "pydantic-1.9.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a79330f8571faf71bf93667d3ee054609816f10a259a109a0738dac983b23c3"}, + {file = "pydantic-1.9.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f5a64b64ddf4c99fe201ac2724daada8595ada0d102ab96d019c1555c2d6441d"}, + {file = "pydantic-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a733965f1a2b4090a5238d40d983dcd78f3ecea221c7af1497b845a9709c1721"}, + {file = "pydantic-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cc6a4cb8a118ffec2ca5fcb47afbacb4f16d0ab8b7350ddea5e8ef7bcc53a16"}, + {file = "pydantic-1.9.0-py3-none-any.whl", hash = "sha256:085ca1de245782e9b46cefcf99deecc67d418737a1fd3f6a4f511344b613a5b3"}, + {file = "pydantic-1.9.0.tar.gz", hash = "sha256:742645059757a56ecd886faf4ed2441b9c0cd406079c2b4bee51bcc3fbcd510a"}, +] +pydot = [ + {file = "pydot-1.4.2-py2.py3-none-any.whl", hash = "sha256:66c98190c65b8d2e2382a441b4c0edfdb4f4c025ef9cb9874de478fb0793a451"}, + {file = "pydot-1.4.2.tar.gz", hash = "sha256:248081a39bcb56784deb018977e428605c1c758f10897a339fce1dd728ff007d"}, +] +pynacl = [ + {file = "PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858"}, + {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b"}, + {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff"}, + {file = "PyNaCl-1.5.0-cp36-abi3-win32.whl", hash = "sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543"}, + {file = "PyNaCl-1.5.0-cp36-abi3-win_amd64.whl", hash = "sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93"}, + {file = "PyNaCl-1.5.0.tar.gz", hash = "sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba"}, +] +pyparsing = [ + {file = "pyparsing-3.0.8-py3-none-any.whl", hash = "sha256:ef7b523f6356f763771559412c0d7134753f037822dad1b16945b7b846f7ad06"}, + {file = "pyparsing-3.0.8.tar.gz", hash = "sha256:7bf433498c016c4314268d95df76c81b842a4cb2b276fa3312cfb1e1d85f6954"}, +] +pyperclip = [ + {file = "pyperclip-1.8.2.tar.gz", hash = "sha256:105254a8b04934f0bc84e9c24eb360a591aaf6535c9def5f29d92af107a9bf57"}, +] +pyreadline3 = [ + {file = "pyreadline3-3.4.1-py3-none-any.whl", hash = "sha256:b0efb6516fd4fb07b45949053826a62fa4cb353db5be2bbb4a7aa1fdd1e345fb"}, + {file = "pyreadline3-3.4.1.tar.gz", hash = "sha256:6f3d1f7b8a31ba32b73917cefc1f28cc660562f39aea8646d30bd6eff21f7bae"}, +] +pysam = [ + {file = "pysam-0.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:44de1a3af7c7eb5f404d6337f0c9c4ee88c34c2d2fee1a7896ccd8e7d2aa475a"}, + {file = "pysam-0.19.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0ceb07c6253598ec70fef6ac0c0f7ab0d299562c1a91e737adb07239afba22d6"}, + {file = "pysam-0.19.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2057f3b8cc20562fd010e7971e83ab78978f17975563a711c94bca583ce8a2d3"}, + {file = "pysam-0.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a1a9ee6cd6dfa50973dcb51cd2245ea7d4d64d4e962d60e5e1a088f7b790e3e"}, + {file = "pysam-0.19.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b831170ff810bfd1242dbce4ddf8e693e95e39a4332d5903d416233d3d1be50a"}, + {file = "pysam-0.19.0-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fa4a5d334986d0696522246820f295cbf6c18dc1b78798f800a2d57d56207789"}, + {file = "pysam-0.19.0-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ab58d7d9b140e2e8a4918fc00661aa901ba461d9bccd4b499102b0828f2d897e"}, + {file = "pysam-0.19.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1d964c29fedf55d2a5d095227d19d915c2e0e5e42b1e0bdf7fab30cd1d2a850"}, + {file = "pysam-0.19.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8c78f9b84f7fe69530eaccf5b7f08636b3419f0017b5050aa7013f1c59d3d654"}, + {file = "pysam-0.19.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ada92b376717ac4c9c9924a096af9186035115d29a113eaa997d1c020ce421b9"}, + {file = "pysam-0.19.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:80b8f5b929c7f660b6e1497790a13c61f386b310a31ca54ad6ba110674d11c55"}, + {file = "pysam-0.19.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30017bed8d002d41f83fa7e10569525c811a8e3860d73a880ee653ef29fd4fbc"}, + {file = "pysam-0.19.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5e2e465522ba2c34cb96c013a28f9c9db303f8ab1350b4c11cca73677f1e9594"}, + {file = "pysam-0.19.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2103b4b6d9b0cc0b4aaccf64e87a92bfdabb7dc92810cf84be35ffe78fafa002"}, + {file = "pysam-0.19.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:eaf342b9c71ed83a63237df2000e3bc1f0236165d48fd7240c4c78b66f28d63b"}, + {file = "pysam-0.19.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc0021b154206edfbaa13515cb67523c76c576b7a670e72a793f2da4f03139f4"}, + {file = "pysam-0.19.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3101e0fcd2f6accbfa72a554a71baf83f1c096bb0f9045059b3ead35901ce128"}, + {file = "pysam-0.19.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5118dcabac574638df43739941f0ee20cc4e9197aee4a8f10f195542a13f18e3"}, + {file = "pysam-0.19.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4fe1f1fae0e559d3412625dc7d4d08b477e80211b3fe5b267ba341a84f78b8be"}, + {file = "pysam-0.19.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2911e3760dd2b709c686f075146787b8bda35629093352c28a7e52ac00ddaffc"}, + {file = "pysam-0.19.0.tar.gz", hash = "sha256:dcc052566f9509fd93b2a2664f094a13f016fd60cdd189e05fb4eafa0c89505b"}, +] +pyserde = [ + {file = "pyserde-0.6.0-py3-none-any.whl", hash = "sha256:89775789b20b9e03ee773502a619361063613c284762b77c5d27cfa000f3a94c"}, + {file = "pyserde-0.6.0.tar.gz", hash = "sha256:428fdb97824626e83f962c5b26ef67a3880fe11c5ab722f731fce481142d28b4"}, +] +python-cinderclient = [ + {file = "python-cinderclient-8.3.0.tar.gz", hash = "sha256:e00103875029dc85cbb59131d00ccc8534f692956acde32b5a3cc5af4c24580b"}, + {file = "python_cinderclient-8.3.0-py3-none-any.whl", hash = "sha256:a19ce34f8efad8eabe874494295e6f8251e4b5f02a4c3d648c522259f3b5ae0a"}, +] +python-dateutil = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] +python-keystoneclient = [ + {file = "python-keystoneclient-4.4.0.tar.gz", hash = "sha256:fc17ca9a1aa493104b496ba347f12507f271b5b6e819f4de4aef6574918aa071"}, + {file = "python_keystoneclient-4.4.0-py3-none-any.whl", hash = "sha256:1a9f30bfa3a5bf004159736d8cec91f8ce47301a0e5ad976b9cd064f5de39295"}, +] +python-novaclient = [ + {file = "python-novaclient-17.7.0.tar.gz", hash = "sha256:4ebc27f4ce06c155b8e991a44463b9358596e6856cf171c9af8dc7568d868bed"}, + {file = "python_novaclient-17.7.0-py3-none-any.whl", hash = "sha256:7f3cbe33a65f8fc3be4d34b29c09c16f50faa5465fd78fc9615b03c1880a8e6b"}, +] +python-openstackclient = [ + {file = "python-openstackclient-5.8.0.tar.gz", hash = "sha256:334852df8897b95f0581ec12ee287de8c7a9289a208a18f0a8b38777019fd986"}, + {file = "python_openstackclient-5.8.0-py3-none-any.whl", hash = "sha256:93054487ad1235c3f413f50aef4f7981b532c81d7e9f1d9ea4dd2f28705621db"}, +] +pytz = [ + {file = "pytz-2022.1-py2.py3-none-any.whl", hash = "sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c"}, + {file = "pytz-2022.1.tar.gz", hash = "sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7"}, +] +pyyaml = [ + {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, + {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, + {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, + {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, + {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, + {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, + {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, + {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, + {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, + {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, + {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, + {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, + {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, + {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, + {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, + {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, +] +rdflib = [ + {file = "rdflib-6.1.1-py3-none-any.whl", hash = "sha256:fc81cef513cd552d471f2926141396b633207109d0154c8e77926222c70367fe"}, + {file = "rdflib-6.1.1.tar.gz", hash = "sha256:8dbfa0af2990b98471dacbc936d6494c997ede92fd8ed693fb84ee700ef6f754"}, +] +"repoze.lru" = [ + {file = "repoze.lru-0.7-py3-none-any.whl", hash = "sha256:f77bf0e1096ea445beadd35f3479c5cff2aa1efe604a133e67150bc8630a62ea"}, + {file = "repoze.lru-0.7.tar.gz", hash = "sha256:0429a75e19380e4ed50c0694e26ac8819b4ea7851ee1fc7583c8572db80aff77"}, +] +requests = [ + {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"}, + {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"}, +] +requests-toolbelt = [ + {file = "requests-toolbelt-0.9.1.tar.gz", hash = "sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0"}, + {file = "requests_toolbelt-0.9.1-py2.py3-none-any.whl", hash = "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f"}, +] +requestsexceptions = [ + {file = "requestsexceptions-1.4.0-py2.py3-none-any.whl", hash = "sha256:3083d872b6e07dc5c323563ef37671d992214ad9a32b0ca4a3d7f5500bf38ce3"}, + {file = "requestsexceptions-1.4.0.tar.gz", hash = "sha256:b095cbc77618f066d459a02b137b020c37da9f46d9b057704019c9f77dba3065"}, +] +resolvelib = [ + {file = "resolvelib-0.5.5-py2.py3-none-any.whl", hash = "sha256:b0143b9d074550a6c5163a0f587e49c49017434e3cdfe853941725f5455dd29c"}, + {file = "resolvelib-0.5.5.tar.gz", hash = "sha256:123de56548c90df85137425a3f51eb93df89e2ba719aeb6a8023c032758be950"}, +] +rfc3986 = [ + {file = "rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd"}, + {file = "rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c"}, +] +routes = [ + {file = "Routes-2.5.1-py2.py3-none-any.whl", hash = "sha256:fab5a042a3a87778eb271d053ca2723cadf43c95b471532a191a48539cb606ea"}, + {file = "Routes-2.5.1.tar.gz", hash = "sha256:b6346459a15f0cbab01a45a90c3d25caf980d4733d628b4cc1952b865125d053"}, +] +"ruamel.yaml" = [ + {file = "ruamel.yaml-0.17.21-py3-none-any.whl", hash = "sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7"}, + {file = "ruamel.yaml-0.17.21.tar.gz", hash = "sha256:8b7ce697a2f212752a35c1ac414471dc16c424c9573be4926b56ff3f5d23b7af"}, +] +"ruamel.yaml.clib" = [ + {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6e7be2c5bcb297f5b82fee9c665eb2eb7001d1050deaba8471842979293a80b0"}, + {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:221eca6f35076c6ae472a531afa1c223b9c29377e62936f61bc8e6e8bdc5f9e7"}, + {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-win32.whl", hash = "sha256:1070ba9dd7f9370d0513d649420c3b362ac2d687fe78c6e888f5b12bf8bc7bee"}, + {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:77df077d32921ad46f34816a9a16e6356d8100374579bc35e15bab5d4e9377de"}, + {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:cfdb9389d888c5b74af297e51ce357b800dd844898af9d4a547ffc143fa56751"}, + {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7b2927e92feb51d830f531de4ccb11b320255ee95e791022555971c466af4527"}, + {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-win32.whl", hash = "sha256:ada3f400d9923a190ea8b59c8f60680c4ef8a4b0dfae134d2f2ff68429adfab5"}, + {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-win_amd64.whl", hash = "sha256:de9c6b8a1ba52919ae919f3ae96abb72b994dd0350226e28f3686cb4f142165c"}, + {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d67f273097c368265a7b81e152e07fb90ed395df6e552b9fa858c6d2c9f42502"}, + {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:72a2b8b2ff0a627496aad76f37a652bcef400fd861721744201ef1b45199ab78"}, + {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-win32.whl", hash = "sha256:9efef4aab5353387b07f6b22ace0867032b900d8e91674b5d8ea9150db5cae94"}, + {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-win_amd64.whl", hash = "sha256:846fc8336443106fe23f9b6d6b8c14a53d38cef9a375149d61f99d78782ea468"}, + {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0847201b767447fc33b9c235780d3aa90357d20dd6108b92be544427bea197dd"}, + {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:78988ed190206672da0f5d50c61afef8f67daa718d614377dcd5e3ed85ab4a99"}, + {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-win32.whl", hash = "sha256:a49e0161897901d1ac9c4a79984b8410f450565bbad64dbfcbf76152743a0cdb"}, + {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-win_amd64.whl", hash = "sha256:bf75d28fa071645c529b5474a550a44686821decebdd00e21127ef1fd566eabe"}, + {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a32f8d81ea0c6173ab1b3da956869114cae53ba1e9f72374032e33ba3118c233"}, + {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7f7ecb53ae6848f959db6ae93bdff1740e651809780822270eab111500842a84"}, + {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-win32.whl", hash = "sha256:89221ec6d6026f8ae859c09b9718799fea22c0e8da8b766b0b2c9a9ba2db326b"}, + {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-win_amd64.whl", hash = "sha256:31ea73e564a7b5fbbe8188ab8b334393e06d997914a4e184975348f204790277"}, + {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dc6a613d6c74eef5a14a214d433d06291526145431c3b964f5e16529b1842bed"}, + {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:1866cf2c284a03b9524a5cc00daca56d80057c5ce3cdc86a52020f4c720856f0"}, + {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-win32.whl", hash = "sha256:3fb9575a5acd13031c57a62cc7823e5d2ff8bc3835ba4d94b921b4e6ee664104"}, + {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-win_amd64.whl", hash = "sha256:825d5fccef6da42f3c8eccd4281af399f21c02b32d98e113dbc631ea6a6ecbc7"}, + {file = "ruamel.yaml.clib-0.2.6.tar.gz", hash = "sha256:4ff604ce439abb20794f05613c374759ce10e3595d1867764dd1ae675b85acbd"}, +] +s3transfer = [ + {file = "s3transfer-0.6.0-py3-none-any.whl", hash = "sha256:06176b74f3a15f61f1b4f25a1fc29a4429040b7647133a463da8fa5bd28d5ecd"}, + {file = "s3transfer-0.6.0.tar.gz", hash = "sha256:2ed07d3866f523cc561bf4a00fc5535827981b117dd7876f036b0c1aca42c947"}, +] +schema-salad = [ + {file = "schema-salad-8.2.20220204150214.tar.gz", hash = "sha256:3e53dbfe7137796b9e5135920e96bb2713ded9e7be2859dae554d1dc8b029704"}, + {file = "schema_salad-8.2.20220204150214-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:609ba090fb40f493cedf64bb124c1d208af7388b6663af9d76a91a03ec7d8710"}, + {file = "schema_salad-8.2.20220204150214-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecf2c386cff04f7bc08b0ec4f3f3f0c6196043d746799c64a98ca4cb596e4c9f"}, + {file = "schema_salad-8.2.20220204150214-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8ec4ad0f1fd56637ad3f935d647adceaebc28bbe7c204cf24165c231320c09b8"}, + {file = "schema_salad-8.2.20220204150214-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77ff2a0d76e490f5c39375ca4d3f625fffa51a93aa719966a5c06b5375f76fc8"}, + {file = "schema_salad-8.2.20220204150214-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86addbf36269646914c666035318ca38a5e84a4ae5732c013cde3f03550ca5f7"}, + {file = "schema_salad-8.2.20220204150214-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b72181040f4330decdad422130fac6d7953d0415843a6bb02d6e7835f20e10e7"}, + {file = "schema_salad-8.2.20220204150214-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b27300acf4d71547dae3c0f065789075c157391de74e71fdbf1daf318ef958dc"}, + {file = "schema_salad-8.2.20220204150214-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd1f72602c21fb4e201cecba36c60f896f0cc95edaeb49bb9b720ed9869af372"}, + {file = "schema_salad-8.2.20220204150214-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2de190abaccd39408e384982c0bbb871708502ebb81682cf0a6e522bcdf86dc3"}, + {file = "schema_salad-8.2.20220204150214-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c8d4b433f745e3c51fa8ea83ab319c45269af3003cb180d53e6eb719c1f8606"}, + {file = "schema_salad-8.2.20220204150214-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4976cbb5c4801b29295bdadb3e234e7b9572b1b9716b063f79806cb30820181"}, + {file = "schema_salad-8.2.20220204150214-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1627e1666e8f835d337926fac65d5484128ce5798fefa4bb1a0ae862e1f9ec82"}, + {file = "schema_salad-8.2.20220204150214-py3-none-any.whl", hash = "sha256:ce49adf0bc97fd44d99df98bf00d6bc66b38a2483b3cc721cbd200d010add3c5"}, +] +shellescape = [ + {file = "shellescape-3.8.1-py2.py3-none-any.whl", hash = "sha256:f17127e390fa3f9aaa80c69c16ea73615fd9b5318fd8309c1dca6168ae7d85bf"}, + {file = "shellescape-3.8.1.tar.gz", hash = "sha256:40b310b30479be771bf3ab28bd8d40753778488bd46ea0969ba0b35038c3ec26"}, +] +simplejson = [ + {file = "simplejson-3.17.6-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a89acae02b2975b1f8e4974cb8cdf9bf9f6c91162fb8dec50c259ce700f2770a"}, + {file = "simplejson-3.17.6-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:82ff356ff91be0ab2293fc6d8d262451eb6ac4fd999244c4b5f863e049ba219c"}, + {file = "simplejson-3.17.6-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:0de783e9c2b87bdd75b57efa2b6260c24b94605b5c9843517577d40ee0c3cc8a"}, + {file = "simplejson-3.17.6-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:d24a9e61df7a7787b338a58abfba975414937b609eb6b18973e25f573bc0eeeb"}, + {file = "simplejson-3.17.6-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:e8603e691580487f11306ecb066c76f1f4a8b54fb3bdb23fa40643a059509366"}, + {file = "simplejson-3.17.6-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:9b01e7b00654115965a206e3015f0166674ec1e575198a62a977355597c0bef5"}, + {file = "simplejson-3.17.6-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:37bc0cf0e5599f36072077e56e248f3336917ded1d33d2688624d8ed3cefd7d2"}, + {file = "simplejson-3.17.6-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:cf6e7d5fe2aeb54898df18db1baf479863eae581cce05410f61f6b4188c8ada1"}, + {file = "simplejson-3.17.6-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:bdfc54b4468ed4cd7415928cbe782f4d782722a81aeb0f81e2ddca9932632211"}, + {file = "simplejson-3.17.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:dd16302d39c4d6f4afde80edd0c97d4db643327d355a312762ccd9bd2ca515ed"}, + {file = "simplejson-3.17.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:deac4bdafa19bbb89edfb73b19f7f69a52d0b5bd3bb0c4ad404c1bbfd7b4b7fd"}, + {file = "simplejson-3.17.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a8bbdb166e2fb816e43ab034c865147edafe28e1b19c72433147789ac83e2dda"}, + {file = "simplejson-3.17.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7854326920d41c3b5d468154318fe6ba4390cb2410480976787c640707e0180"}, + {file = "simplejson-3.17.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:04e31fa6ac8e326480703fb6ded1488bfa6f1d3f760d32e29dbf66d0838982ce"}, + {file = "simplejson-3.17.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f63600ec06982cdf480899026f4fda622776f5fabed9a869fdb32d72bc17e99a"}, + {file = "simplejson-3.17.6-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e03c3b8cc7883a54c3f34a6a135c4a17bc9088a33f36796acdb47162791b02f6"}, + {file = "simplejson-3.17.6-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a2d30d6c1652140181dc6861f564449ad71a45e4f165a6868c27d36745b65d40"}, + {file = "simplejson-3.17.6-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a1aa6e4cae8e3b8d5321be4f51c5ce77188faf7baa9fe1e78611f93a8eed2882"}, + {file = "simplejson-3.17.6-cp310-cp310-win32.whl", hash = "sha256:97202f939c3ff341fc3fa84d15db86156b1edc669424ba20b0a1fcd4a796a045"}, + {file = "simplejson-3.17.6-cp310-cp310-win_amd64.whl", hash = "sha256:80d3bc9944be1d73e5b1726c3bbfd2628d3d7fe2880711b1eb90b617b9b8ac70"}, + {file = "simplejson-3.17.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9fa621b3c0c05d965882c920347b6593751b7ab20d8fa81e426f1735ca1a9fc7"}, + {file = "simplejson-3.17.6-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd2fb11922f58df8528adfca123f6a84748ad17d066007e7ac977720063556bd"}, + {file = "simplejson-3.17.6-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:724c1fe135aa437d5126138d977004d165a3b5e2ee98fc4eb3e7c0ef645e7e27"}, + {file = "simplejson-3.17.6-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4ff4ac6ff3aa8f814ac0f50bf218a2e1a434a17aafad4f0400a57a8cc62ef17f"}, + {file = "simplejson-3.17.6-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:67093a526e42981fdd954868062e56c9b67fdd7e712616cc3265ad0c210ecb51"}, + {file = "simplejson-3.17.6-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:5d6b4af7ad7e4ac515bc6e602e7b79e2204e25dbd10ab3aa2beef3c5a9cad2c7"}, + {file = "simplejson-3.17.6-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:1c9b1ed7ed282b36571638297525f8ef80f34b3e2d600a56f962c6044f24200d"}, + {file = "simplejson-3.17.6-cp36-cp36m-win32.whl", hash = "sha256:632ecbbd2228575e6860c9e49ea3cc5423764d5aa70b92acc4e74096fb434044"}, + {file = "simplejson-3.17.6-cp36-cp36m-win_amd64.whl", hash = "sha256:4c09868ddb86bf79b1feb4e3e7e4a35cd6e61ddb3452b54e20cf296313622566"}, + {file = "simplejson-3.17.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4b6bd8144f15a491c662f06814bd8eaa54b17f26095bb775411f39bacaf66837"}, + {file = "simplejson-3.17.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5decdc78849617917c206b01e9fc1d694fd58caa961be816cb37d3150d613d9a"}, + {file = "simplejson-3.17.6-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:521877c7bd060470806eb6335926e27453d740ac1958eaf0d8c00911bc5e1802"}, + {file = "simplejson-3.17.6-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:65b998193bd7b0c7ecdfffbc825d808eac66279313cb67d8892bb259c9d91494"}, + {file = "simplejson-3.17.6-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ac786f6cb7aa10d44e9641c7a7d16d7f6e095b138795cd43503769d4154e0dc2"}, + {file = "simplejson-3.17.6-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:3ff5b3464e1ce86a8de8c88e61d4836927d5595c2162cab22e96ff551b916e81"}, + {file = "simplejson-3.17.6-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:69bd56b1d257a91e763256d63606937ae4eb890b18a789b66951c00062afec33"}, + {file = "simplejson-3.17.6-cp37-cp37m-win32.whl", hash = "sha256:b81076552d34c27e5149a40187a8f7e2abb2d3185576a317aaf14aeeedad862a"}, + {file = "simplejson-3.17.6-cp37-cp37m-win_amd64.whl", hash = "sha256:07ecaafc1b1501f275bf5acdee34a4ad33c7c24ede287183ea77a02dc071e0c0"}, + {file = "simplejson-3.17.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:068670af975247acbb9fc3d5393293368cda17026db467bf7a51548ee8f17ee1"}, + {file = "simplejson-3.17.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4d1c135af0c72cb28dd259cf7ba218338f4dc027061262e46fe058b4e6a4c6a3"}, + {file = "simplejson-3.17.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:23fe704da910ff45e72543cbba152821685a889cf00fc58d5c8ee96a9bad5f94"}, + {file = "simplejson-3.17.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f444762fed1bc1fd75187ef14a20ed900c1fbb245d45be9e834b822a0223bc81"}, + {file = "simplejson-3.17.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:681eb4d37c9a9a6eb9b3245a5e89d7f7b2b9895590bb08a20aa598c1eb0a1d9d"}, + {file = "simplejson-3.17.6-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8e8607d8f6b4f9d46fee11447e334d6ab50e993dd4dbfb22f674616ce20907ab"}, + {file = "simplejson-3.17.6-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b10556817f09d46d420edd982dd0653940b90151d0576f09143a8e773459f6fe"}, + {file = "simplejson-3.17.6-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e1ec8a9ee0987d4524ffd6299e778c16cc35fef6d1a2764e609f90962f0b293a"}, + {file = "simplejson-3.17.6-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0b4126cac7d69ac06ff22efd3e0b3328a4a70624fcd6bca4fc1b4e6d9e2e12bf"}, + {file = "simplejson-3.17.6-cp38-cp38-win32.whl", hash = "sha256:35a49ebef25f1ebdef54262e54ae80904d8692367a9f208cdfbc38dbf649e00a"}, + {file = "simplejson-3.17.6-cp38-cp38-win_amd64.whl", hash = "sha256:743cd768affaa508a21499f4858c5b824ffa2e1394ed94eb85caf47ac0732198"}, + {file = "simplejson-3.17.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:fb62d517a516128bacf08cb6a86ecd39fb06d08e7c4980251f5d5601d29989ba"}, + {file = "simplejson-3.17.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:12133863178a8080a3dccbf5cb2edfab0001bc41e5d6d2446af2a1131105adfe"}, + {file = "simplejson-3.17.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5540fba2d437edaf4aa4fbb80f43f42a8334206ad1ad3b27aef577fd989f20d9"}, + {file = "simplejson-3.17.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d74ee72b5071818a1a5dab47338e87f08a738cb938a3b0653b9e4d959ddd1fd9"}, + {file = "simplejson-3.17.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:28221620f4dcabdeac310846629b976e599a13f59abb21616356a85231ebd6ad"}, + {file = "simplejson-3.17.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b09bc62e5193e31d7f9876220fb429ec13a6a181a24d897b9edfbbdbcd678851"}, + {file = "simplejson-3.17.6-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7255a37ff50593c9b2f1afa8fafd6ef5763213c1ed5a9e2c6f5b9cc925ab979f"}, + {file = "simplejson-3.17.6-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:401d40969cee3df7bda211e57b903a534561b77a7ade0dd622a8d1a31eaa8ba7"}, + {file = "simplejson-3.17.6-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a649d0f66029c7eb67042b15374bd93a26aae202591d9afd71e111dd0006b198"}, + {file = "simplejson-3.17.6-cp39-cp39-win32.whl", hash = "sha256:522fad7be85de57430d6d287c4b635813932946ebf41b913fe7e880d154ade2e"}, + {file = "simplejson-3.17.6-cp39-cp39-win_amd64.whl", hash = "sha256:3fe87570168b2ae018391e2b43fbf66e8593a86feccb4b0500d134c998983ccc"}, + {file = "simplejson-3.17.6.tar.gz", hash = "sha256:cf98038d2abf63a1ada5730e91e84c642ba6c225b0198c3684151b1f80c5f8a6"}, +] +six = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] +sortedcontainers = [ + {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"}, + {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, +] +soupsieve = [ + {file = "soupsieve-2.3.2.post1-py3-none-any.whl", hash = "sha256:3b2503d3c7084a42b1ebd08116e5f81aadfaea95863628c80a3b774a11b7c759"}, + {file = "soupsieve-2.3.2.post1.tar.gz", hash = "sha256:fc53893b3da2c33de295667a0e19f078c14bf86544af307354de5fcf12a3f30d"}, +] +stevedore = [ + {file = "stevedore-3.5.0-py3-none-any.whl", hash = "sha256:a547de73308fd7e90075bb4d301405bebf705292fa90a90fc3bcf9133f58616c"}, + {file = "stevedore-3.5.0.tar.gz", hash = "sha256:f40253887d8712eaa2bb0ea3830374416736dc8ec0e22f5a65092c1174c44335"}, +] +stringcase = [ + {file = "stringcase-1.2.0.tar.gz", hash = "sha256:48a06980661908efe8d9d34eab2b6c13aefa2163b3ced26972902e3bdfd87008"}, +] +tabulate = [ + {file = "tabulate-0.8.9-py3-none-any.whl", hash = "sha256:d7c013fe7abbc5e491394e10fa845f8f32fe54f8dc60c6622c6cf482d25d47e4"}, + {file = "tabulate-0.8.9.tar.gz", hash = "sha256:eb1d13f25760052e8931f2ef80aaf6045a6cceb47514db8beab24cded16f13a7"}, +] +tinydb = [] +tomli = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] +tuspy = [] +types-cryptography = [ + {file = "types-cryptography-3.3.21.tar.gz", hash = "sha256:ad1b9c63159c009f8676c7e41a4d595dfb96e8c03affa2e693e1617908bb409e"}, + {file = "types_cryptography-3.3.21-py3-none-any.whl", hash = "sha256:bdeb6dd07280ac724e05f02e0d8ef01fdef729b18bb07d635d64de83171a4e70"}, +] +types-paramiko = [ + {file = "types-paramiko-2.10.0.tar.gz", hash = "sha256:ab6893d5fce5ed06964d61939ed6a7168ab14952945a90995a628a5e82a5e161"}, + {file = "types_paramiko-2.10.0-py3-none-any.whl", hash = "sha256:f558bb26ff3b085f7282125559927ebf9e1c08410cd4aac5766addbf4d15c8bc"}, +] +types-requests = [ + {file = "types-requests-2.27.25.tar.gz", hash = "sha256:805ae7e38fd9d157153066dc4381cf585fd34dfa212f2fc1fece248c05aac571"}, + {file = "types_requests-2.27.25-py3-none-any.whl", hash = "sha256:2444905c89731dbcb6bbcd6d873a04252445df7623917c640e463b2b28d2a708"}, +] +types-urllib3 = [ + {file = "types-urllib3-1.26.14.tar.gz", hash = "sha256:2a2578e4b36341ccd240b00fccda9826988ff0589a44ba4a664bbd69ef348d27"}, + {file = "types_urllib3-1.26.14-py3-none-any.whl", hash = "sha256:5d2388aa76395b1e3999ff789ea5b3283677dad8e9bcf3d9117ba19271fd35d9"}, +] +typing-extensions = [ + {file = "typing_extensions-4.2.0-py3-none-any.whl", hash = "sha256:6657594ee297170d19f67d55c05852a874e7eb634f4f753dbd667855e07c1708"}, + {file = "typing_extensions-4.2.0.tar.gz", hash = "sha256:f1c24655a0da0d1b67f07e17a5e6b2a105894e6824b92096378bb3668ef02376"}, +] +typing-inspect = [ + {file = "typing_inspect-0.7.1-py2-none-any.whl", hash = "sha256:b1f56c0783ef0f25fb064a01be6e5407e54cf4a4bf4f3ba3fe51e0bd6dcea9e5"}, + {file = "typing_inspect-0.7.1-py3-none-any.whl", hash = "sha256:3cd7d4563e997719a710a3bfe7ffb544c6b72069b6812a02e9b414a8fa3aaa6b"}, + {file = "typing_inspect-0.7.1.tar.gz", hash = "sha256:047d4097d9b17f46531bf6f014356111a1b6fb821a24fe7ac909853ca2a782aa"}, +] +urllib3 = [ + {file = "urllib3-1.26.9-py2.py3-none-any.whl", hash = "sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14"}, + {file = "urllib3-1.26.9.tar.gz", hash = "sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"}, +] +virtualenv = [ + {file = "virtualenv-20.14.1-py2.py3-none-any.whl", hash = "sha256:e617f16e25b42eb4f6e74096b9c9e37713cf10bf30168fb4a739f3fa8f898a3a"}, + {file = "virtualenv-20.14.1.tar.gz", hash = "sha256:ef589a79795589aada0c1c5b319486797c03b67ac3984c48c669c0e4f50df3a5"}, +] +wcwidth = [ + {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, + {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, +] +webencodings = [ + {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, + {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, +] +wrapt = [ + {file = "wrapt-1.14.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3"}, + {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef"}, + {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28"}, + {file = "wrapt-1.14.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59"}, + {file = "wrapt-1.14.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87"}, + {file = "wrapt-1.14.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1"}, + {file = "wrapt-1.14.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b"}, + {file = "wrapt-1.14.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462"}, + {file = "wrapt-1.14.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1"}, + {file = "wrapt-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320"}, + {file = "wrapt-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2"}, + {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4"}, + {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069"}, + {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310"}, + {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f"}, + {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656"}, + {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c"}, + {file = "wrapt-1.14.1-cp310-cp310-win32.whl", hash = "sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8"}, + {file = "wrapt-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164"}, + {file = "wrapt-1.14.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907"}, + {file = "wrapt-1.14.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3"}, + {file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3"}, + {file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d"}, + {file = "wrapt-1.14.1-cp35-cp35m-win32.whl", hash = "sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7"}, + {file = "wrapt-1.14.1-cp35-cp35m-win_amd64.whl", hash = "sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00"}, + {file = "wrapt-1.14.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4"}, + {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1"}, + {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1"}, + {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff"}, + {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d"}, + {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1"}, + {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569"}, + {file = "wrapt-1.14.1-cp36-cp36m-win32.whl", hash = "sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed"}, + {file = "wrapt-1.14.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471"}, + {file = "wrapt-1.14.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248"}, + {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68"}, + {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d"}, + {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77"}, + {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7"}, + {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015"}, + {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a"}, + {file = "wrapt-1.14.1-cp37-cp37m-win32.whl", hash = "sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853"}, + {file = "wrapt-1.14.1-cp37-cp37m-win_amd64.whl", hash = "sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c"}, + {file = "wrapt-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456"}, + {file = "wrapt-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f"}, + {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc"}, + {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1"}, + {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af"}, + {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b"}, + {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0"}, + {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57"}, + {file = "wrapt-1.14.1-cp38-cp38-win32.whl", hash = "sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5"}, + {file = "wrapt-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d"}, + {file = "wrapt-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383"}, + {file = "wrapt-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7"}, + {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86"}, + {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735"}, + {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b"}, + {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3"}, + {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3"}, + {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe"}, + {file = "wrapt-1.14.1-cp39-cp39-win32.whl", hash = "sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5"}, + {file = "wrapt-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb"}, + {file = "wrapt-1.14.1.tar.gz", hash = "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d"}, +] +yarl = [] +zipstream-new = [ + {file = "zipstream-new-1.1.8.tar.gz", hash = "sha256:b031fe181b94e51678389d26b174bc76382605a078d7d5d8f5beae083f111c76"}, + {file = "zipstream_new-1.1.8-py3-none-any.whl", hash = "sha256:0662eb3ebe764fa168a5883cd8819ef83b94bd9e39955537188459d2264a7f60"}, +] diff --git a/prepare_galaxy.yml b/prepare_galaxy.yml deleted file mode 100644 index d54da18a..00000000 --- a/prepare_galaxy.yml +++ /dev/null @@ -1,50 +0,0 @@ -- hosts: all - become: yes - handlers: - - name: restart galaxy - supervisorctl: - name: galaxy - state: restarted - tasks: - - name: Copy pulsar_actions.yml - copy: - src: galaxy_files/pulsar_actions.yml - dest: "{{ galaxy_config_dir }}/pulsar_actions.yml" - owner: "{{ galaxy_user }}" - group: "{{ galaxy_user }}" - notify: restart galaxy - - name: Copy job_conf.xml - copy: - src: galaxy_files/job_conf.xml.tmp - dest: "{{ galaxy_config_dir }}/job_conf.xml" - owner: "{{ galaxy_user }}" - group: "{{ galaxy_user }}" - notify: restart galaxy - - name: Copy dynamic_destination.py - copy: - src: galaxy_files/dynamic_destination.py - dest: "{{ galaxy_root_path }}/server/lib/galaxy/jobs/rules/dynamic_destination.py" - owner: "{{ galaxy_user }}" - group: "{{ galaxy_user }}" - notify: restart galaxy - - name: Copy JobStatus-MetricPlugin - copy: - src: galaxy_files/job_status.py - dest: "{{ galaxy_root_path }}/server/lib/galaxy/jobs/metrics/instrumenters/job_status.py" - owner: "{{ galaxy_user }}" - group: "{{ galaxy_user }}" - notify: restart galaxy - - name: Copy StagingTime-MetricPlugin - copy: - src: galaxy_files/staging_time.py - dest: "{{ galaxy_root_path }}/server/lib/galaxy/jobs/metrics/instrumenters/staging_time.py" - owner: "{{ galaxy_user }}" - group: "{{ galaxy_user }}" - notify: restart galaxy - - name: Copy job_metrics_conf.yml - copy: - src: galaxy_files/job_metrics_conf.xml - dest: "{{ galaxy_config_dir }}/job_metrics_conf.xml" - owner: "{{ galaxy_user }}" - group: "{{ galaxy_user }}" - notify: restart galaxy diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..f8e1e492 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,46 @@ +[tool.poetry] +name = "galaxy_benchmarker" +version = "0.1.0" +description = "" +authors = [ + "Andreas Skorczyk ", + "Stefan Möhrle <26185187+smoehrle@users.noreply.github.com>" +] + +[tool.poetry.dependencies] +python = "^3.10" + +# Connectors/Bridges +ansible = "^5" +bioblend = { git = "https://github.com/galaxyproject/bioblend.git", branch = "main" } +paramiko = "^2" +planemo = "^0.74" +python-openstackclient = "^5" + +# File operations +## Templates +Jinja2 = "^3" +## Serialization/Deserialization +pyserde = "^0.6" +pyyaml = "^6" +boto3 = "^1.24.38" + +[tool.poetry.dev-dependencies] +isort = "^5" +black = "*" +types-requests = "^2" +types-paramiko = "^2" + +[build-system] +# Fix for editable installs +requires = ["poetry-core@https://github.com/python-poetry/poetry-core/archive/325312c016d69189ac93c945ba0c1b69296c5e54.zip"] +# requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.isort] +multi_line_output = 3 +include_trailing_comma = true +force_grid_wrap = 0 +use_parentheses = true +ensure_newline_before_comments = true +line_length = 88 diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 03a70e45..00000000 --- a/requirements.txt +++ /dev/null @@ -1,9 +0,0 @@ -urllib3 -planemo -pyyaml -bioblend -Jinja2 -influxdb -paramiko -ansible -python-openstackclient \ No newline at end of file diff --git a/scripts/entrypoint.sh b/scripts/entrypoint.sh new file mode 100755 index 00000000..e36fe21c --- /dev/null +++ b/scripts/entrypoint.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +echo "#############################" +echo "######## New run ########" +echo "#############################" + +if [ -d "/root/.ssh-host" ]; then + # Host ssh-config is mounted -> fix permissions + echo "Fixing permissions for ssh config" + + mkdir -p ~/.ssh + cp -r /root/.ssh-host/* ~/.ssh/ + chown -R $(id -u):$(id -g) ~/.ssh +fi + +# Send SIGNALS to child processes +trap 'echo entrypoint.sh: signal received, stoping child processes; kill -SIGINT $(jobs -p); wait;' SIGINT SIGTERM + +# Run benchmarker +python3 -m galaxy_benchmarker "$@" & + +wait \ No newline at end of file