aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.coveragerc7
-rw-r--r--.testr.conf4
-rw-r--r--ci/run_tasks.sh43
-rwxr-xr-xdocs/Yardstick_task_templates.rst141
-rw-r--r--samples/fio-template.yaml39
-rw-r--r--samples/fio.yaml11
-rw-r--r--samples/iperf3.yaml1
-rw-r--r--samples/lmbench.yaml1
-rw-r--r--samples/perf.yaml1
-rw-r--r--samples/ping-ext-ip.yaml1
-rw-r--r--samples/ping-ext-stimuli.yaml1
-rwxr-xr-xsamples/ping-iteration.yaml1
-rw-r--r--samples/ping-multiple-context.yaml2
-rw-r--r--samples/ping-option-list.yaml1
-rw-r--r--samples/ping-parallel.yaml1
-rw-r--r--samples/ping-serial.yaml1
-rw-r--r--samples/ping-template.yaml48
-rw-r--r--samples/ping.yaml1
-rw-r--r--samples/pktgen.yaml1
-rwxr-xr-x[-rw-r--r--]setup.py1
-rw-r--r--tests/__init__.py0
-rw-r--r--tests/unit/benchmark/context/__init__.py0
-rw-r--r--tests/unit/benchmark/context/test_model.py345
-rw-r--r--tests/unit/benchmark/scenarios/networking/iperf3_sample_output.json1
-rw-r--r--tests/unit/benchmark/scenarios/networking/test_iperf3.py133
-rw-r--r--tests/unit/benchmark/scenarios/storage/__init__.py0
-rw-r--r--tests/unit/benchmark/scenarios/storage/test_fio.py88
-rw-r--r--tests/unit/common/test_utils.py90
-rwxr-xr-xtools/ubuntu-server-cloudimg-modify.sh7
-rwxr-xr-xtools/yardstick-img-modify21
-rw-r--r--yardstick/benchmark/scenarios/storage/fio.py13
-rwxr-xr-x[-rw-r--r--]yardstick/cmd/commands/task.py66
-rwxr-xr-xyardstick/common/task_template.py53
33 files changed, 1090 insertions, 34 deletions
diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 000000000..07ca20984
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,7 @@
+[run]
+branch = True
+source = yardstick
+
+[report]
+ignore_errors = True
+precision = 3
diff --git a/.testr.conf b/.testr.conf
new file mode 100644
index 000000000..126233527
--- /dev/null
+++ b/.testr.conf
@@ -0,0 +1,4 @@
+[DEFAULT]
+test_command=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 ${PYTHON:-python} -m subunit.run discover -t ./ ./tests/unit $LISTOPT $IDOPTION
+test_id_option=--load-list $IDFILE
+test_list_option=--list
diff --git a/ci/run_tasks.sh b/ci/run_tasks.sh
new file mode 100644
index 000000000..27ccb3a3c
--- /dev/null
+++ b/ci/run_tasks.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+
+##############################################################################
+# Copyright (c) 2015 Ericsson AB and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+# Run yardstick tasks back-to-back
+# This script is called from yardstick-{pod} job and decides which tasks
+# are executed as part of that job.
+
+
+# verify that virtual environment is activated
+# assumes the virtual environment has been created as described in README.rst
+if [[ ! $(which python | grep venv) ]]; then
+ echo "Unable to activate venv...Exiting"
+ exit 1
+fi
+
+EXIT_CODE=0
+
+# Define tasks to be run
+TASK_FILE_NAMES[0]='samples/ping.yaml'
+TASK_FILE_NAMES[1]='samples/iperf3.yaml'
+TASK_FILE_NAMES[2]='samples/pktgen.yaml'
+TASK_FILE_NAMES[3]='samples/fio.yaml'
+
+# Execute tasks
+for TASK_FILE in ${TASK_FILE_NAMES[@]}
+do
+ echo "Executing task from file: $TASK_FILE"
+ yardstick -d task start $TASK_FILE
+
+ if [ $? -ne 0 ]; then
+ EXIT_CODE=1
+ fi
+done
+
+exit $EXIT_CODE \ No newline at end of file
diff --git a/docs/Yardstick_task_templates.rst b/docs/Yardstick_task_templates.rst
new file mode 100755
index 000000000..538937fd7
--- /dev/null
+++ b/docs/Yardstick_task_templates.rst
@@ -0,0 +1,141 @@
+Task Template Syntax
+====================
+
+Basic template syntax
+---------------------
+A nice feature of the input task format used in Yardstick is that it supports the template syntax based on Jinja2.
+This turns out to be extremely useful when, say, you have a fixed structure of your task but you want to
+parameterize this task in some way.
+For example, imagine your input task file (task.yaml) runs a set of Ping scenarios:
+
+::
+
+ # Sample benchmark task config file
+ # measure network latency using ping
+ schema: "yardstick:task:0.1"
+
+ scenarios:
+ -
+ type: Ping
+ options:
+ packetsize: 200
+ host: athena.demo
+ target: ares.demo
+
+ runner:
+ type: Duration
+ duration: 60
+ interval: 1
+
+ sla:
+ max_rtt: 10
+ action: monitor
+
+ context:
+ ...
+
+Let's say you want to run the same set of scenarios with the same runner/context/sla,
+but you want to try another packetsize to compare the performance.
+The most elegant solution is then to turn the packetsize name into a template variable:
+
+::
+
+ # Sample benchmark task config file
+ # measure network latency using ping
+
+ schema: "yardstick:task:0.1"
+ scenarios:
+ -
+ type: Ping
+ options:
+ packetsize: {{packetsize}}
+ host: athena.demo
+ target: ares.demo
+
+ runner:
+ type: Duration
+ duration: 60
+ interval: 1
+
+ sla:
+ max_rtt: 10
+ action: monitor
+
+ context:
+ ...
+
+and then pass the argument value for {{packetsize}} when starting a task with this configuration file.
+Yardstick provides you with different ways to do that:
+
+1.Pass the argument values directly in the command-line interface (with either a JSON or YAML dictionary):
+
+::
+
+ yardstick task start samples/ping-template.yaml --task-args '{"packetsize": "200"}'
+
+2.Refer to a file that specifies the argument values (JSON/YAML):
+
+::
+
+ yardstick task start samples/ping-template.yaml --task-args-file args.yaml
+
+Using the default values
+------------------------
+Note that the Jinja2 template syntax allows you to set the default values for your parameters.
+With default values set, your task file will work even if you don't parameterize it explicitly while starting a task.
+The default values should be set using the {% set ... %} clause (task.yaml).For example:
+
+::
+
+ # Sample benchmark task config file
+ # measure network latency using ping
+ schema: "yardstick:task:0.1"
+ {% set packetsize = packetsize or "100" %}
+ scenarios:
+ -
+ type: Ping
+ options:
+ packetsize: {{packetsize}}
+ host: athena.demo
+ target: ares.demo
+
+ runner:
+ type: Duration
+ duration: 60
+ interval: 1
+ ...
+
+If you don't pass the value for {{packetsize}} while starting a task, the default one will be used.
+
+Advanced templates
+------------------
+Yardstick makes it possible to use all the power of Jinja2 template syntax, including the mechanism of built-in functions.
+As an example, let us make up a task file that will do a block storage performance test.
+The input task file (fio-template.yaml) below uses the Jinja2 for-endfor construct to accomplish that:
+
+::
+
+ #Test block sizes of 4KB, 8KB, 64KB, 1MB
+ #Test 5 workloads: read, write, randwrite, randread, rw
+ schema: "yardstick:task:0.1"
+
+ scenarios:
+ {% for bs in ['4k', '8k', '64k', '1024k' ] %}
+ {% for rw in ['read', 'write', 'randwrite', 'randread', 'rw' ] %}
+ -
+ type: Fio
+ options:
+ filename: /home/ec2-user/data.raw
+ bs: {{bs}}
+ rw: {{rw}}
+ ramp_time: 10
+ host: fio.demo
+ runner:
+ type: Duration
+ duration: 60
+ interval: 60
+
+ {% endfor %}
+ {% endfor %}
+ context
+ ...
diff --git a/samples/fio-template.yaml b/samples/fio-template.yaml
new file mode 100644
index 000000000..395e4c417
--- /dev/null
+++ b/samples/fio-template.yaml
@@ -0,0 +1,39 @@
+# Sample benchmark task config file
+# measure storage performance using fio
+# Jinja2 Syntax is supported
+# using built-in functions ( Jinja2 for-endfor construct ) to test complex tasks
+# Test block sizes of 4KB, 8KB, 64KB, 1MB
+# Test 5 workloads: 4 corners and 1 mixed :read, write, randwrite, randread, rw
+schema: "yardstick:task:0.1"
+
+scenarios:
+{% for rw in ['read', 'write', 'randwrite', 'randread', 'rw'] %}
+ {% for bs in ['4k', '8k', '64k', '1024k'] %}
+-
+ type: Fio
+ options:
+ filename: /home/ec2-user/data.raw
+ bs: {{bs}}
+ rw: {{rw}}
+ ramp_time: 10
+ duration: 20
+ host: fio.demo
+ runner:
+ type: Iteration
+ iterations: 2
+ interval: 1
+ {% endfor %}
+{% endfor %}
+
+context:
+ name: demo
+ image: yardstick-trusty-server
+ flavor: yardstick-flavor
+ user: ec2-user
+ servers:
+ fio:
+ floating_ip: true
+ networks:
+ test:
+ cidr: "10.0.1.0/24"
+
diff --git a/samples/fio.yaml b/samples/fio.yaml
index f70912a15..44444c7cc 100644
--- a/samples/fio.yaml
+++ b/samples/fio.yaml
@@ -5,9 +5,10 @@
# For this sample just like running the command below on the test vm and
# getting benchmark info back to the yardstick.
#
-# sudo fio -filename=/home/ec2-user/data.raw -bs=4k -rw=write -ramp_time=10 \
-# -runtime=60 -name=yardstick-fio -ioengine=libaio -direct=1 \
-# -group_reporting -numjobs=1 -time_based --output=yardstick-fio.log
+# sudo fio -filename=/home/ec2-user/data.raw -bs=4k -ipdepth=1 -rw=write \
+# -ramp_time=10 -runtime=60 -name=yardstick-fio -ioengine=libaio \
+# -direct=1 -group_reporting -numjobs=1 -time_based \
+# --output=yardstick-fio.log
#
# When the above fio command done, the yardstick-fio.log file will contain
# information like below and the benchmark script will take iops, throughput
@@ -52,13 +53,14 @@ scenarios:
options:
filename: /home/ec2-user/data.raw
bs: 4k
+ iodepth: 1
rw: write
ramp_time: 10
host: fio.demo
runner:
type: Duration
duration: 60
- interval: 60
+ interval: 1
context:
name: demo
@@ -71,4 +73,3 @@ context:
networks:
test:
cidr: "10.0.1.0/24"
- external_network: "net04_ext"
diff --git a/samples/iperf3.yaml b/samples/iperf3.yaml
index b3a7a9f3b..8de44678c 100644
--- a/samples/iperf3.yaml
+++ b/samples/iperf3.yaml
@@ -40,5 +40,4 @@ context:
networks:
test:
cidr: '10.0.1.0/24'
- external_network: "net04_ext"
diff --git a/samples/lmbench.yaml b/samples/lmbench.yaml
index c7526c04c..256d8c67e 100644
--- a/samples/lmbench.yaml
+++ b/samples/lmbench.yaml
@@ -41,6 +41,5 @@ context:
networks:
test:
cidr: '10.0.1.0/24'
- external_network: "net04_ext"
diff --git a/samples/perf.yaml b/samples/perf.yaml
index e7ba2d0e7..b6ce2a226 100644
--- a/samples/perf.yaml
+++ b/samples/perf.yaml
@@ -40,4 +40,3 @@ context:
networks:
test:
cidr: "10.0.1.0/24"
- external_network: "net04_ext"
diff --git a/samples/ping-ext-ip.yaml b/samples/ping-ext-ip.yaml
index f2923f65f..d36c29544 100644
--- a/samples/ping-ext-ip.yaml
+++ b/samples/ping-ext-ip.yaml
@@ -32,5 +32,4 @@ context:
networks:
test:
cidr: '10.0.1.0/24'
- external_network: "net04_ext"
diff --git a/samples/ping-ext-stimuli.yaml b/samples/ping-ext-stimuli.yaml
index cfe791567..451f0105f 100644
--- a/samples/ping-ext-stimuli.yaml
+++ b/samples/ping-ext-stimuli.yaml
@@ -45,5 +45,4 @@ context:
networks:
test:
cidr: '10.0.1.0/24'
- external_network: "net04_ext"
diff --git a/samples/ping-iteration.yaml b/samples/ping-iteration.yaml
index 810530c82..a5e90941b 100755
--- a/samples/ping-iteration.yaml
+++ b/samples/ping-iteration.yaml
@@ -41,5 +41,4 @@ context:
networks:
test:
cidr: '10.0.1.0/24'
- external_network: "net04_ext"
diff --git a/samples/ping-multiple-context.yaml b/samples/ping-multiple-context.yaml
index c529fccb8..1c27e1bf1 100644
--- a/samples/ping-multiple-context.yaml
+++ b/samples/ping-multiple-context.yaml
@@ -36,7 +36,6 @@ contexts:
networks:
test:
cidr: '10.0.1.0/24'
- external_network: "net04_ext"
-
name: demo2
image: cirros-0.3.3
@@ -52,5 +51,4 @@ contexts:
networks:
test:
cidr: '10.0.1.0/24'
- external_network: "net04_ext"
diff --git a/samples/ping-option-list.yaml b/samples/ping-option-list.yaml
index 1fa95e6c2..30d133eb8 100644
--- a/samples/ping-option-list.yaml
+++ b/samples/ping-option-list.yaml
@@ -39,6 +39,5 @@ context:
networks:
test:
cidr: '10.0.1.0/24'
- external_network: "net04_ext"
diff --git a/samples/ping-parallel.yaml b/samples/ping-parallel.yaml
index f3f698993..00d261383 100644
--- a/samples/ping-parallel.yaml
+++ b/samples/ping-parallel.yaml
@@ -53,5 +53,4 @@ context:
networks:
test:
cidr: '10.0.1.0/24'
- external_network: "net04_ext"
diff --git a/samples/ping-serial.yaml b/samples/ping-serial.yaml
index ff281ee82..37ea715a2 100644
--- a/samples/ping-serial.yaml
+++ b/samples/ping-serial.yaml
@@ -45,5 +45,4 @@ context:
networks:
test:
cidr: '10.0.1.0/24'
- external_network: "net04_ext"
diff --git a/samples/ping-template.yaml b/samples/ping-template.yaml
new file mode 100644
index 000000000..cde6ddd7c
--- /dev/null
+++ b/samples/ping-template.yaml
@@ -0,0 +1,48 @@
+# Sample benchmark task config file
+# measure network latency using ping
+# Jinja2 Syntax is supported
+# parameterize this task, {{packetsize}} is passed to the scenario as an argument
+# If you don't pass the value for {{packetsize}} while starting a task,
+# the default one will be used.
+
+
+schema: "yardstick:task:0.1"
+{% set packetsize = packetsize or "100" %}
+scenarios:
+-
+ type: Ping
+ options:
+ packetsize: {{packetsize}}
+ host: athena.demo
+ target: ares.demo
+
+ runner:
+ type: Duration
+ duration: 60
+ interval: 1
+
+ sla:
+ max_rtt: 10
+ action: monitor
+
+context:
+ name: demo
+ image: cirros-0.3.3
+ flavor: m1.tiny
+ user: cirros
+
+ placement_groups:
+ pgrp1:
+ policy: "availability"
+
+ servers:
+ athena:
+ floating_ip: true
+ placement: "pgrp1"
+ ares:
+ placement: "pgrp1"
+
+ networks:
+ test:
+ cidr: '10.0.1.0/24'
+
diff --git a/samples/ping.yaml b/samples/ping.yaml
index f8b6a3167..845d10dcc 100644
--- a/samples/ping.yaml
+++ b/samples/ping.yaml
@@ -41,5 +41,4 @@ context:
networks:
test:
cidr: '10.0.1.0/24'
- external_network: "net04_ext"
diff --git a/samples/pktgen.yaml b/samples/pktgen.yaml
index 609730842..11d62795e 100644
--- a/samples/pktgen.yaml
+++ b/samples/pktgen.yaml
@@ -47,6 +47,5 @@ context:
networks:
test:
cidr: '10.0.1.0/24'
- external_network: "net04_ext"
diff --git a/setup.py b/setup.py
index f73094ac1..f171aafee 100644..100755
--- a/setup.py
+++ b/setup.py
@@ -19,6 +19,7 @@ setup(
url="https://www.opnfv.org",
install_requires=["backport_ipaddress", # remove with python3
"flake8",
+ "Jinja2>=2.6",
"PyYAML>=3.10",
"pbr<2.0,>=1.3",
"python-glanceclient>=0.12.0",
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/__init__.py
diff --git a/tests/unit/benchmark/context/__init__.py b/tests/unit/benchmark/context/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/unit/benchmark/context/__init__.py
diff --git a/tests/unit/benchmark/context/test_model.py b/tests/unit/benchmark/context/test_model.py
new file mode 100644
index 000000000..cf0a605f4
--- /dev/null
+++ b/tests/unit/benchmark/context/test_model.py
@@ -0,0 +1,345 @@
+#!/usr/bin/env python
+
+##############################################################################
+# Copyright (c) 2015 Ericsson AB and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+# Unittest for yardstick.benchmark.context.model
+
+import mock
+import unittest
+
+from yardstick.benchmark.context import model
+
+
+class ObjectTestCase(unittest.TestCase):
+
+ def setUp(self):
+ self.mock_context = mock.Mock()
+
+ def test_construct(self):
+
+ test_object = model.Object('foo', self.mock_context)
+
+ self.assertEqual(test_object.name, 'foo')
+ self.assertEqual(test_object._context, self.mock_context)
+ self.assertIsNone(test_object.stack_name)
+ self.assertIsNone(test_object.stack_id)
+
+ def test_dn(self):
+
+ self.mock_context.name = 'bar'
+ test_object = model.Object('foo', self.mock_context)
+
+ self.assertEqual('foo.bar', test_object.dn)
+
+
+class PlacementGroupTestCase(unittest.TestCase):
+
+ def setUp(self):
+ self.mock_context = mock.Mock()
+ self.mock_context.name = 'bar'
+
+ def tearDown(self):
+ model.PlacementGroup.map = {}
+
+ def test_sucessful_construct(self):
+
+ test_pg = model.PlacementGroup('foo', self.mock_context, 'affinity')
+
+ self.assertEqual(test_pg.name, 'foo')
+ self.assertEqual(test_pg.members, set())
+ self.assertEqual(test_pg.stack_name, 'bar-foo')
+ self.assertEqual(test_pg.policy, 'affinity')
+
+ test_map = {'foo': test_pg}
+ self.assertEqual(model.PlacementGroup.map, test_map)
+
+ def test_wrong_policy_in_construct(self):
+
+ self.assertRaises(ValueError, model.PlacementGroup, 'foo',
+ self.mock_context, 'baz')
+
+ def test_add_member(self):
+
+ test_pg = model.PlacementGroup('foo', self.mock_context, 'affinity')
+ test_pg.add_member('foo')
+
+ self.assertEqual(test_pg.members, set(['foo']))
+
+ def test_get_name_successful(self):
+
+ model.PlacementGroup.map = {'foo': True}
+ self.assertTrue(model.PlacementGroup.get('foo'))
+
+ def test_get_name_unsuccessful(self):
+
+ self.assertIsNone(model.PlacementGroup.get('foo'))
+
+
+class RouterTestCase(unittest.TestCase):
+
+ def test_construct(self):
+
+ mock_context = mock.Mock()
+ mock_context.name = 'baz'
+ test_router = model.Router('foo', 'bar', mock_context, 'qux')
+
+ self.assertEqual(test_router.stack_name, 'baz-bar-foo')
+ self.assertEqual(test_router.stack_if_name, 'baz-bar-foo-if0')
+ self.assertEqual(test_router.external_gateway_info, 'qux')
+
+
+class NetworkTestCase(unittest.TestCase):
+
+ def setUp(self):
+ self.mock_context = mock.Mock()
+ self.mock_context.name = 'bar'
+
+ def tearDown(self):
+ model.Network.list = []
+
+ def test_construct_no_external_network(self):
+
+ attrs = {'cidr': '10.0.0.0/24'}
+ test_network = model.Network('foo', self.mock_context, attrs)
+
+ self.assertEqual(test_network.stack_name, 'bar-foo')
+ self.assertEqual(test_network.subnet_stack_name, 'bar-foo-subnet')
+ self.assertEqual(test_network.subnet_cidr, attrs['cidr'])
+ self.assertIsNone(test_network.router)
+ self.assertIn(test_network, model.Network.list)
+
+ def test_construct_has_external_network(self):
+
+ attrs = {'external_network': 'ext_net'}
+ test_network = model.Network('foo', self.mock_context, attrs)
+ exp_router = model.Router('router', 'foo', self.mock_context, 'ext_net')
+
+ self.assertEqual(test_network.router.stack_name, exp_router.stack_name)
+ self.assertEqual(test_network.router.stack_if_name,
+ exp_router.stack_if_name)
+ self.assertEqual(test_network.router.external_gateway_info,
+ exp_router.external_gateway_info)
+
+ def test_has_route_to(self):
+
+ attrs = {'external_network': 'ext_net'}
+ test_network = model.Network('foo', self.mock_context, attrs)
+
+ self.assertTrue(test_network.has_route_to('ext_net'))
+
+ def test_has_no_route_to(self):
+
+ attrs = {}
+ test_network = model.Network('foo', self.mock_context, attrs)
+
+ self.assertFalse(test_network.has_route_to('ext_net'))
+
+ @mock.patch('yardstick.benchmark.context.model.Network.has_route_to')
+ def test_find_by_route_to(self, mock_has_route_to):
+
+ mock_network = mock.Mock()
+ model.Network.list = [mock_network]
+ mock_has_route_to.return_value = True
+
+ self.assertIs(mock_network, model.Network.find_by_route_to('foo'))
+
+ def test_find_external_network(self):
+
+ mock_network = mock.Mock()
+ mock_network.router = mock.Mock()
+ mock_network.router.external_gateway_info = 'ext_net'
+ model.Network.list = [mock_network]
+
+ self.assertEqual(model.Network.find_external_network(), 'ext_net')
+
+
+class ServerTestCase(unittest.TestCase):
+
+ def setUp(self):
+ self.mock_context = mock.Mock()
+ self.mock_context.name = 'bar'
+ self.mock_context.keypair_name = 'some-keys'
+ self.mock_context.secgroup_name = 'some-secgroup'
+
+ def test_construct_defaults(self):
+
+ attrs = None
+ test_server = model.Server('foo', self.mock_context, attrs)
+
+ self.assertEqual(test_server.stack_name, 'foo.bar')
+ self.assertEqual(test_server.keypair_name, 'some-keys')
+ self.assertEqual(test_server.secgroup_name, 'some-secgroup')
+ self.assertEqual(test_server.placement_groups, [])
+ self.assertEqual(test_server.instances, 1)
+ self.assertIsNone(test_server.floating_ip)
+ self.assertIsNone(test_server._image)
+ self.assertIsNone(test_server._flavor)
+ self.assertIn(test_server, model.Server.list)
+
+ @mock.patch('yardstick.benchmark.context.model.PlacementGroup')
+ def test_construct_get_wrong_placement_group(self, mock_pg):
+
+ attrs = {'placement': 'baz'}
+ mock_pg.get.return_value = None
+
+ self.assertRaises(ValueError, model.Server, 'foo',
+ self.mock_context, attrs)
+
+ @mock.patch('yardstick.benchmark.context.model.HeatTemplate')
+ def test__add_instance(self, mock_template):
+
+ attrs = {'image': 'some-image', 'flavor': 'some-flavor'}
+ test_server = model.Server('foo', self.mock_context, attrs)
+
+ mock_network = mock.Mock()
+ mock_network.name = 'some-network'
+ mock_network.stack_name = 'some-network-stack'
+ mock_network.subnet_stack_name = 'some-network-stack-subnet'
+
+ test_server._add_instance(mock_template, 'some-server',
+ [mock_network], 'hints')
+
+ mock_template.add_port.assert_called_with(
+ 'some-server-some-network-port',
+ mock_network.stack_name,
+ mock_network.subnet_stack_name,
+ sec_group_id=self.mock_context.secgroup_name)
+
+ mock_template.add_server.assert_called_with(
+ 'some-server', 'some-image', 'some-flavor',
+ ports=['some-server-some-network-port'],
+ key_name=self.mock_context.keypair_name,
+ scheduler_hints='hints')
+
+
+class ContextTestCase(unittest.TestCase):
+
+ def setUp(self):
+ self.test_context = model.Context()
+ self.mock_context = mock.Mock()
+
+ def tearDown(self):
+ model.Context.list = []
+
+ def test_construct(self):
+
+ self.assertIsNone(self.test_context.name)
+ self.assertIsNone(self.test_context.stack)
+ self.assertEqual(self.test_context.networks, [])
+ self.assertEqual(self.test_context.servers, [])
+ self.assertEqual(self.test_context.placement_groups, [])
+ self.assertIsNone(self.test_context.keypair_name)
+ self.assertIsNone(self.test_context.secgroup_name)
+ self.assertEqual(self.test_context._server_map, {})
+ self.assertIsNone(self.test_context._image)
+ self.assertIsNone(self.test_context._flavor)
+ self.assertIsNone(self.test_context._user)
+ self.assertIsNone(self.test_context.template_file)
+ self.assertIsNone(self.test_context.heat_parameters)
+ self.assertIn(self.test_context, model.Context.list)
+
+ @mock.patch('yardstick.benchmark.context.model.PlacementGroup')
+ @mock.patch('yardstick.benchmark.context.model.Network')
+ @mock.patch('yardstick.benchmark.context.model.Server')
+ def test_init(self, mock_server, mock_network, mock_pg):
+
+ pgs = {'pgrp1': {'policy': 'availability'}}
+ networks = {'bar': {'cidr': '10.0.1.0/24'}}
+ servers = {'baz': {'floating_ip': True, 'placement': 'pgrp1'}}
+ attrs = {'name': 'foo',
+ 'placement_groups': pgs,
+ 'networks': networks,
+ 'servers': servers}
+
+ self.test_context.init(attrs)
+
+ self.assertEqual(self.test_context.keypair_name, "foo-key")
+ self.assertEqual(self.test_context.secgroup_name, "foo-secgroup")
+
+ mock_pg.assert_called_with('pgrp1', self.test_context,
+ pgs['pgrp1']['policy'])
+ self.assertTrue(len(self.test_context.placement_groups) == 1)
+
+ mock_network.assert_called_with(
+ 'bar', self.test_context, networks['bar'])
+ self.assertTrue(len(self.test_context.networks) == 1)
+
+ mock_server.assert_called_with('baz', self.test_context, servers['baz'])
+ self.assertTrue(len(self.test_context.servers) == 1)
+
+ @mock.patch('yardstick.benchmark.context.model.HeatTemplate')
+ def test__add_resources_to_template_no_servers(self, mock_template):
+
+ self.test_context.keypair_name = "foo-key"
+ self.test_context.secgroup_name = "foo-secgroup"
+
+ self.test_context._add_resources_to_template(mock_template)
+ mock_template.add_keypair.assert_called_with("foo-key")
+ mock_template.add_security_group.assert_called_with("foo-secgroup")
+
+ @mock.patch('yardstick.benchmark.context.model.HeatTemplate')
+ def test_deploy(self, mock_template):
+
+ self.test_context.name = 'foo'
+ self.test_context.template_file = '/bar/baz/some-heat-file'
+ self.test_context.heat_parameters = {'image': 'cirros'}
+ self.test_context.deploy()
+
+ mock_template.assert_called_with(self.test_context.name,
+ self.test_context.template_file,
+ self.test_context.heat_parameters)
+ self.assertIsNotNone(self.test_context.stack)
+
+ @mock.patch('yardstick.benchmark.context.model.HeatTemplate')
+ def test_undeploy(self, mock_template):
+
+ self.test_context.stack = mock_template
+ self.test_context.undeploy()
+
+ self.assertTrue(mock_template.delete.called)
+
+ def test_get_server_by_name(self):
+
+ self.mock_context._server_map = {'foo.bar': True}
+ model.Context.list = [self.mock_context]
+
+ self.assertTrue(model.Context.get_server_by_name('foo.bar'))
+
+ def test_get_server_by_wrong_name(self):
+
+ self.assertRaises(ValueError, model.Context.get_server_by_name, 'foo')
+
+ def test_get_context_by_name(self):
+
+ self.mock_context.name = 'foo'
+ model.Context.list = [self.mock_context]
+
+ self.assertIs(model.Context.get_context_by_name('foo'),
+ self.mock_context)
+
+ def test_get_unknown_context_by_name(self):
+
+ model.Context.list = []
+ self.assertIsNone(model.Context.get_context_by_name('foo'))
+
+ @mock.patch('yardstick.benchmark.context.model.Server')
+ def test_get_server(self, mock_server):
+
+ self.mock_context.name = 'bar'
+ self.mock_context.stack.outputs = {'public_ip': '127.0.0.1',
+ 'private_ip': '10.0.0.1'}
+ model.Context.list = [self.mock_context]
+ attr_name = {'name': 'foo.bar',
+ 'public_ip_attr': 'public_ip',
+ 'private_ip_attr': 'private_ip'}
+ result = model.Context.get_server(attr_name)
+
+ self.assertEqual(result.public_ip, '127.0.0.1')
+ self.assertEqual(result.private_ip, '10.0.0.1')
diff --git a/tests/unit/benchmark/scenarios/networking/iperf3_sample_output.json b/tests/unit/benchmark/scenarios/networking/iperf3_sample_output.json
new file mode 100644
index 000000000..b56009ba1
--- /dev/null
+++ b/tests/unit/benchmark/scenarios/networking/iperf3_sample_output.json
@@ -0,0 +1 @@
+{"start": {"connecting_to": {"host": "172.16.0.252", "port": 5201}, "timestamp": {"timesecs": 1436254758, "time": "Tue, 07 Jul 2015 07:39:18 GMT"}, "test_start": {"protocol": "TCP", "num_streams": 1, "omit": 0, "bytes": 0, "blksize": 131072, "duration": 10, "blocks": 0, "reverse": 0}, "system_info": "Linux client 3.13.0-55-generic #94-Ubuntu SMP Thu Jun 18 00:27:10 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux\n", "version": "iperf 3.0.7", "connected": [{"local_host": "10.0.1.2", "local_port": 37633, "remote_host": "172.16.0.252", "socket": 4, "remote_port": 5201}], "cookie": "client.1436254758.606879.1fb328dc230", "tcp_mss_default": 1448}, "intervals": [{"sum": {"end": 1.00068, "seconds": 1.00068, "bytes": 16996624, "bits_per_second": 135881000.0, "start": 0, "retransmits": 0, "omitted": false}, "streams": [{"end": 1.00068, "socket": 4, "seconds": 1.00068, "bytes": 16996624, "bits_per_second": 135881000.0, "start": 0, "retransmits": 0, "omitted": false, "snd_cwnd": 451776}]}, {"sum": {"end": 2.00048, "seconds": 0.999804, "bytes": 20010192, "bits_per_second": 160113000.0, "start": 1.00068, "retransmits": 0, "omitted": false}, "streams": [{"end": 2.00048, "socket": 4, "seconds": 0.999804, "bytes": 20010192, "bits_per_second": 160113000.0, "start": 1.00068, "retransmits": 0, "omitted": false, "snd_cwnd": 713864}]}, {"sum": {"end": 3.00083, "seconds": 1.00035, "bytes": 18330464, "bits_per_second": 146592000.0, "start": 2.00048, "retransmits": 0, "omitted": false}, "streams": [{"end": 3.00083, "socket": 4, "seconds": 1.00035, "bytes": 18330464, "bits_per_second": 146592000.0, "start": 2.00048, "retransmits": 0, "omitted": false, "snd_cwnd": 768888}]}, {"sum": {"end": 4.00707, "seconds": 1.00624, "bytes": 19658376, "bits_per_second": 156292000.0, "start": 3.00083, "retransmits": 0, "omitted": false}, "streams": [{"end": 4.00707, "socket": 4, "seconds": 1.00624, "bytes": 19658376, "bits_per_second": 156292000.0, "start": 3.00083, "retransmits": 0, "omitted": false, "snd_cwnd": 812328}]}, {"sum": {"end": 5.00104, "seconds": 0.993972, "bytes": 15709072, "bits_per_second": 126435000.0, "start": 4.00707, "retransmits": 0, "omitted": false}, "streams": [{"end": 5.00104, "socket": 4, "seconds": 0.993972, "bytes": 15709072, "bits_per_second": 126435000.0, "start": 4.00707, "retransmits": 0, "omitted": false, "snd_cwnd": 849976}]}, {"sum": {"end": 6.00049, "seconds": 0.999443, "bytes": 19616288, "bits_per_second": 157018000.0, "start": 5.00104, "retransmits": 53, "omitted": false}, "streams": [{"end": 6.00049, "socket": 4, "seconds": 0.999443, "bytes": 19616288, "bits_per_second": 157018000.0, "start": 5.00104, "retransmits": 53, "omitted": false, "snd_cwnd": 641464}]}, {"sum": {"end": 7.00085, "seconds": 1.00036, "bytes": 22250480, "bits_per_second": 177939000.0, "start": 6.00049, "retransmits": 0, "omitted": false}, "streams": [{"end": 7.00085, "socket": 4, "seconds": 1.00036, "bytes": 22250480, "bits_per_second": 177939000.0, "start": 6.00049, "retransmits": 0, "omitted": false, "snd_cwnd": 706624}]}, {"sum": {"end": 8.00476, "seconds": 1.00391, "bytes": 22282240, "bits_per_second": 177564000.0, "start": 7.00085, "retransmits": 0, "omitted": false}, "streams": [{"end": 8.00476, "socket": 4, "seconds": 1.00391, "bytes": 22282240, "bits_per_second": 177564000.0, "start": 7.00085, "retransmits": 0, "omitted": false, "snd_cwnd": 761648}]}, {"sum": {"end": 9.0016, "seconds": 0.996847, "bytes": 19657680, "bits_per_second": 157759000.0, "start": 8.00476, "retransmits": 28, "omitted": false}, "streams": [{"end": 9.0016, "socket": 4, "seconds": 0.996847, "bytes": 19657680, "bits_per_second": 157759000.0, "start": 8.00476, "retransmits": 28, "omitted": false, "snd_cwnd": 570512}]}, {"sum": {"end": 10.0112, "seconds": 1.00955, "bytes": 20932520, "bits_per_second": 165876000.0, "start": 9.0016, "retransmits": 0, "omitted": false}, "streams": [{"end": 10.0112, "socket": 4, "seconds": 1.00955, "bytes": 20932520, "bits_per_second": 165876000.0, "start": 9.0016, "retransmits": 0, "omitted": false, "snd_cwnd": 615400}]}], "end": {"sum_received": {"seconds": 10.0112, "start": 0, "end": 10.0112, "bytes": 193366712, "bits_per_second": 154521000.0}, "streams": [{"sender": {"end": 10.0112, "socket": 4, "seconds": 10.0112, "bytes": 195443936, "bits_per_second": 156181000.0, "start": 0, "retransmits": 81}, "receiver": {"end": 10.0112, "socket": 4, "seconds": 10.0112, "bytes": 193366712, "bits_per_second": 154521000.0, "start": 0}}], "sum_sent": {"end": 10.0112, "seconds": 10.0112, "bytes": 195443936, "bits_per_second": 156181000.0, "start": 0, "retransmits": 81}, "cpu_utilization_percent": {"remote_user": 1.10295, "remote_system": 40.0403, "host_user": 2.41785, "remote_total": 41.1438, "host_system": 5.09548, "host_total": 7.51411}}} \ No newline at end of file
diff --git a/tests/unit/benchmark/scenarios/networking/test_iperf3.py b/tests/unit/benchmark/scenarios/networking/test_iperf3.py
new file mode 100644
index 000000000..239e46a1c
--- /dev/null
+++ b/tests/unit/benchmark/scenarios/networking/test_iperf3.py
@@ -0,0 +1,133 @@
+#!/usr/bin/env python
+
+##############################################################################
+# Copyright (c) 2015 Ericsson AB and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+# Unittest for yardstick.benchmark.scenarios.networking.iperf3.Iperf
+
+import mock
+import unittest
+import os
+import json
+
+from yardstick.benchmark.scenarios.networking import iperf3
+
+
+@mock.patch('yardstick.benchmark.scenarios.networking.iperf3.ssh')
+class IperfTestCase(unittest.TestCase):
+
+ def setUp(self):
+ self.ctx = {
+ 'host': '172.16.0.137',
+ 'target': '172.16.0.138',
+ 'user': 'cirros',
+ 'key_filename': "mykey.key"
+ }
+
+ def test_iperf_successful_setup(self, mock_ssh):
+
+ p = iperf3.Iperf(self.ctx)
+ mock_ssh.SSH().execute.return_value = (0, '', '')
+
+ p.setup()
+ self.assertIsNotNone(p.target)
+ self.assertIsNotNone(p.host)
+ mock_ssh.SSH().execute.assert_called_with("iperf3 -s -D")
+
+ def test_iperf_unsuccessful_setup(self, mock_ssh):
+
+ p = iperf3.Iperf(self.ctx)
+ mock_ssh.SSH().execute.return_value = (1, '', 'FOOBAR')
+ self.assertRaises(RuntimeError, p.setup)
+
+ def test_iperf_successful_teardown(self, mock_ssh):
+
+ p = iperf3.Iperf(self.ctx)
+ mock_ssh.SSH().execute.return_value = (0, '', '')
+ p.host = mock_ssh.SSH()
+ p.target = mock_ssh.SSH()
+
+ p.teardown()
+ self.assertTrue(mock_ssh.SSH().close.called)
+ mock_ssh.SSH().execute.assert_called_with("pkill iperf3")
+
+ def test_iperf_successful_no_sla(self, mock_ssh):
+
+ p = iperf3.Iperf(self.ctx)
+ mock_ssh.SSH().execute.return_value = (0, '', '')
+ p.host = mock_ssh.SSH()
+
+ options = {}
+ args = {'options': options}
+
+ sample_output = self._read_sample_output()
+ mock_ssh.SSH().execute.return_value = (0, sample_output, '')
+ expected_result = json.loads(sample_output)
+ result = p.run(args)
+ self.assertEqual(result, expected_result)
+
+ def test_iperf_successful_sla(self, mock_ssh):
+
+ p = iperf3.Iperf(self.ctx)
+ mock_ssh.SSH().execute.return_value = (0, '', '')
+ p.host = mock_ssh.SSH()
+
+ options = {}
+ args = {
+ 'options': options,
+ 'sla': {'bytes_per_second': 15000000}
+ }
+
+ sample_output = self._read_sample_output()
+ mock_ssh.SSH().execute.return_value = (0, sample_output, '')
+ expected_result = json.loads(sample_output)
+ result = p.run(args)
+ self.assertEqual(result, expected_result)
+
+ def test_iperf_unsuccessful_sla(self, mock_ssh):
+
+ p = iperf3.Iperf(self.ctx)
+ mock_ssh.SSH().execute.return_value = (0, '', '')
+ p.host = mock_ssh.SSH()
+
+ options = {}
+ args = {
+ 'options': options,
+ 'sla': {'bytes_per_second': 25000000}
+ }
+
+ sample_output = self._read_sample_output()
+ mock_ssh.SSH().execute.return_value = (0, sample_output, '')
+ self.assertRaises(AssertionError, p.run, args)
+
+ def test_iperf_unsuccessful_script_error(self, mock_ssh):
+
+ p = iperf3.Iperf(self.ctx)
+ mock_ssh.SSH().execute.return_value = (0, '', '')
+ p.host = mock_ssh.SSH()
+
+ options = {}
+ args = {'options': options}
+
+ mock_ssh.SSH().execute.return_value = (1, '', 'FOOBAR')
+ self.assertRaises(RuntimeError, p.run, args)
+
+ def _read_sample_output(self):
+ curr_path = os.path.dirname(os.path.abspath(__file__))
+ output = os.path.join(curr_path, 'iperf3_sample_output.json')
+ with open(output) as f:
+ sample_output = f.read()
+ return sample_output
+
+
+def main():
+ unittest.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/unit/benchmark/scenarios/storage/__init__.py b/tests/unit/benchmark/scenarios/storage/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/unit/benchmark/scenarios/storage/__init__.py
diff --git a/tests/unit/benchmark/scenarios/storage/test_fio.py b/tests/unit/benchmark/scenarios/storage/test_fio.py
new file mode 100644
index 000000000..54f493ee6
--- /dev/null
+++ b/tests/unit/benchmark/scenarios/storage/test_fio.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python
+
+##############################################################################
+# Copyright (c) 2015 Ericsson AB and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+# Unittest for yardstick.benchmark.scenarios.storage.fio.Fio
+
+import mock
+import unittest
+import json
+
+from yardstick.benchmark.scenarios.storage import fio
+
+
+@mock.patch('yardstick.benchmark.scenarios.storage.fio.ssh')
+class FioTestCase(unittest.TestCase):
+
+ def setUp(self):
+ self.ctx = {
+ 'host': '172.16.0.137',
+ 'user': 'cirros',
+ 'key_filename': "mykey.key"
+ }
+
+ def test_fio_successful_setup(self, mock_ssh):
+
+ p = fio.Fio(self.ctx)
+ options = {
+ 'filename': "/home/ec2-user/data.raw",
+ 'bs': "4k",
+ 'rw': "write",
+ 'ramp_time': 10
+ }
+ args = {'options': options}
+ p.setup()
+
+ mock_ssh.SSH().execute.return_value = (0, '', '')
+ self.assertIsNotNone(p.client)
+ self.assertEqual(p.setup_done, True)
+
+ def test_fio_successful_no_sla(self, mock_ssh):
+
+ p = fio.Fio(self.ctx)
+ options = {
+ 'filename': "/home/ec2-user/data.raw",
+ 'bs': "4k",
+ 'rw': "write",
+ 'ramp_time': 10
+ }
+ args = {'options': options}
+ p.client = mock_ssh.SSH()
+
+ sample_output = '{"read_bw": "N/A", "write_lat": "407.08usec", \
+ "read_iops": "N/A", "write_bw": "9507KB/s", \
+ "write_iops": "2376", "read_lat": "N/A"}'
+ mock_ssh.SSH().execute.return_value = (0, sample_output, '')
+
+ result = p.run(args)
+ expected_result = json.loads(sample_output)
+ self.assertEqual(result, expected_result)
+
+ def test_fio_unsuccessful_script_error(self, mock_ssh):
+
+ p = fio.Fio(self.ctx)
+ options = {
+ 'filename': "/home/ec2-user/data.raw",
+ 'bs': "4k",
+ 'rw': "write",
+ 'ramp_time': 10
+ }
+ args = {'options': options}
+ p.client = mock_ssh.SSH()
+
+ mock_ssh.SSH().execute.return_value = (1, '', 'FOOBAR')
+ self.assertRaises(RuntimeError, p.run, args)
+
+
+def main():
+ unittest.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/unit/common/test_utils.py b/tests/unit/common/test_utils.py
new file mode 100644
index 000000000..002d0494c
--- /dev/null
+++ b/tests/unit/common/test_utils.py
@@ -0,0 +1,90 @@
+##############################################################################
+# Copyright (c) 2015 Ericsson AB and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+# Unittest for yardstick.common.utils
+
+import os
+import mock
+import unittest
+
+from yardstick.common import utils
+
+
+class IterSubclassesTestCase(unittest.TestCase):
+# Disclaimer: this class is a modified copy from
+# rally/tests/unit/common/plugin/test_discover.py
+# Copyright 2015: Mirantis Inc.
+ def test_itersubclasses(self):
+ class A(object):
+ pass
+
+ class B(A):
+ pass
+
+ class C(A):
+ pass
+
+ class D(C):
+ pass
+
+ self.assertEqual([B, C, D], list(utils.itersubclasses(A)))
+
+
+class TryAppendModuleTestCase(unittest.TestCase):
+
+ @mock.patch('yardstick.common.utils.importutils')
+ def test_try_append_module_not_in_modules(self, mock_importutils):
+
+ modules = {}
+ name = 'foo'
+ utils.try_append_module(name, modules)
+ mock_importutils.import_module.assert_called_with(name)
+
+ @mock.patch('yardstick.common.utils.importutils')
+ def test_try_append_module_already_in_modules(self, mock_importutils):
+
+ modules = {'foo'}
+ name = 'foo'
+ utils.try_append_module(name, modules)
+ self.assertFalse(mock_importutils.import_module.called)
+
+
+class ImportModulesFromPackageTestCase(unittest.TestCase):
+
+ @mock.patch('yardstick.common.utils.os.walk')
+ @mock.patch('yardstick.common.utils.try_append_module')
+ def test_import_modules_from_package_no_mod(self, mock_append, mock_walk):
+
+ sep = os.sep
+ mock_walk.return_value = ([
+ ('..' + sep + 'foo', ['bar'], ['__init__.py']),
+ ('..' + sep + 'foo' + sep + 'bar', [], ['baz.txt', 'qux.rst'])
+ ])
+
+ utils.import_modules_from_package('foo.bar')
+ self.assertFalse(mock_append.called)
+
+ @mock.patch('yardstick.common.utils.os.walk')
+ @mock.patch('yardstick.common.utils.importutils')
+ def test_import_modules_from_package(self, mock_importutils, mock_walk):
+
+ sep = os.sep
+ mock_walk.return_value = ([
+ ('foo' + sep + '..' + sep + 'bar', [], ['baz.py'])
+ ])
+
+ utils.import_modules_from_package('foo.bar')
+ mock_importutils.import_module.assert_called_with('bar.baz')
+
+
+def main():
+ unittest.main()
+
+if __name__ == '__main__':
+ main()
diff --git a/tools/ubuntu-server-cloudimg-modify.sh b/tools/ubuntu-server-cloudimg-modify.sh
index 41d654a08..ac43d28f8 100755
--- a/tools/ubuntu-server-cloudimg-modify.sh
+++ b/tools/ubuntu-server-cloudimg-modify.sh
@@ -25,6 +25,13 @@ fi
# iperf3 only available for trusty in backports
grep trusty /etc/apt/sources.list && \
echo "deb http://archive.ubuntu.com/ubuntu/ trusty-backports main restricted universe multiverse" >> /etc/apt/sources.list
+
+# Workaround for building on CentOS (apt-get is not working with http sources)
+sed -i 's/http/ftp/' /etc/apt/sources.list
+
+# Force apt to use ipv4 due to build problems on LF POD.
+echo 'Acquire::ForceIPv4 "true";' > /etc/apt/apt.conf.d/99force-ipv4
+
apt-get update
apt-get install -y \
fio \
diff --git a/tools/yardstick-img-modify b/tools/yardstick-img-modify
index eba85473c..c90027213 100755
--- a/tools/yardstick-img-modify
+++ b/tools/yardstick-img-modify
@@ -66,15 +66,22 @@ download() {
cd -
}
-# mount image using qemu-nbd
+# mount image
setup() {
- modprobe nbd max_part=16
- qemu-nbd -c /dev/nbd0 $imgfile
- partprobe /dev/nbd0
-
mkdir -p $mountdir
- mount /dev/nbd0p1 $mountdir
-
+ if [ -f /etc/centos-release ]; then
+ # CentOS, mount image using guestmount.
+ # (needs libguestfs-tools installed)
+ export LIBGUESTFS_BACKEND=direct
+ guestmount -a $imgfile -i --rw $mountdir
+ else
+ # mount image using qemu-nbd
+ modprobe nbd max_part=16
+ qemu-nbd -c /dev/nbd0 $imgfile
+ partprobe /dev/nbd0
+
+ mount /dev/nbd0p1 $mountdir
+ fi
cp $cmd $mountdir/$(basename $cmd)
}
diff --git a/yardstick/benchmark/scenarios/storage/fio.py b/yardstick/benchmark/scenarios/storage/fio.py
index a39af06aa..4eaf8796a 100644
--- a/yardstick/benchmark/scenarios/storage/fio.py
+++ b/yardstick/benchmark/scenarios/storage/fio.py
@@ -29,6 +29,10 @@ class Fio(base.Scenario):
type: int
unit: bytes
default: 4k
+ iodepth - number of iobuffers to keep in flight
+ type: int
+ unit: na
+ default: 1
rw - type of io pattern [read, write, randwrite, randread, rw, randrw]
type: string
unit: na
@@ -79,6 +83,7 @@ class Fio(base.Scenario):
options = args["options"]
filename = options.get("filename", "/home/ec2-user/data.raw")
bs = options.get("bs", "4k")
+ iodepth = options.get("iodepth", "1")
rw = options.get("rw", "write")
ramp_time = options.get("ramp_time", 20)
name = "yardstick-fio"
@@ -93,10 +98,11 @@ class Fio(base.Scenario):
else:
runtime = 30
- args = "-filename=%s -bs=%s -rw=%s -ramp_time=%s -runtime=%s -name=%s" \
- % (filename, bs, rw, ramp_time, runtime, name)
+ args = "-filename=%s -bs=%s -iodepth=%s -rw=%s -ramp_time=%s " \
+ "-runtime=%s -name=%s" \
+ % (filename, bs, iodepth, rw, ramp_time, runtime, name)
cmd = "sudo bash fio.sh %s %s %s" \
- % (filename, args, default_args)
+ % (filename, args, default_args)
LOG.debug("Executing command: %s", cmd)
status, stdout, stderr = self.client.execute(cmd)
if status:
@@ -127,6 +133,7 @@ def _test():
options = {
"filename": "/home/ec2-user/data.raw",
"bs": "4k",
+ "iodepth": "1",
"rw": "write",
"ramp_time": 10,
}
diff --git a/yardstick/cmd/commands/task.py b/yardstick/cmd/commands/task.py
index 8b9f269c5..6e117edd0 100644..100755
--- a/yardstick/cmd/commands/task.py
+++ b/yardstick/cmd/commands/task.py
@@ -18,7 +18,7 @@ import ipaddress
from yardstick.benchmark.context.model import Context
from yardstick.benchmark.runners import base as base_runner
-
+from yardstick.common.task_template import TaskTemplate
from yardstick.common.utils import cliargs
output_file_default = "/tmp/yardstick.out"
@@ -31,6 +31,13 @@ class TaskCommands(object):
'''
@cliargs("taskfile", type=str, help="path to taskfile", nargs=1)
+ @cliargs("--task-args", dest="task_args",
+ help="Input task args (dict in json). These args are used"
+ "to render input task that is jinja2 template.")
+ @cliargs("--task-args-file", dest="task_args_file",
+ help="Path to the file with input task args (dict in "
+ "json/yaml). These args are used to render input"
+ "task that is jinja2 template.")
@cliargs("--keep-deploy", help="keep context deployed in cloud",
action="store_true")
@cliargs("--parse-only", help="parse the benchmark config file and exit",
@@ -43,7 +50,8 @@ class TaskCommands(object):
atexit.register(atexit_handler)
parser = TaskParser(args.taskfile[0])
- scenarios, run_in_parallel = parser.parse()
+ scenarios, run_in_parallel = parser.parse(args.task_args,
+ args.task_args_file)
if args.parse_only:
sys.exit(0)
@@ -80,20 +88,39 @@ class TaskCommands(object):
print "Done, exiting"
-
# TODO: Move stuff below into TaskCommands class !?
+
class TaskParser(object):
'''Parser for task config files in yaml format'''
def __init__(self, path):
self.path = path
- def parse(self):
+ def parse(self, task_args=None, task_args_file=None):
'''parses the task file and return an context and scenario instances'''
print "Parsing task config:", self.path
+
+ try:
+ kw = {}
+ if task_args_file:
+ with open(task_args_file) as f:
+ kw.update(parse_task_args("task_args_file", f.read()))
+ kw.update(parse_task_args("task_args", task_args))
+ except TypeError:
+ raise TypeError()
+
try:
- with open(self.path) as stream:
- cfg = yaml.load(stream)
+ with open(self.path) as f:
+ try:
+ input_task = f.read()
+ rendered_task = TaskTemplate.render(input_task, **kw)
+ except Exception as e:
+ print(("Failed to render template:\n%(task)s\n%(err)s\n")
+ % {"task": input_task, "err": e})
+ raise e
+ print(("Input task is:\n%s\n") % rendered_task)
+
+ cfg = yaml.load(rendered_task)
except IOError as ioerror:
sys.exit(ioerror)
@@ -108,6 +135,10 @@ class TaskParser(object):
context_cfgs = cfg["contexts"]
for cfg_attrs in context_cfgs:
+ # config external_network based on env var
+ for _, attrs in cfg_attrs["networks"].items():
+ attrs["external_network"] = os.environ.get('EXTERNAL_NETWORK',
+ 'net04_ext')
context = Context()
context.init(cfg_attrs)
@@ -181,3 +212,26 @@ def runner_join(runner):
base_runner.Runner.release(runner)
if status != 0:
sys.exit("Runner failed")
+
+
+def print_invalid_header(source_name, args):
+ print(("Invalid %(source)s passed:\n\n %(args)s\n")
+ % {"source": source_name, "args": args})
+
+
+def parse_task_args(src_name, args):
+ try:
+ kw = args and yaml.safe_load(args)
+ kw = {} if kw is None else kw
+ except yaml.parser.ParserError as e:
+ print_invalid_header(src_name, args)
+ print(("%(source)s has to be YAML. Details:\n\n%(err)s\n")
+ % {"source": src_name, "err": e})
+ raise TypeError()
+
+ if not isinstance(kw, dict):
+ print_invalid_header(src_name, args)
+ print(("%(src)s had to be dict, actually %(src_type)s\n")
+ % {"src": src_name, "src_type": type(kw)})
+ raise TypeError()
+ return kw
diff --git a/yardstick/common/task_template.py b/yardstick/common/task_template.py
new file mode 100755
index 000000000..2739323bd
--- /dev/null
+++ b/yardstick/common/task_template.py
@@ -0,0 +1,53 @@
+##############################################################################
+# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+# yardstick: this file is copied from rally and slightly modified
+##############################################################################
+import re
+import jinja2
+import jinja2.meta
+
+
+class TaskTemplate(object):
+ @classmethod
+ def render(cls, task_template, **kwargs):
+ """Render jinja2 task template to Yardstick input task.
+
+ :param task_template: string that contains template
+ :param kwargs: Dict with template arguments
+ :returns:rendered template str
+ """
+
+ from six.moves import builtins
+
+ ast = jinja2.Environment().parse(task_template)
+ required_kwargs = jinja2.meta.find_undeclared_variables(ast)
+
+ missing = set(required_kwargs) - set(kwargs) - set(dir(builtins))
+ real_missing = [mis for mis in missing
+ if is_really_missing(mis, task_template)]
+
+ if real_missing:
+ multi_msg = ("Please specify next template task arguments:%s")
+ single_msg = ("Please specify template task argument:%s")
+ raise TypeError((len(real_missing) > 1 and multi_msg or single_msg)
+ % ", ".join(real_missing))
+ return jinja2.Template(task_template).render(**kwargs)
+
+
+def is_really_missing(mis, task_template):
+ # Removing variables that have default values from
+ # missing. Construction that won't be properly
+ # check is {% set x = x or 1}
+ if re.search(mis.join(["{%\s*set\s+", "\s*=\s*", "[^\w]+"]),
+ task_template):
+ return False
+ # Also check for a default filter which can show up as
+ # a missing variable
+ if re.search(mis + "\s*\|\s*default\(", task_template):
+ return False
+ return True