Testing live migration using qemu 17/36417/20
authorRajithaY <rajithax.yerrumsetty@intel.com>
Fri, 7 Jul 2017 12:18:56 +0000 (05:18 -0700)
committerRajitha Yerrumchetty <rajithax.yerrumsetty@intel.com>
Sat, 8 Jul 2017 15:16:45 +0000 (15:16 +0000)
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

samples/migrate-node-context.yaml [new file with mode: 0644]
tests/unit/benchmark/scenarios/compute/test_qemumigrate.py [new file with mode: 0644]
yardstick/benchmark/scenarios/compute/qemu_migrate.py [new file with mode: 0644]
yardstick/benchmark/scenarios/compute/qemu_migrate_benchmark.bash [new file with mode: 0644]

diff --git a/samples/migrate-node-context.yaml b/samples/migrate-node-context.yaml
new file mode 100644 (file)
index 0000000..9fe1acf
--- /dev/null
@@ -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 (file)
index 0000000..9514729
--- /dev/null
@@ -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 (file)
index 0000000..cee87a5
--- /dev/null
@@ -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 (file)
index 0000000..5520981
--- /dev/null
@@ -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