From cf053ddbd2edf6a65c411429970173156fe67dc0 Mon Sep 17 00:00:00 2001 From: Joe Talerico Date: Fri, 2 Jun 2017 09:54:41 -0400 Subject: [PATCH] Browbeat Workloads - Rally/UPerf Adding linpack to Browbeat. Eventaully I will be moving all pbench workloads to this directory. Change-Id: If35a7952feaae19d5201ba171f0b1919137412dc --- ansible/install/browbeat.yml | 1 + ansible/install/group_vars/all.yml | 26 ++ .../install/roles/workloads/tasks/main.yml | 31 ++ .../workloads/templates/linpack-user.file | 24 ++ .../templates/pbench-uperf-user.file | 29 ++ conf/browbeat-workloads.yml | 83 ++++++ doc/source/installation.rst | 25 ++ rally/rally-plugins/workloads/linpack.sh | 25 ++ rally/rally-plugins/workloads/linpack.yml | 46 +++ .../rally-plugins/workloads/pbench-uperf.png | Bin 0 -> 54145 bytes rally/rally-plugins/workloads/pbench-uperf.py | 281 ++++++++++++++++++ .../rally-plugins/workloads/pbench-uperf.yml | 63 ++++ 12 files changed, 634 insertions(+) create mode 100644 ansible/install/roles/workloads/tasks/main.yml create mode 100644 ansible/install/roles/workloads/templates/linpack-user.file create mode 100644 ansible/install/roles/workloads/templates/pbench-uperf-user.file create mode 100644 conf/browbeat-workloads.yml create mode 100644 rally/rally-plugins/workloads/linpack.sh create mode 100644 rally/rally-plugins/workloads/linpack.yml create mode 100644 rally/rally-plugins/workloads/pbench-uperf.png create mode 100644 rally/rally-plugins/workloads/pbench-uperf.py create mode 100644 rally/rally-plugins/workloads/pbench-uperf.yml diff --git a/ansible/install/browbeat.yml b/ansible/install/browbeat.yml index a431a7f49..aa85fe22a 100644 --- a/ansible/install/browbeat.yml +++ b/ansible/install/browbeat.yml @@ -21,6 +21,7 @@ - shaker - flavors - images + - { role: workloads, when: install_browbeat_workloads} environment: "{{proxy_env}}" - hosts: compute diff --git a/ansible/install/group_vars/all.yml b/ansible/install/group_vars/all.yml index fef865958..75017c3f6 100644 --- a/ansible/install/group_vars/all.yml +++ b/ansible/install/group_vars/all.yml @@ -55,6 +55,32 @@ supported_distro: ((ansible_distribution == "CentOS" && ansible_distribution_maj # iptables file - RHEL (/etc/sysconfig/iptables) CentOS (/etc/sysconfig/iptables-config) iptables_file: /etc/sysconfig/iptables +######################################## +# Browbeat Workloads +######################################## + +# Install Browbeat workloads +install_browbeat_workloads: false + +# Network ID which has external access +browbeat_network: + +# For Pbench Repos - Provide the internal RPM URL +pbench_internal_url: + +# Browbeat Rally workloads +browbeat_workloads: + linpack: + name: browbeat-linpack + src: linpack-user.file + dest: "{{ browbeat_path }}/linpack-user.file" + image: centos7 + uperf: + name: browbeat-uperf + src: pbench-uperf-user.file + dest: "{{ browbeat_path }}/pbench-uperf-user.file" + image: centos7 + ######################################## # Other Install Configuration Items ######################################## diff --git a/ansible/install/roles/workloads/tasks/main.yml b/ansible/install/roles/workloads/tasks/main.yml new file mode 100644 index 000000000..440195ee9 --- /dev/null +++ b/ansible/install/roles/workloads/tasks/main.yml @@ -0,0 +1,31 @@ +--- + +- name: Check browbeat_network + fail: msg="browbeat_network needs to be set" + when: browbeat_network is not defined + +- name: Copy userdata files + template: + src: "{{ browbeat_workloads[item].src }}" + dest: "{{ browbeat_workloads[item].dest }}" + with_items: "{{ browbeat_workloads }}" + +- name: Build images + shell: source {{ overcloudrc }} ; openstack server create --wait --flavor m1.small --image {{ browbeat_workloads[item].image }} --nic net-id={{ browbeat_network }} --user-data {{ browbeat_workloads[item].dest }} {{ browbeat_workloads[item].name }} | egrep '\sid\s' | awk '{print $4}' + register: workload_ids + with_items: "{{ browbeat_workloads }}" + +- name: Check status of images + shell: source {{ overcloudrc }} ; nova console-log {{ item.stdout }} + register: guest_output + until: guest_output.stdout.find("Browbeat workload installed") != -1 + retries: 10 + with_items: "{{ workload_ids.results }}" + +- name: Copy prepared workload guest into Glance + shell: source {{ overcloudrc }} ; openstack server image create --wait --name {{ browbeat_workloads[item].name }} {{ browbeat_workloads[item].name }} + with_items: "{{ browbeat_workloads }}" + +- name: Update visibility + shell: source {{ overcloudrc }} ; openstack image set {{ browbeat_workloads[item].name }} --public + with_items: "{{ browbeat_workloads }}" diff --git a/ansible/install/roles/workloads/templates/linpack-user.file b/ansible/install/roles/workloads/templates/linpack-user.file new file mode 100644 index 000000000..83aae0706 --- /dev/null +++ b/ansible/install/roles/workloads/templates/linpack-user.file @@ -0,0 +1,24 @@ +#!/bin/bash +sudo echo "nameserver {{ dns_server }}" > /etc/resolv.conf +if [ $? -gt 0 ] +then + exit 1 +fi +sudo curl -O http://registrationcenter-download.intel.com/akdlm/irc_nas/9752/l_mklb_p_2017.3.018.tgz +sudo tar -xvzf l_mklb_p_2017.3.018.tgz +sudo mkdir /opt/linpack +sudo cp l_mklb_p_2017.3.018/benchmarks_2017/linux/mkl/benchmarks/linpack/* /opt/linpack +if [ $? -gt 0 ] +then + exit 1 +fi + +# Allow for root access +sudo sed -i 's/disable_root: 1/disable_root: 0/g' /etc/cloud/cloud.cfg +cat /etc/cloud/cloud.cfg | grep disable_root +if [ $? -gt 0 ] +then + exit 1 +fi + +echo "Browbeat workload installed" diff --git a/ansible/install/roles/workloads/templates/pbench-uperf-user.file b/ansible/install/roles/workloads/templates/pbench-uperf-user.file new file mode 100644 index 000000000..ea1f20ac3 --- /dev/null +++ b/ansible/install/roles/workloads/templates/pbench-uperf-user.file @@ -0,0 +1,29 @@ +#!/bin/bash +sudo echo "nameserver {{ dns_server }}" > /etc/resolv.conf +sudo wget -O /etc/yum.repos.d/pbench.repo "{{ pbench_internal_url }}" + +sudo cat << EOF >> /etc/yum.repos.d/pbench.repo +# Template file to be used with ansible playbook "pbench-repo.yml" + +########################################################################### +# External COPR repo +[copr-pbench] +name=Copr repo for pbench owned by ndokos +baseurl=https://copr-be.cloud.fedoraproject.org/results/ndokos/pbench/epel-7-x86_64/ +skip_if_unavailable=True +gpgcheck=1 +gpgkey=https://copr-be.cloud.fedoraproject.org/results/ndokos/pbench/pubkey.gpg +enabled=1 +enabled_metadata=1 +skip_if_unavailable=1 +EOF +cat /etc/yum.repos.d/pbench.repo +sudo yum clean all +sudo yum install -y pbench-agent +sudo yum install -y pbench-agent-internal +sudo yum install -y pbench-sysstat +sudo yum install -y pbench-uperf +sudo sed -i 's/disable_root: 1/disable_root: 0/g' /etc/cloud/cloud.cfg +cat /etc/cloud/cloud.cfg | grep disable_root + +echo "Browbeat workload installed" diff --git a/conf/browbeat-workloads.yml b/conf/browbeat-workloads.yml new file mode 100644 index 000000000..83e0a6725 --- /dev/null +++ b/conf/browbeat-workloads.yml @@ -0,0 +1,83 @@ +# Complete set of Workload Benchmarks +browbeat: + results : results/ + rerun: 1 + cloud_name: openstack +elasticsearch: + enabled: false + regather: false + host: 1.1.1.1 + port: 9200 + metadata_files: + - name: hardware-metadata + file: metadata/hardware-metadata.json + - name: environment-metadata + file: metadata/environment-metadata.json + - name: software-metadata + file: metadata/software-metadata.json + - name: version + file: metadata/version.json +ansible: + ssh_config: ansible/ssh-config + hosts: ansible/hosts + adjust: + keystone_token: ansible/browbeat/adjustment-keystone-token.yml + neutron_l3: ansible/browbeat/adjustment-l3.yml + nova_db: ansible/browbeat/adjustment-db.yml + workers: ansible/browbeat/adjustment-workers.yml + grafana_snapshot: ansible/browbeat/snapshot-general-performance-dashboard.yml + metadata: ansible/gather/site.yml +connmon: + enabled: false + sudo: true +grafana: + enabled: true + grafana_ip: 1.1.1.1 + grafana_port: 3000 + dashboards: + - openstack-general-system-performance + snapshot: + enabled: false + snapshot_compute: false +rally: + enabled: true + sleep_before: 5 + sleep_after: 5 + venv: /home/stack/rally-venv/bin/activate + plugins: + - workloads: rally/rally-plugins/workloads + benchmarks: + - name: workloads + enabled: true + concurrency: + - 1 + times: 1 + scenarios: + - name: linpack + enabled: true + image_name: browbeat-linpack + external_network: + net_id: + flavor_name: m1.small + file: rally/rally-plugins/workloads/linpack.yml + - name: uperf + enabled: true + user: root + password: None + image_name: browbeat-uperf + external_network: + net_id: + flavor_name: m1.small + test_name: test-uperf + protocols: 'tcp' + send_results: true + num_pairs: 1 + instances: 1 + samples: 1 + test_types: 'stream' + message_sizes: '64' + cloudname: test-cloud + elastic_host: + elastic_port: 9200 + file: rally/rally-plugins/workloads/pbench-uperf.yml + diff --git a/doc/source/installation.rst b/doc/source/installation.rst index 970e73aed..b64e8a568 100644 --- a/doc/source/installation.rst +++ b/doc/source/installation.rst @@ -56,6 +56,30 @@ On the Undercloud internet. Some useful documentation can be found at: https://access.redhat.com/documentation/en/red-hat-openstack-platform/11/single/networking-guide/ +(Optional) Install Browbeat instance workloads +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Browbeat instance workloads are orchestrated Rally plugins that ship with Browbeat. +We currently support a handful of workloads + +- Pbench-Uperf - Networking throughput / RR test +- Linpack - Microbenchmark for CPU load + +To enable installation of the Browbeat workloads set install_browbeat_workloads: true in +ansible/install/group_vars/all.yml. + +It is also required to provide the neutron network id of a private network which +has external access. To set this, edit ansible/install/group_vars/all.yml and +provide the network id for the browbeat_network: + +This work can either be done prior to installation of Browbeat, or after Browbeat +has been installed. To skip directly to this task execute: + +:: + + $ ansible-playbook -i hosts install/browbeat.yml --start-at-task "Check browbeat_network" + ... + + (Optional) Install Collectd ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -550,6 +574,7 @@ Graphite dashboard included and it is recommended to install collectd on your monitoring host such that you can see if you hit resource issues with your monitoring host. + Install ELK Host (ElasticSearch/LogStash/Kibana) ------------------------------------------------- diff --git a/rally/rally-plugins/workloads/linpack.sh b/rally/rally-plugins/workloads/linpack.sh new file mode 100644 index 000000000..9e81eb1c9 --- /dev/null +++ b/rally/rally-plugins/workloads/linpack.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# Location of Linpack binary +LINPACK='/opt/linpack/xlinpack_xeon64' + +# Location to create linpack dat file +LINPACK_DAT='linpack.dat' + +NUM_CPU=`cat /proc/cpuinfo | grep processor | wc -l` +export OMP_NUM_THREADS=$NUM_CPU +echo "Sample Intel(R) LINPACK data file (from lininput_xeon64)" > ${LINPACK_DAT} +echo "Intel(R) LINPACK data" >> ${LINPACK_DAT} +echo "1 # number of tests" >> ${LINPACK_DAT} +echo "10514 # problem sizes" >> ${LINPACK_DAT} +echo "20016 # leading dimensions" >> ${LINPACK_DAT} +echo "2 # times to run a test " >> ${LINPACK_DAT} +echo "4 # alignment values (in KBytes)" >> ${LINPACK_DAT} +OUTPUT=$(sudo ${LINPACK} < ${LINPACK_DAT} | grep -A 1 Average | grep 20016) +AVERAGE=$(echo $OUTPUT | awk '{print $4}') +MAX=$(echo $OUTPUT | awk '{print $5}') + +echo "{ + \"average_gflops\": $AVERAGE, + \"max_gflops\": $MAX + }" + diff --git a/rally/rally-plugins/workloads/linpack.yml b/rally/rally-plugins/workloads/linpack.yml new file mode 100644 index 000000000..989b5e37c --- /dev/null +++ b/rally/rally-plugins/workloads/linpack.yml @@ -0,0 +1,46 @@ +{% set sla_max_avg_duration = sla_max_avg_duration or 60 %} +{% set sla_max_failure = sla_max_failure or 0 %} +{% set sla_max_seconds = sla_max_seconds or 60 %} +{% set times = times or 1 %} +{% set concurrency = concurrency or 1 %} +{% set username = username or 'centos' %} + +--- +VMTasks.boot_runcommand_delete: + - + args: + image: + name: '{{image_name}}' + flavor: + name: '{{flavor_name}}' + external: + name: '{{external_network}}' + command: + remote_path: "./linpack.sh" + local_path: "rally/rally-plugins/workloads/linpack.sh" + username: '{{username}}' + nics: + - net-id: '{{net_id}}' + runner: + concurrency: {{concurrency}} + times: {{times}} + type: "constant" + context: + users: + tenants: 1 + users_per_tenant: 1 + quotas: + neutron: + network: -1 + port: -1 + router: -1 + subnet: -1 + nova: + instances: -1 + cores: -1 + ram: -1 + sla: + max_avg_duration: {{sla_max_avg_duration}} + max_seconds_per_iteration: {{sla_max_seconds}} + failure_rate: + max: {{sla_max_failure}} diff --git a/rally/rally-plugins/workloads/pbench-uperf.png b/rally/rally-plugins/workloads/pbench-uperf.png new file mode 100644 index 0000000000000000000000000000000000000000..7a143be2d84ebee0d7bacdebd6026ccf8be82dcf GIT binary patch literal 54145 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

R++o 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 FO#rFtT8#hz literal 0 HcmV?d00001 diff --git a/rally/rally-plugins/workloads/pbench-uperf.py b/rally/rally-plugins/workloads/pbench-uperf.py new file mode 100644 index 000000000..d959f272d --- /dev/null +++ b/rally/rally-plugins/workloads/pbench-uperf.py @@ -0,0 +1,281 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from rally.task import scenario +from rally.plugins.openstack.scenarios.vm import utils as vm_utils +from rally.plugins.openstack.scenarios.neutron import utils as neutron_utils +from rally.task import types +from rally.task import validation +from rally.common import sshutils +import time +import StringIO +import csv +import json +import datetime +import logging +from Elastic import Elastic + +LOG = logging.getLogger(__name__) + + +class BrowbeatPlugin(neutron_utils.NeutronScenario, + vm_utils.VMScenario, + scenario.Scenario): + + def build_jump_host( + self, + external, + image, + flavor, + user, + password=None, + **kwargs): + keyname = self.context["user"]["keypair"]["name"] + jump_host, jump_host_ip = self._boot_server_with_fip(image, + flavor, + use_floating_ip=True, + floating_network=external['name'], + key_name=keyname, + **kwargs) + # Wait for ping + self._wait_for_ping(jump_host_ip['ip']) + + # Open SSH Connection + jump_ssh = sshutils.SSH(user, jump_host_ip['ip'], 22, self.context[ + "user"]["keypair"]["private"], password) + + # Check for connectivity + self._wait_for_ssh(jump_ssh) + + # Write id_rsa to get to guests. + self._run_command_over_ssh(jump_ssh, {'remote_path': "rm -rf ~/.ssh"}) + self._run_command_over_ssh(jump_ssh, {'remote_path': "mkdir ~/.ssh"}) + jump_ssh.run( + "cat > ~/.ssh/id_rsa", + stdin=self.context["user"]["keypair"]["private"]) + + jump_ssh.execute("chmod 0600 ~/.ssh/id_rsa") + return jump_ssh, jump_host_ip, jump_host + + def create_guest_pairs( + self, + jump_ssh, + num_pairs, + image, + flavor, + user, + zones=None, + **kwargs): + _servers = [] + _clients = [] + # Launch Guests + network_name = None + if num_pairs is 1: + if zones['server'] != 'None': + kwargs['availability_zone'] = zones['server'] + server = self._boot_server( + image, + flavor, + key_name=self.context["user"]["keypair"]["name"], + **kwargs) + if zones['client'] != 'None': + kwargs['availability_zone'] = zones['client'] + client = self._boot_server( + image, + flavor, + key_name=self.context["user"]["keypair"]["name"], + **kwargs) + for net in server.addresses: + network_name = net + break + if network_name is None: + return False + # IP Addresses + _servers.append( + str(server.addresses[network_name][0]["addr"])) + _clients.append( + str(client.addresses[network_name][0]["addr"])) + else: + for i in range(num_pairs): + if zones['server'] != 'None': + kwargs['availability_zone'] = zones['server'] + server = self._boot_server( + image, + flavor, + key_name=self.context["user"]["keypair"]["name"], + **kwargs) + if zones['client'] != 'None': + kwargs['availability_zone'] = zones['client'] + client = self._boot_server( + image, + flavor, + key_name=self.context["user"]["keypair"]["name"], + **kwargs) + + if network_name is None: + # IP Addresses + for net in server.addresses: + network_name = net + break + + if network_name is None: + return False + + _servers.append( + str(server.addresses[network_name][0]["addr"])) + _clients.append( + str(client.addresses[network_name][0]["addr"])) + + # Check status of guest + ready = False + retry = 10 + while (not ready): + for sip in _servers + _clients: + cmd = "ssh -o StrictHostKeyChecking=no {}@{} /bin/true".format( + user, sip) + s1_exitcode, s1_stdout, s1_stderr = jump_ssh.execute(cmd) + if retry < 1: + LOG.error( + "Error : Issue reaching {} the guests through the Jump host".format(sip)) + return False + if s1_exitcode is 0: + LOG.info("Server: {} ready".format(sip)) + ready = True + else: + LOG.info( + "Error reaching server: {} error {}".format( + sip, s1_stderr)) + retry = retry - 1 + time.sleep(10) + + return _clients, _servers + + @types.convert(image={"type": "glance_image"}, + flavor={"type": "nova_flavor"}) + @validation.required_openstack(users=True) + @scenario.configure(context={"cleanup": ["nova", "neutron", "cinder"], + "keypair": {}, "allow_ssh": {}}) + def pbench_uperf( + self, + image, + flavor, + user, + test_types, + protocols, + samples, + test_name, + external=None, + send_results=True, + num_pairs=1, + password="", + network_id=None, + zones=None, + message_sizes=None, + instances=None, + elastic_host=None, + elastic_port=None, + cloudname=None, + **kwargs): + + pbench_path = "/opt/pbench-agent" + pbench_results = "/var/lib/pbench-agent" + + # Create env + if not network_id: + router = self._create_router({}, external_gw=external) + network = self._create_network({}) + subnet = self._create_subnet(network, {}) + kwargs["nics"] = [{'net-id': network['network']['id']}] + self._add_interface_router(subnet['subnet'], router['router']) + else: + kwargs["nics"] = [{'net-id': network_id}] + + jump_ssh, jump_host_ip, jump_host = self.build_jump_host( + external, image, flavor, user, **kwargs) + _clients, _servers = self.create_guest_pairs( + jump_ssh, num_pairs, image, flavor, user, zones, **kwargs) + + # Register pbench across FIP + for sip in _servers + _clients: + cmd = "{}/util-scripts/pbench-register-tool-set --remote={}".format( + pbench_path, sip) + exitcode, stdout, stderr = jump_ssh.execute(cmd) + + # Start uperf against private address + uperf = "{}/bench-scripts/pbench-uperf --clients={} --servers={} --samples={}".format( + pbench_path, ','.join(_clients), ','.join(_servers), samples) + uperf += " --test-types={} --protocols={} --config={}".format( + test_types, + protocols, + test_name) + + if message_sizes is not None: + uperf += " --message-sizes={}".format(message_sizes) + + if instances is not None: + uperf += " --instances={}".format(instances) + + # Execute pbench-uperf + # execute returns, exitcode,stdout,stderr + LOG.info("Starting Rally - PBench UPerf") + uperf_exitcode, stdout_uperf, stderr = jump_ssh.execute(uperf) + + # Prepare results + cmd = "cat {}/uperf_{}*/result.csv".format(pbench_results, test_name) + exitcode, stdout, stderr = jump_ssh.execute(cmd) + if exitcode is 1: + return False + + if send_results: + if uperf_exitcode is not 1: + cmd = "cat {}/uperf_{}*/result.json".format( + pbench_results, test_name) + LOG.info("Running command : {}".format(cmd)) + exitcode, stdout_json, stderr = jump_ssh.execute(cmd) + LOG.info("Result: {}".format(stderr)) + + es_ts = datetime.datetime.utcnow() + config = { + 'elasticsearch': { + 'host': elastic_host, + 'port': elastic_port}, + 'browbeat': { + 'cloud_name': cloudname, + 'timestamp': es_ts, + 'num_pairs': num_pairs}} + elastic = Elastic(config, 'pbench') + json_result = StringIO.StringIO(stdout_json) + json_data = json.load(json_result) + for iteration in json_data: + elastic.index_result(iteration, test_name, 'results/') + else: + LOG.error("Error with PBench Results") + + # Parse results + result = StringIO.StringIO('\n'.join(stdout.split('\n')[1:])) + creader = csv.reader(result) + report = [] + for row in creader: + if len(row) >= 1: + report.append(["aggregate.{}".format(row[1]), float(row[2])]) + report.append(["single.{}".format(row[1]), float(row[3])]) + if len(report) > 0: + self.add_output( + additive={"title": "PBench UPerf Stats", + "description": "PBench UPerf Scenario", + "chart_plugin": "StatsTable", + "axis_label": "Gbps", + "label": "Gbps", + "data": report}) + + cmd = "{}/util-scripts/pbench-move-results".format(pbench_path) + self._run_command_over_ssh(jump_ssh, {"remote_path": cmd}) diff --git a/rally/rally-plugins/workloads/pbench-uperf.yml b/rally/rally-plugins/workloads/pbench-uperf.yml new file mode 100644 index 000000000..2b154f918 --- /dev/null +++ b/rally/rally-plugins/workloads/pbench-uperf.yml @@ -0,0 +1,63 @@ +{% set sla_max_avg_duration = sla_max_avg_duration or 60 %} +{% set sla_max_failure = sla_max_failure or 0 %} +{% set sla_max_seconds = sla_max_seconds or 60 %} +{% set times = times or 1 %} +{% set num_pairs = num_pairs or 1 %} +{% set instances = instances or 1 %} +{% set password = password or 'None' %} +{% set protocols = protocols or 'tcp' %} +{% set message_sizes = message_sizes or '64,1024,16384' %} +{% set hypervisor_server = hypervsior_server or 'None' %} +{% set hypervisor_client = hypervsior_client or 'None' %} + +--- +BrowbeatPlugin.pbench_uperf: + - + args: + image: + name: '{{image_name}}' + flavor: + name: '{{flavor_name}}' + zones: + server: '{{hypervisor_server}}' + client: '{{hypervisor_client}}' + external: + name: '{{external_network}}' + user: '{{user}}' + password: '{{password}}' + num_pairs: {{num_pairs}} + network_id: '{{net_id}}' + test_types: '{{test_types}}' + protocols: '{{protocols}}' + samples: '{{samples}}' + message_sizes: '{{message_sizes}}' + instances: '{{instances}}' + test_name: '{{test_name}}' + send_results: {{send_results}} + cloudname: '{{cloudname}}' + elastic_host: '{{elastic_host}}' + elastic_port: '{{elastic_port}}' + runner: + concurrency: 1 + times: 1 + type: "constant" + context: + users: + tenants: 1 + users_per_tenant: 1 + quotas: + neutron: + network: -1 + port: -1 + router: -1 + subnet: -1 + nova: + instances: -1 + cores: -1 + ram: -1 + sla: + max_avg_duration: {{sla_max_avg_duration}} + max_seconds_per_iteration: {{sla_max_seconds}} + failure_rate: + max: {{sla_max_failure}} +