[Kimchi-devel] [PATCH 3/3 v2] Live migration backend: unit tests

Aline Manera alinefm at linux.vnet.ibm.com
Wed Oct 28 20:17:10 UTC 2015


Please, also add a test to cover the API

More comments inline below:

On 28/10/2015 15:41, dhbarboza82 at gmail.com wrote:
> From: Daniel Henrique Barboza <dhbarboza82 at gmail.com>
>
> These unit tests requires an additional variable passed in the
> command line, otherwise it won't be possible to test the
> migration types. Example:
>
> $ sudo KIMCHI_LIVE_MIGRATION_TEST=1.1.1.1 ./run_tests.sh test_livemigration
>
> It also depends on the common pre-requisites of migration:
>
> - valid remote host
> - password-less root login at the remote host
>
> The unit test will silently skip if the KIMCHI_LIVE_MIGRATION_TEST is not
> defined to avoid tampering with the common usage of ./run_tests.sh script.
>
> Signed-off-by: Daniel Henrique Barboza <dhbarboza82 at gmail.com>
> ---
>   src/wok/plugins/kimchi/tests/test_livemigration.py | 282 +++++++++++++++++++++
>   1 file changed, 282 insertions(+)
>   create mode 100644 src/wok/plugins/kimchi/tests/test_livemigration.py
>
> diff --git a/src/wok/plugins/kimchi/tests/test_livemigration.py b/src/wok/plugins/kimchi/tests/test_livemigration.py
> new file mode 100644
> index 0000000..860c406
> --- /dev/null
> +++ b/src/wok/plugins/kimchi/tests/test_livemigration.py
> @@ -0,0 +1,282 @@
> +#
> +# Project Kimchi
> +#
> +# Copyright IBM, Corp. 2015
> +#
> +# This library is free software; you can redistribute it and/or
> +# modify it under the terms of the GNU Lesser General Public
> +# License as published by the Free Software Foundation; either
> +# version 2.1 of the License, or (at your option) any later version.
> +#
> +# This library is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +# Lesser General Public License for more details.
> +#
> +# You should have received a copy of the GNU Lesser General Public
> +# License along with this library; if not, write to the Free Software
> +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
> +
> +import libvirt
> +import os
> +import shutil
> +import unittest
> +
> +from wok.basemodel import Singleton
> +from wok.exception import OperationFailed
> +from wok.rollbackcontext import RollbackContext
> +from wok.utils import run_command
> +
> +
> +from wok.plugins.kimchi.model import model
> +from wok.plugins.kimchi.model.libvirtconnection import LibvirtConnection
> +from wok.plugins.kimchi.model.vms import VMModel
> +
> +import iso_gen
> +import utils
> +
> +
> +TMP_DIR = '/var/lib/kimchi/tests/'
> +UBUNTU_ISO = TMP_DIR + 'ubuntu14.04.iso'
> +KIMCHI_LIVE_MIGRATION_TEST = None
> +
> +
> +def setUpModule():
> +    if not os.path.exists(TMP_DIR):
> +        os.makedirs(TMP_DIR)
> +    iso_gen.construct_fake_iso(UBUNTU_ISO, True, '14.04', 'ubuntu')
> +    # Some FeatureTests functions depend on server to validate their result.
> +    # As CapabilitiesModel is a Singleton class it will get the first result
> +    # from FeatureTests which may be wrong when using the Model instance
> +    # directly - the case of this test_model.py
> +    # So clean Singleton instances to make sure to get the right result when
> +    # running the following tests.
> +    Singleton._instances = {}
> +
> +
> +def tearDownModule():
> +    shutil.rmtree(TMP_DIR)
> +
> +
> +def remoteserver_environment_defined():
> +    global KIMCHI_LIVE_MIGRATION_TEST
> +    KIMCHI_LIVE_MIGRATION_TEST = os.environ.get('KIMCHI_LIVE_MIGRATION_TEST')
> +    return KIMCHI_LIVE_MIGRATION_TEST is not None
> +
> +
> +def running_root_and_remoteserver_defined():
> +    return utils.running_as_root() and remoteserver_environment_defined()
> +
> +
> +def check_if_vm_migration_test_possible():
> +    inst = model.Model(objstore_loc='/tmp/kimchi-store-test')
> +    try:
> +        inst.vm_migration_pre_check(KIMCHI_LIVE_MIGRATION_TEST, 'root')
> +    except:
> +        return False
> +    return True
> +
> +
> + at unittest.skipUnless(running_root_and_remoteserver_defined(),
> +                     'Must be run as root and with a remote server '
> +                     'defined in the KIMCHI_LIVE_MIGRATION_TEST variable')
> +class LiveMigrationTests(unittest.TestCase):
> +    def setUp(self):
> +        self.tmp_store = '/tmp/kimchi-store-test'
> +        self.inst = model.Model(objstore_loc=self.tmp_store)
> +        params = {'name': u'template_test_vm_migrate',
> +                  'disks': [],
> +                  'cdrom': UBUNTU_ISO,
> +                  'memory': 2048,
> +                  'max_memory': 4096*1024}
> +        self.inst.templates_create(params)
> +
> +    def tearDown(self):
> +        self.inst.template_delete('template_test_vm_migrate')
> +

> +        # FIXME: Tests using 'test:///default' URI should be moved to
> +        # test_rest or test_mockmodel to avoid overriding problems
> +        LibvirtConnection._connections['test:///default'] = {}
> +

The above lines are not needed as you don't use the test:///default 
drive in this test case.

> +        os.unlink(self.tmp_store)
> +
> +    def create_vm_test(self):
> +        params = {
> +            'name': u'test_vm_migrate',
> +            'template': u'/plugins/kimchi/templates/template_test_vm_migrate'
> +        }
> +        task = self.inst.vms_create(params)
> +        self.inst.task_wait(task['id'])
> +
> +    def test_vm_migrate_fails_if_remote_is_localhost(self):
> +        with RollbackContext() as rollback:
> +            self.create_vm_test()
> +            rollback.prependDefer(utils.rollback_wrapper, self.inst.vm_delete,
> +                                  u'test_vm_migrate')
> +
> +            self.assertRaises(OperationFailed,
> +                              self.inst.vm_migrate,
> +                              'test_vm_migrate',
> +                              '127.0.0.1')
> +
> +            self.assertRaises(OperationFailed,
> +                              self.inst.vm_migrate,
> +                              'test_vm_migrate',
> +                              'localhost')
> +
> +            hostname, _, _ = run_command(['hostname'], 5, silent=True)
> +
> +            self.assertRaises(OperationFailed,
> +                              self.inst.vm_migrate,
> +                              'test_vm_migrate',
> +                              hostname)
> +
> +    def test_vm_migrate_fails_if_remotehost_unreachable(self):
> +        with RollbackContext() as rollback:
> +            self.create_vm_test()
> +            rollback.prependDefer(utils.rollback_wrapper, self.inst.vm_delete,
> +                                  u'test_vm_migrate')
> +
> +            self.assertRaises(OperationFailed,
> +                              self.inst.vm_migrate,
> +                              'test_vm_migrate',
> +                              'test.vm.migrate.host.unreachable')
> +
> +    def test_vm_migrate_fails_if_not_passwordless_login(self):
> +        with RollbackContext() as rollback:
> +            self.create_vm_test()
> +            rollback.prependDefer(utils.rollback_wrapper, self.inst.vm_delete,
> +                                  u'test_vm_migrate')
> +
> +            self.assertRaises(OperationFailed,
> +                              self.inst.vm_migrate,
> +                              'test_vm_migrate',
> +                              KIMCHI_LIVE_MIGRATION_TEST,
> +                              user='test_vm_migrate_fake_user')
> +
> +    def get_remote_conn(self):
> +        remote_uri = 'qemu+ssh://%s@%s/system' % \
> +            ('root', KIMCHI_LIVE_MIGRATION_TEST)
> +        remote_conn = libvirt.open(remote_uri)
> +        return remote_conn
> +
> +    def get_remote_vm_list(self):
> +        remote_uri = 'qemu+ssh://%s@%s/system' % \
> +            ('root', KIMCHI_LIVE_MIGRATION_TEST)
> +        remote_conn = libvirt.open(remote_uri)
> +        return [vm.name() for vm in remote_conn.listAllDomains()]
> +
> +    @unittest.skipUnless(check_if_vm_migration_test_possible(),
> +                         'not possible to test a live migration')
> +    def test_vm_livemigrate_persistent(self):
> +        inst = model.Model(libvirt_uri='qemu:///system',
> +                           objstore_loc=self.tmp_store)
> +
> +        with RollbackContext() as rollback:
> +            self.create_vm_test()
> +            rollback.prependDefer(utils.rollback_wrapper, self.inst.vm_delete,
> +                                  u'test_vm_migrate')
> +
> +            # removing cdrom because it is not shared storage and will make
> +            # the migration fail
> +            dev_list = self.inst.vmstorages_get_list('test_vm_migrate')
> +            self.inst.vmstorage_delete('test_vm_migrate',  dev_list[0])
> +
> +            try:
> +                self.inst.vm_start('test_vm_migrate')
> +            except Exception, e:
> +                self.fail('Failed to start the vm, reason: %s' % e.message)
> +            try:
> +                task = inst.vm_migrate('test_vm_migrate',
> +                                       KIMCHI_LIVE_MIGRATION_TEST)
> +                inst.task_wait(task['id'])
> +                self.assertIn('test_vm_migrate', self.get_remote_vm_list())
> +
> +                remote_conn = self.get_remote_conn()
> +                remote_vm = remote_conn.lookupByName('test_vm_migrate')
> +                self.assertTrue(remote_vm.isPersistent())
> +
> +                remote_vm.destroy()
> +                remote_vm.undefine()
> +            except Exception, e:
> +                self.fail('Migration test failed: %s' % e.message)
> +
> +    @unittest.skipUnless(check_if_vm_migration_test_possible(),
> +                         'not possible to test a live migration')
> +    def test_vm_livemigrate_transient(self):
> +        inst = model.Model(libvirt_uri='qemu:///system',
> +                           objstore_loc=self.tmp_store)
> +
> +        self.create_vm_test()
> +
> +        try:
> +            # removing cdrom because it is not shared storage and will make
> +            # the migration fail
> +            dev_list = self.inst.vmstorages_get_list('test_vm_migrate')
> +            self.inst.vmstorage_delete('test_vm_migrate',  dev_list[0])
> +
> +            self.inst.vm_start('test_vm_migrate')
> +
> +            # to make the VM transient, undefine it while it's running
> +            vm = VMModel.get_vm(
> +                'test_vm_migrate',
> +                LibvirtConnection('qemu:///system')
> +            )
> +            vm.undefine()
> +
> +            task = inst.vm_migrate('test_vm_migrate',
> +                                   KIMCHI_LIVE_MIGRATION_TEST)
> +            inst.task_wait(task['id'])
> +            self.assertIn('test_vm_migrate', self.get_remote_vm_list())
> +
> +            remote_conn = self.get_remote_conn()
> +            remote_vm = remote_conn.lookupByName('test_vm_migrate')
> +            self.assertFalse(remote_vm.isPersistent())
> +
> +            remote_vm.destroy()
> +        except Exception, e:
> +            # Clean up here instead of rollback because if the
> +            # VM was turned transient and shut down it might
> +            # not exist already - rollback in this case  will cause
> +            # a QEMU error
> +            vm = VMModel.get_vm(
> +                'test_vm_migrate',
> +                LibvirtConnection('qemu:///system')
> +            )
> +            if vm.isPersistent():
> +                vm.undefine()
> +            vm.shutdown()
> +            self.fail('Migration test failed: %s' % e.message)
> +
> +    @unittest.skipUnless(check_if_vm_migration_test_possible(),
> +                         'not possible to test shutdown migration')
> +    def test_vm_coldmigrate(self):
> +        inst = model.Model(libvirt_uri='qemu:///system',
> +                           objstore_loc=self.tmp_store)
> +
> +        with RollbackContext() as rollback:
> +            self.create_vm_test()
> +            rollback.prependDefer(utils.rollback_wrapper, self.inst.vm_delete,
> +                                  u'test_vm_migrate')
> +
> +            # removing cdrom because it is not shared storage and will make
> +            # the migration fail
> +            dev_list = self.inst.vmstorages_get_list('test_vm_migrate')
> +            self.inst.vmstorage_delete('test_vm_migrate',  dev_list[0])
> +
> +            try:
> +                task = inst.vm_migrate('test_vm_migrate',
> +                                       KIMCHI_LIVE_MIGRATION_TEST)
> +                inst.task_wait(task['id'])
> +                self.assertIn('test_vm_migrate', self.get_remote_vm_list())
> +
> +                remote_conn = self.get_remote_conn()
> +                remote_vm = remote_conn.lookupByName('test_vm_migrate')
> +                self.assertTrue(remote_vm.isPersistent())
> +
> +                state = remote_vm.info()[0]
> +                self.assertEqual(state, libvirt.VIR_DOMAIN_SHUTOFF)
> +
> +                remote_vm.undefine()
> +            except Exception, e:
> +                self.fail('Migration test failed: %s' % e.message)




More information about the Kimchi-devel mailing list