On Mon, 2015-10-26 at 16:08 -0200, dhbarboza82(a)gmail.com wrote:
From: Daniel Henrique Barboza <dhbarboza82(a)gmail.com>
This patch adds the backend code to support live (and cold) migration
in Kimchi.
Signed-off-by: Daniel Henrique Barboza <dhbarboza82(a)gmail.com>
---
src/wok/plugins/kimchi/control/vms.py | 3 ++
src/wok/plugins/kimchi/model/vms.py | 68
++++++++++++++++++++++++++++++++++-
2 files changed, 70 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..5d242e8 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,72 @@ class VMModel(object):
raise OperationFailed('KCHVM0040E', {'name': name,
'err': e.message})
+ def _check_if_host_not_localhost(self, remote_host):
+ if remote_host in ['localhost', '127.0.0.1']:
+ raise OperationFailed("KCHVM0055E", {'host':
remote_host})
Only a curiosity: Is it OK accept the hostname or IP address of my
'localhost', or only these two cases are not a good case to migrate?
+
+ 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):