
From: Daniel Henrique Barboza <dhbarboza82@gmail.com> This patch adds the backend code to support live (and cold) migration in Kimchi. Signed-off-by: Daniel Henrique Barboza <dhbarboza82@gmail.com> --- src/wok/plugins/kimchi/control/vms.py | 3 ++ src/wok/plugins/kimchi/model/vms.py | 75 ++++++++++++++++++++++++++++++++++- 2 files changed, 77 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 6e77bae..63681c7 100644 --- a/src/wok/plugins/kimchi/model/vms.py +++ b/src/wok/plugins/kimchi/model/vms.py @@ -22,6 +22,7 @@ import libvirt import lxml.etree as ET import os import random +import socket import string import threading import time @@ -36,7 +37,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 @@ -1309,6 +1310,78 @@ class VMModel(object): raise OperationFailed('KCHVM0040E', {'name': name, 'err': e.message}) + def _check_if_host_not_localhost(self, remote_host): + hostname = socket.gethostname() + + 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: + dest_conn.close() + 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}) + finally: + dest_conn.close() + + cb('Migrate finished', True) + class VMScreenshotModel(object): def __init__(self, **kargs): -- 2.4.3