diff options
author | RajithaY <rajithax.yerrumsetty@intel.com> | 2017-07-07 05:18:56 -0700 |
---|---|---|
committer | Rajitha Yerrumchetty <rajithax.yerrumsetty@intel.com> | 2017-07-08 15:16:45 +0000 |
commit | a9d81a2bd9c3826e000d99be67a7a726278e3d46 (patch) | |
tree | 8f721d557bba9aa15012569eaee9c92b628e5a5e | |
parent | 3369461d0e51b22aa96e5bfd0d80b3f0f7f82c67 (diff) |
Testing live migration using qemu
This patch includes the scripts to execute the live migration using
qemu and provide the information total time ,VM downtime,setuptime
once live migration is completed
Signed-off-by:RajithaY<rajithax.yerrumsetty@intel.com>
Change-Id: I61380f757f6f3a852fe0a7bc566b10753ce0cc53
-rw-r--r-- | samples/migrate-node-context.yaml | 40 | ||||
-rw-r--r-- | tests/unit/benchmark/scenarios/compute/test_qemumigrate.py | 166 | ||||
-rw-r--r-- | yardstick/benchmark/scenarios/compute/qemu_migrate.py | 155 | ||||
-rw-r--r-- | yardstick/benchmark/scenarios/compute/qemu_migrate_benchmark.bash | 68 |
4 files changed, 429 insertions, 0 deletions
diff --git a/samples/migrate-node-context.yaml b/samples/migrate-node-context.yaml new file mode 100644 index 000000000..9fe1acf78 --- /dev/null +++ b/samples/migrate-node-context.yaml @@ -0,0 +1,40 @@ +--- + +schema: "yardstick:task:0.1" + +scenarios: +- + type: QemuMigrate + options: + smp: 2 + migrate_to_port: 4444 + incoming_ip: 0 + qmp_src_path: "/tmp/qmp-sock-src" + qmp_dst_path: "/tmp/qmp-sock-dst" + max_down_time: "0.10" + host: kvm.LF + runner: + type: Duration + duration: 1 + interval: 1 + sla: + max_totaltime: 10 + max_downtime: 0.10 + max_setuptime: 0.50 + action: monitor + setup_options: + rpm_dir: "/opt/rpm" + script_dir: "/opt/scripts" + image_dir: "/opt/image" + host_setup_seqs: + - "host-setup0.sh" + - "reboot" + - "host-setup1.sh" + - "setup-ovsdpdk.sh" + - "host-install-qemu.sh" + - "host-run-qemu4lm.sh" + +context: + type: Node + name: LF + file: /root/yardstick/pod.yaml diff --git a/tests/unit/benchmark/scenarios/compute/test_qemumigrate.py b/tests/unit/benchmark/scenarios/compute/test_qemumigrate.py new file mode 100644 index 000000000..9514729ba --- /dev/null +++ b/tests/unit/benchmark/scenarios/compute/test_qemumigrate.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python + +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and other. +# +# 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.compute.qemu_migrate.QemuMigrate + +from __future__ import absolute_import + +import unittest + +import mock +from oslo_serialization import jsonutils + +from yardstick.benchmark.scenarios.compute import qemu_migrate + + +@mock.patch('yardstick.benchmark.scenarios.compute.qemu_migrate.ssh') +class QemuMigrateTestCase(unittest.TestCase): + + def setUp(self): + self.scenario_cfg = { + "host": "kvm.LF", + "setup_options": { + "rpm_dir": "/opt/rpm", + "script_dir": "/opt/scripts", + "image_dir": "/opt/image", + "host_setup_seqs": [ + "host-setup0.sh", + "host-setup1.sh", + "setup-ovsdpdk.sh", + "host-install-qemu.sh", + "host-run-qemu4lm.sh" + ] + }, + "sla": { + "action": "monitor", + "max_totaltime": 10, + "max_downtime": 0.10, + "max_setuptime": 0.50 + }, + "options": { + "smp": 99, + "migrate_to_port": 4444, + "incoming_ip": 0, + "qmp_src_path": "/tmp/qmp-sock-src", + "qmp_dst_path": "/tmp/qmp-sock-dst", + "max_down_time": "0.10" + } + } + self.context_cfg = { + "host": { + "ip": "10.229.43.154", + "key_filename": "/yardstick/resources/files/yardstick_key", + "role": "BareMetal", + "name": "kvm.LF", + "user": "root" + } + } + + def test_qemu_migrate_successful_setup(self, mock_ssh): + + q = qemu_migrate.QemuMigrate(self.scenario_cfg, self.context_cfg) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + + q.setup() + self.assertIsNotNone(q.host) + self.assertEqual(q.setup_done, True) + + def test_qemu_migrate_successful_no_sla(self, mock_ssh): + result = {} + self.scenario_cfg.pop("sla", None) + q = qemu_migrate.QemuMigrate(self.scenario_cfg, self.context_cfg) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + q.setup() + + sample_output = '{"totaltime": 15, "downtime": 2, "setuptime": 1}' + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + + q.run(result) + expected_result = jsonutils.loads(sample_output) + self.assertEqual(result, expected_result) + + def test_qemu_migrate_successful_sla(self, mock_ssh): + result = {} + self.scenario_cfg.update({"sla": { + "action": "monitor", + "max_totaltime": 15, + "max_downtime": 2, + "max_setuptime": 1 + } + }) + q = qemu_migrate.QemuMigrate(self.scenario_cfg, self.context_cfg) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + q.setup() + + sample_output = '{"totaltime": 15, "downtime": 2, "setuptime": 1}' + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + + q.run(result) + expected_result = jsonutils.loads(sample_output) + self.assertEqual(result, expected_result) + + def test_qemu_migrate_unsuccessful_sla_totaltime(self, mock_ssh): + + result = {} + self.scenario_cfg.update({"sla": {"max_totaltime": 10}}) + q = qemu_migrate.QemuMigrate(self.scenario_cfg, self.context_cfg) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + q.setup() + + sample_output = '{"totaltime": 15, "downtime": 2, "setuptime": 1}' + + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + self.assertRaises(AssertionError, q.run, result) + + def test_qemu_migrate_unsuccessful_sla_downtime(self, mock_ssh): + + result = {} + self.scenario_cfg.update({"sla": {"max_downtime": 0.10}}) + q = qemu_migrate.QemuMigrate(self.scenario_cfg, self.context_cfg) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + q.setup() + + sample_output = '{"totaltime": 15, "downtime": 2, "setuptime": 1}' + + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + self.assertRaises(AssertionError, q.run, result) + + def test_qemu_migrate_unsuccessful_sla_setuptime(self, mock_ssh): + + result = {} + self.scenario_cfg.update({"sla": {"max_setuptime": 0.50}}) + q = qemu_migrate.QemuMigrate(self.scenario_cfg, self.context_cfg) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + q.setup() + + sample_output = '{"totaltime": 15, "downtime": 2, "setuptime": 1}' + + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + self.assertRaises(AssertionError, q.run, result) + + def test_qemu_migrate_unsuccessful_script_error(self, mock_ssh): + + result = {} + self.scenario_cfg.update({"sla": {"max_totaltime": 10}}) + q = qemu_migrate.QemuMigrate(self.scenario_cfg, self.context_cfg) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + q.setup() + + + mock_ssh.SSH.from_node().execute.return_value = (1, '', 'FOOBAR') + self.assertRaises(RuntimeError, q.run, result) + + +def main(): + unittest.main() + +if __name__ == '__main__': + main() diff --git a/yardstick/benchmark/scenarios/compute/qemu_migrate.py b/yardstick/benchmark/scenarios/compute/qemu_migrate.py new file mode 100644 index 000000000..cee87a545 --- /dev/null +++ b/yardstick/benchmark/scenarios/compute/qemu_migrate.py @@ -0,0 +1,155 @@ +from __future__ import absolute_import +from __future__ import print_function + +import logging +import os +import re +import time + + +import pkg_resources +from oslo_serialization import jsonutils + +import yardstick.ssh as ssh +from yardstick.benchmark.scenarios import base + +LOG = logging.getLogger(__name__) +LOG.setLevel(logging.DEBUG) + + +class QemuMigrate(base.Scenario): + """ + Execute a live migration for two host using qemu + + """ + + __scenario_type__ = "QemuMigrate" + + TARGET_SCRIPT = "qemu_migrate_benchmark.bash" + WORKSPACE = "/root/workspace" + REBOOT_CMD_PATTERN = r";\s*reboot\b" + + def __init__(self, scenario_cfg, context_cfg): + self.scenario_cfg = scenario_cfg + self.context_cfg = context_cfg + self.setup_done = False + + def _connect_host(self): + host = self.context_cfg["host"] + self.host = ssh.SSH.from_node(host, defaults={"user": "root"}) + self.host.wait(timeout=600) + + def _put_files(self, client): + setup_options = self.scenario_cfg["setup_options"] + script_dir = setup_options["script_dir"] + LOG.debug("Send scripts from %s to workspace %s", + script_dir, self.WORKSPACE) + client.put(script_dir, self.WORKSPACE, recursive=True) + + def _run_setup_cmd(self, client, cmd): + LOG.debug("Run cmd: %s", cmd) + status, stdout, stderr = client.execute(cmd) + if status: + if re.search(self.REBOOT_CMD_PATTERN, cmd): + LOG.debug("Error on reboot") + else: + raise RuntimeError(stderr) + + def _run_host_setup_scripts(self, scripts): + setup_options = self.scenario_cfg["setup_options"] + script_dir = os.path.basename(setup_options["script_dir"]) + + for script in scripts: + cmd = "cd %s/%s; export PATH=./:$PATH; %s" %\ + (self.WORKSPACE, script_dir, script) + self._run_setup_cmd(self.host, cmd) + + if re.search(self.REBOOT_CMD_PATTERN, cmd): + time.sleep(3) + self._connect_host() + + def setup(self): + """scenario setup""" + setup_options = self.scenario_cfg["setup_options"] + host_setup_seqs = setup_options["host_setup_seqs"] + + self._connect_host() + self._put_files(self.host) + self._run_host_setup_scripts(host_setup_seqs) + + # copy script to host + self.target_script = pkg_resources.resource_filename( + "yardstick.benchmark.scenarios.compute", + QemuMigrate.TARGET_SCRIPT) + self.host.put_file(self.target_script, "~/qemu_migrate_benchmark.sh") + + self.setup_done = True + + def run(self, result): + """execute the benchmark""" + + options = self.scenario_cfg["options"] + smp = options.get("smp", 2) + qmp_sock_src = options.get("qmp_src_path", "/tmp/qmp-sock-src") + qmp_sock_dst = options.get("qmp_dst_path", "/tmp/qmp-sock-dst") + incoming_ip = options.get("incoming_ip", 0) + migrate_to_port = options.get("migrate_to_port", 4444) + max_down_time = options.get("max_down_time", 0.10) + cmd_args = " %s %s %s %s %s %s" %\ + (smp, qmp_sock_src, qmp_sock_dst, incoming_ip, + migrate_to_port, max_down_time) + cmd = "bash migrate_benchmark.sh %s" % (cmd_args) + LOG.debug("Executing command: %s", cmd) + status, stdout, stderr = self.host.execute(cmd) + if status: + raise RuntimeError(stderr) + + result.update(jsonutils.loads(stdout)) + + if "sla" in self.scenario_cfg: + sla_error = "" + for t, timevalue in result.items(): + if 'max_%s' % t not in self.scenario_cfg['sla']: + continue + + sla_time = int(self.scenario_cfg['sla'][ + 'max_%s' % t]) + timevalue = int(timevalue) + if timevalue > sla_time: + sla_error += "%s timevalue %d > sla:max_%s(%d); " % \ + (t, timevalue, t, sla_time) + assert sla_error == "", sla_error + + +def _test(): # pragma: no cover + """internal test function""" + key_filename = pkg_resources.resource_filename("yardstick.resources", + "files/yardstick_key") + ctx = { + "host": { + "ip": "10.229.47.137", + "user": "root", + "key_filename": key_filename + } + } + + logger = logging.getLogger("yardstick") + logger.setLevel(logging.DEBUG) + options = { + "smp": 2, + "migrate_to_port": 4444, + "incoming_ip": 0, + "qmp_sock_src": "/tmp/qmp-sock-src", + "qmp_sock_dst": "/tmp/qmp-sock-dst", + "max_down_time": 0.10 + } + args = { + "options": options + } + result = {} + migrate = QemuMigrate(args, ctx) + migrate.run(result) + print(result) + +if __name__ == '__main__': # pragma: no cover + _test() diff --git a/yardstick/benchmark/scenarios/compute/qemu_migrate_benchmark.bash b/yardstick/benchmark/scenarios/compute/qemu_migrate_benchmark.bash new file mode 100644 index 000000000..552098103 --- /dev/null +++ b/yardstick/benchmark/scenarios/compute/qemu_migrate_benchmark.bash @@ -0,0 +1,68 @@ +#!/bin/bash + +############################################################################# +#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 +############################################################################## + +set -e + +# Commandline arguments + +src=$2 +dst_ip=$4 +migrate_to_port=$5 +max_down_time=$6 + +OUTPUT_FILE=/tmp/output-qemu.log + +do_migrate() +{ +# local src=`echo $OPTIONS | cut -d ':' -f 2 | cut -d ',' -f 1` + echo "info status" | nc -U $src + # with no speed limit + echo "migrate_set_speed 0" |nc -U $src + # set the expected max downtime + echo "migrate_set_downtime ${max_down_time}" |nc -U $src + # start live migration + echo "migrate -d tcp:${dst_ip}:$migrate_to_port" |nc -U $src + # wait until live migration completed + status="" + while [ "${status}" == "" ] + do + status=`echo "info migrate" | nc -U $src |grep completed | cut -d: -f2` + echo ${status} + sleep 1; + done +} >/dev/null + +output_qemu() +{ + # print detail information + echo "info migrate" | nc -U $src + echo "quit" | nc -U $src + sleep 5 + +} > $OUTPUT_FILE + +output_json() +{ +totaltime=$(grep "total time" $OUTPUT_FILE | cut -d' ' -f3) +downtime=$(grep "downtime" $OUTPUT_FILE | cut -d' ' -f2) +setuptime=$(grep "setup" $OUTPUT_FILE | cut -d' ' -f2) +echo -e "{ \ + \"totaltime\":\"$totaltime\", \ + \"downtime\":\"$downtime\", \ + \"setuptime\":\"$setuptime\" \ + }" +} +# main entry +main() +{ + do_migrate +} +main |