[Plugins] Improve scenario output from boot_runcommand_delete
Scenario VMTasks.boot_runcommand_delete now can populate report tab "Scenario Data" with any allowed charts. SSH command return value structure new reflects Scenario.add_output() arguments. Also, there is new shell script samples/tasks/support/instance_test.sh which generates different load on server and creates proper JSON output. Old format of command output is also supported but marked as deprecated. Change-Id: I6a34b634ea7d9080f9ca08bf0f82d04fa69406d9
This commit is contained in:
parent
057fb25bba
commit
ca4f489b9e
98
rally-jobs/extra/instance_test.sh
Normal file
98
rally-jobs/extra/instance_test.sh
Normal file
@ -0,0 +1,98 @@
|
||||
#!/bin/sh
|
||||
# Load server and output JSON results ready to be processed
|
||||
# by Rally scenario
|
||||
|
||||
get_used_cpu_percent() {
|
||||
echo 100 $(top -b -n 1 | grep -i CPU | head -n 1 | awk '{print $8}' | tr -d %) - p | dc
|
||||
}
|
||||
|
||||
get_used_ram_percent() {
|
||||
local total=$(free | grep Mem: | awk '{print $2}')
|
||||
local used=$(free | grep -- -/+\ buffers | awk '{print $3}')
|
||||
echo ${used} 100 \* ${total} / p | dc
|
||||
}
|
||||
|
||||
get_used_disk_percent() {
|
||||
df -P / | grep -v Filesystem | awk '{print $5}' | tr -d %
|
||||
}
|
||||
|
||||
get_seconds() {
|
||||
(time -p ${1}) 2>&1 | awk '/real/{print $2}'
|
||||
}
|
||||
|
||||
complete_load() {
|
||||
local script_file=${LOAD_SCRIPT_FILE:-/tmp/load.sh}
|
||||
local stop_file=${LOAD_STOP_FILE:-/tmp/load.stop}
|
||||
local processes_num=${LOAD_PROCESSES_COUNT:-20}
|
||||
local size=${LOAD_SIZE_MB:-5}
|
||||
|
||||
cat << EOF > ${script_file}
|
||||
until test -e ${stop_file}
|
||||
do dd if=/dev/urandom bs=1M count=${size} 2>/dev/null | gzip >/dev/null ; done
|
||||
EOF
|
||||
|
||||
local sep
|
||||
local cpu
|
||||
local ram
|
||||
local dis
|
||||
rm -f ${stop_file}
|
||||
for i in $(seq ${processes_num})
|
||||
do
|
||||
i=$((i-1))
|
||||
sh ${script_file} &
|
||||
cpu="${cpu}${sep}[${i}, $(get_used_cpu_percent)]"
|
||||
ram="${ram}${sep}[${i}, $(get_used_ram_percent)]"
|
||||
dis="${dis}${sep}[${i}, $(get_used_disk_percent)]"
|
||||
sep=", "
|
||||
done
|
||||
> ${stop_file}
|
||||
cat << EOF
|
||||
{
|
||||
"title": "Generate load by spawning processes",
|
||||
"description": "Each process runs gzip for ${size}M urandom data in a loop",
|
||||
"chart_plugin": "Lines",
|
||||
"axis_label": "Number of processes",
|
||||
"label": "Usage, %",
|
||||
"data": [
|
||||
["CPU", [${cpu}]],
|
||||
["Memory", [${ram}]],
|
||||
["Disk", [${dis}]]]
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
additive_dd() {
|
||||
local c=${1:-50} # Megabytes
|
||||
local file=/tmp/dd_test.img
|
||||
local write=$(get_seconds "dd if=/dev/urandom of=${file} bs=1M count=${c}")
|
||||
local read=$(get_seconds "dd if=${file} of=/dev/null bs=1M count=${c}")
|
||||
local gzip=$(get_seconds "gzip ${file}")
|
||||
rm ${file}.gz
|
||||
cat << EOF
|
||||
{
|
||||
"title": "Write, read and gzip file",
|
||||
"description": "Using file '${file}', size ${c}Mb.",
|
||||
"chart_plugin": "StackedArea",
|
||||
"data": [
|
||||
["write_${c}M", ${write}],
|
||||
["read_${c}M", ${read}],
|
||||
["gzip_${c}M", ${gzip}]]
|
||||
},
|
||||
{
|
||||
"title": "Statistics for write/read/gzip",
|
||||
"chart_plugin": "StatsTable",
|
||||
"data": [
|
||||
["write_${c}M", ${write}],
|
||||
["read_${c}M", ${read}],
|
||||
["gzip_${c}M", ${gzip}]]
|
||||
}
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
cat << EOF
|
||||
{
|
||||
"additive": [$(additive_dd)],
|
||||
"complete": [$(complete_load)]
|
||||
}
|
||||
EOF
|
@ -670,6 +670,28 @@
|
||||
max: 0
|
||||
|
||||
VMTasks.boot_runcommand_delete:
|
||||
-
|
||||
args:
|
||||
flavor:
|
||||
name: "m1.tiny"
|
||||
image:
|
||||
name: {{image_name}}
|
||||
command:
|
||||
script_file: "~/.rally/extra/instance_test.sh"
|
||||
interpreter: "/bin/sh"
|
||||
username: "cirros"
|
||||
runner:
|
||||
type: "constant"
|
||||
times: {{smoke or 4}}
|
||||
concurrency: {{smoke or 2}}
|
||||
context:
|
||||
users:
|
||||
tenants: {{smoke or 2}}
|
||||
users_per_tenant: {{smoke or 2}}
|
||||
network: {}
|
||||
sla:
|
||||
failure_rate:
|
||||
max: 0
|
||||
-
|
||||
args:
|
||||
flavor:
|
||||
@ -689,6 +711,9 @@
|
||||
tenants: {{smoke or 2}}
|
||||
users_per_tenant: {{smoke or 2}}
|
||||
network: {}
|
||||
sla:
|
||||
failure_rate:
|
||||
max: 0
|
||||
|
||||
VMTasks.boot_runcommand_delete_custom_image:
|
||||
-
|
||||
|
@ -190,21 +190,38 @@ class VMTasks(vm_utils.VMScenario):
|
||||
self._delete_server_with_fip(server, fip,
|
||||
force_delete=force_delete)
|
||||
|
||||
# NOTE(amaretskiy): command output should be in format:
|
||||
# {"key1": numeric_value, "key2": numeric_value, ...}
|
||||
output = None
|
||||
if type(data) == dict:
|
||||
if type(data) != dict:
|
||||
raise exceptions.ScriptError(
|
||||
"Command has returned data in unexpected format.\n"
|
||||
"Expected format: {"
|
||||
"\"additive\": [{chart data}, {chart data}, ...], "
|
||||
"\"complete\": [{chart data}, {chart data}, ...]}\n"
|
||||
"Actual data: %s" % data)
|
||||
|
||||
if set(data) - {"additive", "complete"}:
|
||||
LOG.warning(
|
||||
"Deprecated since Rally release 0.4.1: command has "
|
||||
"returned data in format {\"key\": <value>, ...}\n"
|
||||
"Expected format: {"
|
||||
"\"additive\": [{chart data}, {chart data}, ...], "
|
||||
"\"complete\": [{chart data}, {chart data}, ...]}")
|
||||
output = None
|
||||
try:
|
||||
output = [[str(k), float(v)] for k, v in data.items()]
|
||||
except (TypeError, ValueError):
|
||||
LOG.error(("Command has returned data in unexpected format.\n"
|
||||
"Expected format: {key1: numeric_value, "
|
||||
"key2: numeric_value, ...}.\n"
|
||||
"Actual data: %s" % data))
|
||||
if output:
|
||||
self.add_output(additive={"title": "Command output",
|
||||
"chart_plugin": "Lines",
|
||||
"data": output})
|
||||
raise exceptions.ScriptError(
|
||||
"Command has returned data in unexpected format.\n"
|
||||
"Expected format: {key1: <number>, "
|
||||
"key2: <number>, ...}.\n"
|
||||
"Actual data: %s" % data)
|
||||
if output:
|
||||
self.add_output(additive={"title": "Command output",
|
||||
"chart_plugin": "Lines",
|
||||
"data": output})
|
||||
else:
|
||||
for chart_type, charts in data.items():
|
||||
for chart in charts:
|
||||
self.add_output(**{chart_type: chart})
|
||||
|
||||
@types.convert(image={"type": "glance_image"},
|
||||
flavor={"type": "nova_flavor"})
|
||||
|
@ -13,7 +13,7 @@
|
||||
"force_delete": false,
|
||||
"command": {
|
||||
"interpreter": "/bin/sh",
|
||||
"script_file": "samples/tasks/support/instance_dd_test.sh"
|
||||
"script_file": "samples/tasks/support/instance_test.sh"
|
||||
},
|
||||
"username": "cirros"
|
||||
},
|
||||
|
@ -11,7 +11,7 @@
|
||||
force_delete: false
|
||||
command:
|
||||
interpreter: "/bin/sh"
|
||||
script_file: "samples/tasks/support/instance_dd_test.sh"
|
||||
script_file: "samples/tasks/support/instance_test.sh"
|
||||
username: "cirros"
|
||||
runner:
|
||||
type: "constant"
|
||||
|
98
samples/tasks/support/instance_test.sh
Normal file
98
samples/tasks/support/instance_test.sh
Normal file
@ -0,0 +1,98 @@
|
||||
#!/bin/sh
|
||||
# Load server and output JSON results ready to be processed
|
||||
# by Rally scenario
|
||||
|
||||
get_used_cpu_percent() {
|
||||
echo 100 $(top -b -n 1 | grep -i CPU | head -n 1 | awk '{print $8}' | tr -d %) - p | dc
|
||||
}
|
||||
|
||||
get_used_ram_percent() {
|
||||
local total=$(free | grep Mem: | awk '{print $2}')
|
||||
local used=$(free | grep -- -/+\ buffers | awk '{print $3}')
|
||||
echo ${used} 100 \* ${total} / p | dc
|
||||
}
|
||||
|
||||
get_used_disk_percent() {
|
||||
df -P / | grep -v Filesystem | awk '{print $5}' | tr -d %
|
||||
}
|
||||
|
||||
get_seconds() {
|
||||
(time -p ${1}) 2>&1 | awk '/real/{print $2}'
|
||||
}
|
||||
|
||||
complete_load() {
|
||||
local script_file=${LOAD_SCRIPT_FILE:-/tmp/load.sh}
|
||||
local stop_file=${LOAD_STOP_FILE:-/tmp/load.stop}
|
||||
local processes_num=${LOAD_PROCESSES_COUNT:-20}
|
||||
local size=${LOAD_SIZE_MB:-5}
|
||||
|
||||
cat << EOF > ${script_file}
|
||||
until test -e ${stop_file}
|
||||
do dd if=/dev/urandom bs=1M count=${size} 2>/dev/null | gzip >/dev/null ; done
|
||||
EOF
|
||||
|
||||
local sep
|
||||
local cpu
|
||||
local ram
|
||||
local dis
|
||||
rm -f ${stop_file}
|
||||
for i in $(seq ${processes_num})
|
||||
do
|
||||
i=$((i-1))
|
||||
sh ${script_file} &
|
||||
cpu="${cpu}${sep}[${i}, $(get_used_cpu_percent)]"
|
||||
ram="${ram}${sep}[${i}, $(get_used_ram_percent)]"
|
||||
dis="${dis}${sep}[${i}, $(get_used_disk_percent)]"
|
||||
sep=", "
|
||||
done
|
||||
> ${stop_file}
|
||||
cat << EOF
|
||||
{
|
||||
"title": "Generate load by spawning processes",
|
||||
"description": "Each process runs gzip for ${size}M urandom data in a loop",
|
||||
"chart_plugin": "Lines",
|
||||
"axis_label": "Number of processes",
|
||||
"label": "Usage, %",
|
||||
"data": [
|
||||
["CPU", [${cpu}]],
|
||||
["Memory", [${ram}]],
|
||||
["Disk", [${dis}]]]
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
additive_dd() {
|
||||
local c=${1:-50} # Megabytes
|
||||
local file=/tmp/dd_test.img
|
||||
local write=$(get_seconds "dd if=/dev/urandom of=${file} bs=1M count=${c}")
|
||||
local read=$(get_seconds "dd if=${file} of=/dev/null bs=1M count=${c}")
|
||||
local gzip=$(get_seconds "gzip ${file}")
|
||||
rm ${file}.gz
|
||||
cat << EOF
|
||||
{
|
||||
"title": "Write, read and gzip file",
|
||||
"description": "Using file '${file}', size ${c}Mb.",
|
||||
"chart_plugin": "StackedArea",
|
||||
"data": [
|
||||
["write_${c}M", ${write}],
|
||||
["read_${c}M", ${read}],
|
||||
["gzip_${c}M", ${gzip}]]
|
||||
},
|
||||
{
|
||||
"title": "Statistics for write/read/gzip",
|
||||
"chart_plugin": "StatsTable",
|
||||
"data": [
|
||||
["write_${c}M", ${write}],
|
||||
["read_${c}M", ${read}],
|
||||
["gzip_${c}M", ${gzip}]]
|
||||
}
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
cat << EOF
|
||||
{
|
||||
"additive": [$(additive_dd)],
|
||||
"complete": [$(complete_load)]
|
||||
}
|
||||
EOF
|
@ -13,6 +13,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import ddt
|
||||
import mock
|
||||
|
||||
from rally import exceptions
|
||||
@ -20,6 +21,7 @@ from rally.plugins.openstack.scenarios.vm import vmtasks
|
||||
from tests.unit import test
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class VMTasksTestCase(test.ScenarioTestCase):
|
||||
|
||||
def setUp(self):
|
||||
@ -35,7 +37,7 @@ class VMTasksTestCase(test.ScenarioTestCase):
|
||||
self.scenario._create_volume = mock.Mock(
|
||||
return_value=mock.Mock(id="foo_volume"))
|
||||
self.scenario._run_command = mock.MagicMock(
|
||||
return_value=(0, "\"foo_out\"", "foo_err"))
|
||||
return_value=(0, "{\"foo\": 42}", "foo_err"))
|
||||
self.scenario.add_output = mock.Mock()
|
||||
|
||||
def test_boot_runcommand_delete(self):
|
||||
@ -72,42 +74,57 @@ class VMTasksTestCase(test.ScenarioTestCase):
|
||||
additive={"title": "Command output", "chart_plugin": "Lines",
|
||||
"data": [["foo", 42.0]]})
|
||||
|
||||
def test_boot_runcommand_delete_command(self):
|
||||
self.scenario.boot_runcommand_delete(
|
||||
"foo_image", "foo_flavor",
|
||||
command={"remote_path": "foo"},
|
||||
username="foo_username",
|
||||
password="foo_password",
|
||||
use_floating_ip="use_fip",
|
||||
floating_network="ext_network",
|
||||
force_delete="foo_force",
|
||||
volume_args={"size": 16},
|
||||
foo_arg="foo_value")
|
||||
@ddt.data(
|
||||
{"output": (0, "", ""), "raises": exceptions.ScriptError},
|
||||
{"output": (0, "{\"foo\": 42}", ""),
|
||||
"expected": [{"additive": {"chart_plugin": "Lines",
|
||||
"data": [["foo", 42.0]],
|
||||
"title": "Command output"}}]},
|
||||
{"output": (1, "{\"foo\": 42}", ""), "raises": exceptions.ScriptError},
|
||||
{"output": ("", 1, ""), "raises": TypeError},
|
||||
{"output": (0, "{\"additive\": [1, 2]}", ""),
|
||||
"expected": [{"additive": 1}, {"additive": 2}]},
|
||||
{"output": (0, "{\"complete\": [3, 4]}", ""),
|
||||
"expected": [{"complete": 3}, {"complete": 4}]},
|
||||
{"output": (0, "{\"additive\": [1, 2], \"complete\": [3, 4]}", ""),
|
||||
"expected": [{"additive": 1}, {"additive": 2},
|
||||
{"complete": 3}, {"complete": 4}]}
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_boot_runcommand_delete_add_output(self, output,
|
||||
expected=None, raises=None):
|
||||
self.scenario._run_command.return_value = output
|
||||
kwargs = {"command": {"remote_path": "foo"},
|
||||
"username": "foo_username",
|
||||
"password": "foo_password",
|
||||
"use_floating_ip": "use_fip",
|
||||
"floating_network": "ext_network",
|
||||
"force_delete": "foo_force",
|
||||
"volume_args": {"size": 16},
|
||||
"foo_arg": "foo_value"}
|
||||
if raises:
|
||||
self.assertRaises(raises, self.scenario.boot_runcommand_delete,
|
||||
"foo_image", "foo_flavor", **kwargs)
|
||||
self.assertFalse(self.scenario.add_output.called)
|
||||
else:
|
||||
self.scenario.boot_runcommand_delete("foo_image", "foo_flavor",
|
||||
**kwargs)
|
||||
calls = [mock.call(**kw) for kw in expected]
|
||||
self.scenario.add_output.assert_has_calls(calls, any_order=True)
|
||||
|
||||
self.scenario._create_volume.assert_called_once_with(
|
||||
16, imageRef=None)
|
||||
self.scenario._boot_server_with_fip.assert_called_once_with(
|
||||
"foo_image", "foo_flavor", key_name="keypair_name",
|
||||
use_floating_ip="use_fip", floating_network="ext_network",
|
||||
block_device_mapping={"vdrally": "foo_volume:::1"},
|
||||
foo_arg="foo_value")
|
||||
self.scenario._create_volume.assert_called_once_with(
|
||||
16, imageRef=None)
|
||||
self.scenario._boot_server_with_fip.assert_called_once_with(
|
||||
"foo_image", "foo_flavor", key_name="keypair_name",
|
||||
use_floating_ip="use_fip", floating_network="ext_network",
|
||||
block_device_mapping={"vdrally": "foo_volume:::1"},
|
||||
foo_arg="foo_value")
|
||||
|
||||
self.scenario._run_command.assert_called_once_with(
|
||||
"foo_ip", 22, "foo_username", "foo_password",
|
||||
command={"remote_path": "foo"})
|
||||
self.scenario._delete_server_with_fip.assert_called_once_with(
|
||||
"foo_server", self.ip, force_delete="foo_force")
|
||||
|
||||
def test_boot_runcommand_delete_script_fails(self):
|
||||
self.scenario._run_command = mock.MagicMock(
|
||||
return_value=(1, "\"foo_out\"", "foo_err"))
|
||||
self.assertRaises(exceptions.ScriptError,
|
||||
self.scenario.boot_runcommand_delete,
|
||||
"foo_image", "foo_flavor", "foo_interpreter",
|
||||
"foo_script", "foo_username")
|
||||
self.scenario._delete_server_with_fip.assert_called_once_with(
|
||||
"foo_server", self.ip, force_delete=False)
|
||||
self.assertFalse(self.scenario.add_output.called)
|
||||
self.scenario._run_command.assert_called_once_with(
|
||||
"foo_ip", 22, "foo_username", "foo_password",
|
||||
command={"remote_path": "foo"})
|
||||
self.scenario._delete_server_with_fip.assert_called_once_with(
|
||||
"foo_server", self.ip, force_delete="foo_force")
|
||||
|
||||
def test_boot_runcommand_delete_command_timeouts(self):
|
||||
self.scenario._run_command.side_effect = exceptions.SSHTimeout()
|
||||
|
Loading…
Reference in New Issue
Block a user