On 10/27/2015 03:11 PM, Paulo Ricardo Paz Vital wrote:
On Tue, 2015-10-27 at 11:37 -0200, Daniel Henrique Barboza wrote:
> On 10/27/2015 08:38 AM, Paulo Ricardo Paz Vital wrote:
>> 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?
> Libvirt will freak out if you try to do a 'migrateinception' :P
>
> This is why I've made this verification - to ensure that the user
> doesn't pass the
> IP/hostname of the same host Kimchi is running on by accident.
>
>
So, if I'm running Kimchi in a machine called 'bla.br.ibm.com' and use
this hostname to realize a migration, libvirt will freakout, right?!?!
I suggest to check not only the name 'localhost' and IP '127.0.0.1',
but also the IP address and real hostname of the machine.
hahahaha you're right
I'll fix it in v2
>>
>>
>>> +
>>> + 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):