[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