[Kimchi-devel] [PATCH 2/3 v2] Live migration backend: control/vms and model/vms changes

dhbarboza82 at gmail.com dhbarboza82 at gmail.com
Wed Oct 28 17:41:50 UTC 2015


From: Daniel Henrique Barboza <dhbarboza82 at gmail.com>

This patch adds the backend code to support live (and cold) migration
in Kimchi.

Signed-off-by: Daniel Henrique Barboza <dhbarboza82 at gmail.com>
---
 src/wok/plugins/kimchi/control/vms.py |  3 ++
 src/wok/plugins/kimchi/model/vms.py   | 73 ++++++++++++++++++++++++++++++++++-
 2 files changed, 75 insertions(+), 1 deletion(-)

diff --git a/src/wok/plugins/kimchi/control/vms.py b/src/wok/plugins/kimchi/control/vms.py
index 58d4b67..5e24538 100644
--- a/src/wok/plugins/kimchi/control/vms.py
+++ b/src/wok/plugins/kimchi/control/vms.py
@@ -49,6 +49,9 @@ class VM(Resource):
                                                   destructive=True)
         self.connect = self.generate_action_handler('connect')
         self.clone = self.generate_action_handler_task('clone')
+        self.migrate = self.generate_action_handler_task('migrate',
+                                                         ['remote_host',
+                                                          'user'])
         self.suspend = self.generate_action_handler('suspend')
         self.resume = self.generate_action_handler('resume')
 
diff --git a/src/wok/plugins/kimchi/model/vms.py b/src/wok/plugins/kimchi/model/vms.py
index 60145d0..e90b5ed 100644
--- a/src/wok/plugins/kimchi/model/vms.py
+++ b/src/wok/plugins/kimchi/model/vms.py
@@ -36,7 +36,7 @@ from wok.exception import NotFoundError, OperationFailed
 from wok.model.tasks import TaskModel
 from wok.rollbackcontext import RollbackContext
 from wok.utils import add_task, convert_data_size, get_next_clone_name
-from wok.utils import import_class, run_setfacl_set_attr, wok_log
+from wok.utils import import_class, run_setfacl_set_attr, run_command, wok_log
 from wok.xmlutils.utils import xpath_get_text, xml_item_update
 from wok.xmlutils.utils import dictize
 
@@ -1294,6 +1294,77 @@ class VMModel(object):
             raise OperationFailed('KCHVM0040E', {'name': name,
                                                  'err': e.message})
 
+    def _check_if_host_not_localhost(self, remote_host):
+        hostname, _, returncode = run_command(['hostname'], 5, silent=True)
+
+        if returncode != 0:
+            raise OperationFailed("KCHVM0061E", {'host': remote_host})
+
+        if remote_host in ['localhost', '127.0.0.1', hostname]:
+            raise OperationFailed("KCHVM0055E", {'host': remote_host})
+
+    def _check_if_password_less_login_enabled(self, remote_host, user):
+        username_host = "%s@%s" % (user, remote_host)
+        ssh_cmd = ['ssh', '-oNumberOfPasswordPrompts=0',
+                   '-oStrictHostKeyChecking=no', username_host,
+                   'echo', 'hello']
+        stdout, stderr, returncode = run_command(ssh_cmd, 5, silent=True)
+        if returncode != 0:
+            raise OperationFailed("KCHVM0056E",
+                                  {'host': remote_host, 'user': user})
+
+    def _get_remote_libvirt_conn(self, remote_host,
+                                 user='root', transport='ssh'):
+        dest_uri = 'qemu+%s://%s@%s/system' % (transport, user, remote_host)
+        # TODO: verify why LibvirtConnection(dest_uri) does not work here
+        return libvirt.open(dest_uri)
+
+    def migration_pre_check(self, remote_host, user):
+        self._check_if_host_not_localhost(remote_host)
+        self._check_if_password_less_login_enabled(remote_host, user)
+
+    def migrate(self, name, remote_host, user='root'):
+        name = name.decode('utf-8')
+        remote_host = remote_host.decode('utf-8')
+
+        self.migration_pre_check(remote_host, user)
+        dest_conn = self._get_remote_libvirt_conn(remote_host)
+
+        params = {'name': name,
+                  'dest_conn': dest_conn}
+        task_id = add_task('/vms/%s/migrate' % name, self._migrate_task,
+                           self.objstore, params)
+
+        return self.task.lookup(task_id)
+
+    def _migrate_task(self, cb, params):
+        name = params['name'].decode('utf-8')
+        dest_conn = params['dest_conn']
+
+        cb('starting a migration')
+
+        dom = self.get_vm(name, self.conn)
+        state = DOM_STATE_MAP[dom.info()[0]]
+
+        flags = libvirt.VIR_MIGRATE_PEER2PEER
+        if state == 'shutoff':
+            flags |= (libvirt.VIR_MIGRATE_OFFLINE |
+                      libvirt.VIR_MIGRATE_PERSIST_DEST)
+        elif state in ['running', 'paused']:
+            flags |= libvirt.VIR_MIGRATE_LIVE | libvirt.VIR_MIGRATE_TUNNELLED
+            if dom.isPersistent():
+                flags |= libvirt.VIR_MIGRATE_PERSIST_DEST
+        else:
+            raise OperationFailed("KCHVM0057E", {'name': name,
+                                                 'state': state})
+        try:
+            dom.migrate(dest_conn, flags)
+        except libvirt.libvirtError as e:
+            cb('Migrate failed', False)
+            raise OperationFailed('KCHVM0058E', {'err': e.message,
+                                                 'name': name})
+        cb('Migrate finished', True)
+
 
 class VMScreenshotModel(object):
     def __init__(self, **kargs):
-- 
2.4.3




More information about the Kimchi-devel mailing list