[PATCH v3] [Kimchi 0/5] Live migration RDMA support

From: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> v3: - added migrate mockmodel API and test_rest tests v2: - added unit test. This patch set adds Live Migration RDMA support in Kimchi. The backend/frontend will not verify if the network card is RDMA capable before doing the migration. User will have to deal with the libvirt errors in case RDMA is chosen without proper support/setup. Daniel Henrique Barboza (5): Live migration RDMA support: doc changes Live migration RDMA support: model changes Live migration RDMA support: ui changes Live migration RDMA support: test changes Live migration RDMA support: mockmodel changes API.json | 5 ++++ control/vms.py | 5 ++-- docs/API.md | 1 + i18n.py | 3 +- mockmodel.py | 17 ++++++++++- model/vms.py | 18 ++++++++--- tests/test_livemigration.py | 53 ++++++++++++++++++++++++++++++++- tests/test_rest.py | 48 ++++++++++++++++++++++++++++- ui/js/src/kimchi.guest_livemigration.js | 7 +++-- ui/pages/guest-migration.html.tmpl | 6 +++- 10 files changed, 149 insertions(+), 14 deletions(-) -- 2.7.4

From: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> This patch adds a new boolean parameter of the migrate API, called 'enable_rdma', in docs/API.md and API.json. Signed-off-by: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> --- API.json | 5 +++++ docs/API.md | 1 + i18n.py | 3 ++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/API.json b/API.json index 7dcf435..5729e5d 100644 --- a/API.json +++ b/API.json @@ -444,6 +444,11 @@ "description": "Password of the user in the remote server", "type": "string", "error": "KCHVM0069E" + }, + "enable_rdma": { + "description": "Enables RDMA transport", + "type": "boolean", + "error": "KCHVM0091E" } }, "additionalProperties": false diff --git a/docs/API.md b/docs/API.md index b29108d..3ecc7a0 100644 --- a/docs/API.md +++ b/docs/API.md @@ -219,6 +219,7 @@ mode without block migration. * remote_host: IP address or hostname of the remote server. * user *(optional)*: User to log on at the remote server. * password *(optional)*: password of the user in the remote server. + * enable_rdma *(optional)*: boolean. If set to True, the migration will use RDMA transport. ### Sub-resource: Virtual Machine Screenshot diff --git a/i18n.py b/i18n.py index da1391b..6e2daa8 100644 --- a/i18n.py +++ b/i18n.py @@ -1,7 +1,7 @@ # # Project Kimchi # -# Copyright IBM Corp, 2015-2016 +# Copyright IBM Corp, 2015-2017 # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -141,6 +141,7 @@ messages = { "KCHVM0088E": _("invalid console type, supported types are sclp/virtio."), "KCHVM0089E": _("Unable to setup password-less login at remote host %(host)s using user %(user)s: remote directory %(sshdir)s does not exist."), "KCHVM0090E": _("Unable to create a password-less libvirt connection to the remote libvirt daemon at host %(host)s with the user %(user)s. Please verify the remote server libvirt configuration. More information: http://libvirt.org/auth.html ."), + "KCHVM0091E": _("'enable_rdma' must be of type boolean (true or false)."), "KCHVMHDEV0001E": _("VM %(vmid)s does not contain directly assigned host device %(dev_name)s."), "KCHVMHDEV0002E": _("The host device %(dev_name)s is not allowed to directly assign to VM."), -- 2.7.4

From: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> Added a new boolean parameter called 'enable_rdma' in the migrate API. This parameter, when set to 'True', adds an extra 'uri' parameter in the libvirt migrate call. This uri parameter is of format 'rdma://<remote_host>' and indicates to libvirt that this is a RDMA migration. Signed-off-by: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> --- control/vms.py | 5 +++-- model/vms.py | 18 ++++++++++++++---- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/control/vms.py b/control/vms.py index 645cb40..428de94 100644 --- a/control/vms.py +++ b/control/vms.py @@ -1,7 +1,7 @@ # # Project Kimchi # -# Copyright IBM Corp, 2015-2016 +# Copyright IBM Corp, 2015-2017 # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -81,7 +81,8 @@ class VM(Resource): self.migrate = self.generate_action_handler_task('migrate', ['remote_host', 'user', - 'password']) + 'password', + 'enable_rdma']) self.suspend = self.generate_action_handler('suspend') self.resume = self.generate_action_handler('resume') self.serial = self.generate_action_handler('serial') diff --git a/model/vms.py b/model/vms.py index f752271..7729af0 100644 --- a/model/vms.py +++ b/model/vms.py @@ -1,7 +1,7 @@ # # Project Kimchi # -# Copyright IBM Corp, 2015-2016 +# Copyright IBM Corp, 2015-2017 # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -1998,13 +1998,17 @@ class VMModel(object): user ) - def migrate(self, name, remote_host, user=None, password=None): + def migrate(self, name, remote_host, user=None, password=None, + enable_rdma=None): name = name.decode('utf-8') remote_host = remote_host.decode('utf-8') if user is None: user = 'root' + if enable_rdma is None: + enable_rdma = False + self.migration_pre_check(remote_host, user, password) dest_conn = self._get_remote_libvirt_conn(remote_host, user) @@ -2018,7 +2022,8 @@ class VMModel(object): 'dest_conn': dest_conn, 'non_shared': non_shared, 'remote_host': remote_host, - 'user': user} + 'user': user, + 'enable_rdma': enable_rdma} task_id = AsyncTask('/plugins/kimchi/vms/%s/migrate' % name, self._migrate_task, params).id @@ -2030,6 +2035,7 @@ class VMModel(object): non_shared = params['non_shared'] remote_host = params['remote_host'] user = params['user'] + enable_rdma = params['enable_rdma'] cb('starting a migration') @@ -2057,7 +2063,11 @@ class VMModel(object): ) try: - dom.migrate(dest_conn, flags) + if enable_rdma: + param_uri = 'rdma://' + remote_host + dom.migrate(dest_conn, flags, uri=param_uri) + else: + dom.migrate(dest_conn, flags) except libvirt.libvirtError as e: cb('Migrate failed', False) raise OperationFailed('KCHVM0058E', {'err': e.message, -- 2.7.4

From: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> Added a new checkbox in ui/pages/guest-migration.html.tmpl, giving the user the choice of enabling RDMA transport in the migration process. Signed-off-by: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> --- ui/js/src/kimchi.guest_livemigration.js | 7 ++++--- ui/pages/guest-migration.html.tmpl | 6 +++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/ui/js/src/kimchi.guest_livemigration.js b/ui/js/src/kimchi.guest_livemigration.js index 613fd9e..d89deec 100644 --- a/ui/js/src/kimchi.guest_livemigration.js +++ b/ui/js/src/kimchi.guest_livemigration.js @@ -1,7 +1,7 @@ /* * Project Kimchi * - * Copyright IBM Corp, 2016 + * Copyright IBM Corp, 2016-2017 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -80,13 +80,14 @@ kimchi.getLiveMigrationInputValues = function() { var username = $("#user").val(); var password = $("#password").val(); var toDelete = $("#deleteVM").prop('checked'); + var enable_rdma = $("#enableRDMA").prop('checked'); var data = {}; data[kimchi.selectedGuest] = { values: { - remote_host: host + remote_host: host, + enable_rdma: enable_rdma }, toDelete: toDelete - }; if (username && password) { data[kimchi.selectedGuest].values.user = username; diff --git a/ui/pages/guest-migration.html.tmpl b/ui/pages/guest-migration.html.tmpl index e2d3f25..ccd89a9 100644 --- a/ui/pages/guest-migration.html.tmpl +++ b/ui/pages/guest-migration.html.tmpl @@ -1,7 +1,7 @@ #* * Project Kimchi * - * Copyright IBM Corp, 2016 + * Copyright IBM Corp, 2016-2017 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -51,6 +51,10 @@ <i class="fa fa-info-circle"></i> $_("Password of the user in the remote host")</p> </div> <div class="form-group"> + <input id="enableRDMA" class="wok-checkbox" type="checkbox" value="" /> + <label for="enableRDMA" id="labelEnableRDMA">$_("Use RDMA transport. Check this option only if RDMA is properly configured in both source and destination hosts, otherwise migration will fail.") </label> + </div> + <div class="form-group"> <input id="deleteVM" class="wok-checkbox" type="checkbox" value="" /> <label for="deleteVM" id="labelDeleteVM">$_("Delete this VM when the migration is completed") </label> </div> -- 2.7.4

From: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> Added a new test for RDMA support in test_livemigration.py. Signed-off-by: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> --- tests/test_livemigration.py | 53 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/tests/test_livemigration.py b/tests/test_livemigration.py index 7d7d458..7d49dc1 100644 --- a/tests/test_livemigration.py +++ b/tests/test_livemigration.py @@ -1,7 +1,7 @@ # # Project Kimchi # -# Copyright IBM Corp, 2015-2016 +# Copyright IBM Corp, 2015-2017 # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -476,3 +476,54 @@ class LiveMigrationTests(unittest.TestCase): 'test_vm_migrate_fake_user', 'fake_password' ) + + @mock.patch('wok.plugins.kimchi.model.vms.VMModel.' + 'migration_pre_check') + @mock.patch('wok.plugins.kimchi.model.vms.VMModel.' + '_get_remote_libvirt_conn') + @mock.patch('libvirt.virDomain.migrate') + def test_vm_livemigrate_RDMA(self, mock_migrate, mock_remote_conn, + mock_precheck): + + mock_remote_conn.return_value = 'remote_conn' + 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() + + self.inst.vm_migrate('test_vm_migrate', + KIMCHI_LIVE_MIGRATION_TEST, + enable_rdma=True) + + flags = (libvirt.VIR_MIGRATE_PEER2PEER | + libvirt.VIR_MIGRATE_LIVE | + libvirt.VIR_MIGRATE_TUNNELLED) + + param_uri = 'rdma://' + KIMCHI_LIVE_MIGRATION_TEST + mock_migrate.assert_called_once_with(vm, flags, param_uri) + + 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) -- 2.7.4

From: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> This patch adds the 'migrate' API in mockmodel.py, allowing it to be tested in test_rest.py. A new test called 'test_vm_migrate' was added in test_rest.py. Signed-off-by: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> --- mockmodel.py | 17 ++++++++++++++++- tests/test_rest.py | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/mockmodel.py b/mockmodel.py index 43ef1be..9e27d3f 100644 --- a/mockmodel.py +++ b/mockmodel.py @@ -1,7 +1,7 @@ # # Project Kimchi # -# Copyright IBM Corp, 2015-2016 +# Copyright IBM Corp, 2015-2017 # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -411,6 +411,21 @@ class MockModel(Model): MockModel._mock_snapshots[new_name] = snapshots return self._model_vm_clone(name) + def _mock_vm_migrate(self, name, remote_host, user=None, password=None, + enable_rdma=None): + + if enable_rdma is None: + enable_rdma = False + + params = {'remote_host': remote_host, 'enable_rdma': enable_rdma} + taskid = AsyncTask(u'/plugins/kimchi/vms/%s/migrate' % name, + self._vmmigrate_create_task, + params).id + return self.task_lookup(taskid) + + def _vmmigrate_create_task(self, cb, params): + cb('OK', True) + def _mock_vmvirtviewerfile_lookup(self, vm_name): file_name = 'plugins/kimchi/data/virtviewerfiles/%s' %\ os.path.basename(self.virtviewerfile_tmp.name) diff --git a/tests/test_rest.py b/tests/test_rest.py index f25a693..56b8a89 100644 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -2,7 +2,7 @@ # # Project Kimchi # -# Copyright IBM Corp, 2013-2016 +# Copyright IBM Corp, 2013-2017 # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -1248,6 +1248,52 @@ class RestTests(unittest.TestCase): resp = json.loads(resp.read()) self.assertIn(u"KCHVM0012E", resp['reason']) + def test_vm_migrate(self): + with RollbackContext() as rollback: + req = json.dumps({'name': 'test-migrate', + 'source_media': {'type': 'disk', + 'path': fake_iso}}) + resp = self.request('/plugins/kimchi/templates', req, 'POST') + self.assertEquals(201, resp.status) + rollback.prependDefer(self.request, + '/plugins/kimchi/templates/test-migrate', + '{}', 'DELETE') + + req = json.dumps( + {'name': 'test-vm-migrate', + 'template': '/plugins/kimchi/templates/test-migrate'} + ) + resp = self.request('/plugins/kimchi/vms', req, 'POST') + self.assertEquals(202, resp.status) + task = json.loads(resp.read()) + wait_task(self._task_lookup, task['id']) + rollback.prependDefer(self.request, '/plugins/kimchi/vms/test-vm', + '{}', 'DELETE') + + params = {'remote_host': 'destination_host'} + resp = self.request( + '/plugins/kimchi/vms/test-vm-migrate/migrate', + json.dumps(params), 'POST') + self.assertEquals(202, resp.status) + task = json.loads(resp.read()) + wait_task(self._task_lookup, task['id']) + task = json.loads( + self.request('/plugins/kimchi/tasks/%s' % task['id']).read() + ) + self.assertEquals('finished', task['status']) + + params = {'remote_host': 'rdma_host', 'enable_rdma': True} + resp = self.request( + '/plugins/kimchi/vms/test-vm-migrate/migrate', + json.dumps(params), 'POST') + self.assertEquals(202, resp.status) + task = json.loads(resp.read()) + wait_task(self._task_lookup, task['id']) + task = json.loads( + self.request('/plugins/kimchi/tasks/%s' % task['id']).read() + ) + self.assertEquals('finished', task['status']) + def test_create_vm_with_img_based_template(self): resp = json.loads( self.request( -- 2.7.4
participants (2)
-
Aline Manera
-
dhbarboza82@gmail.com