[Kimchi-devel] [v2 1/1] Virtual machine migration

simonjin simonjin at linux.vnet.ibm.com
Tue Nov 11 09:36:29 UTC 2014


Looks like ssh-agent can do the trick, however i didn't make it work.
http://www.sudo.ws/pipermail/sudo-users/2003-April/001479.html

于 2014年11月11日 13:27, simonjin 写道:
>
> 于 2014年11月11日 12:37, simonjin at linux.vnet.ibm.com 写道:
>> From: Simon Jin <simonjin at linux.vnet.ibm.com>
>>
>> Issue:
>> Migraton SSH authentication,
>> even 2 peers have ssh key exchanged, migration still failed due to 
>> failed authentication,
>> first of all, still need passwd input to get a remote libvirt connection
>> second, after that, migration still failed with error:
>> libvirt: QEMU Driver error : 操作失败: Failed to connect to remote 
>> libvirt URI qemu+ssh://root@9.115.122.75/system: Cannot recv data: 
>> Permission denied, please try again.
>> Permission denied, please try again.
>> Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password).: 
>> Connection reset by peer
> The issue is caused by lunch kimchid with sudo, and ssh will then use 
> ssh key under 'simon' but the real
> userid is root, like simon: sudo ssh root at 9.115.122.75
>
> virt-manager use libvirt.openAuth to pass password which from user 
> input to libvir like below, but it doesn't work well as expected, the 
> _auth_cb doesn't get called:
> import libvirt
> import os
>
> def open(_open_uri, passwordcb):
>         def _auth_cb(creds, (passwordcb, passwordcreds)):
>                 for cred in creds:
>                     if cred[0] not in passwordcreds:
>                         raise RuntimeError("Unknown cred type '%s', 
> expected only "
>                                    "%s" % (cred[0], passwordcreds))
>                 return passwordcb(creds)
>
>         open_flags = 0
>         valid_auth_options = [libvirt.VIR_CRED_AUTHNAME,
>                               libvirt.VIR_CRED_PASSPHRASE]
>
>         authcb = _auth_cb
>         authcb_data = passwordcb
>
>         conn = libvirt.openAuth(_open_uri,
>                     [valid_auth_options, authcb,
>                     (authcb_data, valid_auth_options)],
>                     open_flags)
>
>         print conn.getInfo()
>         print conn.listDomainsID()
>         return conn
>
>
> conn = open('qemu+ssh://simon@9.115.122.75/system','passwd')
>
>
>> How to test:
>> Apply this patch(did some hack to easy debug)
>> and issue migrate action with:
>> sudo curl -k -u use:password -H "Content-Type: application/json" -H 
>> "Accept: application/json" https://localhost:8001/vms/RHEL-7/migrate 
>> -X POST -d '{"remoteHost": "9.115.122.75", "user": "root", "passwd": 
>> "passwd"}'
>>
>> v2:
>> - use generate_action_handler_task
>> - Fix args in generate_action_handler_task
>> - Remove MigrationError
>> - Put import in alphabetic
>> - Support offline migration
>>
>> Signed-off-by: Simon Jin <simonjin at linux.vnet.ibm.com>
>> ---
>>   docs/API.md               |   4 ++
>>   src/kimchi/control/vms.py |   1 +
>>   src/kimchi/i18n.py        |  10 ++++
>>   src/kimchi/model/vms.py   | 145 
>> ++++++++++++++++++++++++++++++++++++++++++++++
>>   4 files changed, 160 insertions(+)
>>
>> diff --git a/docs/API.md b/docs/API.md
>> index 9c06f85..36e5ab6 100644
>> --- a/docs/API.md
>> +++ b/docs/API.md
>> @@ -132,6 +132,10 @@ the following general conventions:
>>            It emulates the power reset button on a machine. Note that 
>> there is a
>>            risk of data loss caused by reset without the guest OS 
>> shutdown.
>>   * connect: Prepare the connection for spice or vnc
>> +* migrate: Migrate a virtual machine to a remote server, only 
>> support live mode without block migration.
>> +    * remoteHost: IP address or hostname of the remote server.
>> +    * user: user of the remote server.
>> +    * passwd: passwd of user on remote server.
>>     ### Sub-resource: Virtual Machine Screenshot
>>   diff --git a/src/kimchi/control/vms.py b/src/kimchi/control/vms.py
>> index 88d8a81..0ab239e 100644
>> --- a/src/kimchi/control/vms.py
>> +++ b/src/kimchi/control/vms.py
>> @@ -42,6 +42,7 @@ class VM(Resource):
>>           for ident, node in sub_nodes.items():
>>               setattr(self, ident, node(model, self.ident))
>>           self.start = self.generate_action_handler('start')
>> +        self.migrate = self.generate_action_handler_task('migrate', 
>> ['remoteHost', 'user', 'passwd'])
>>           self.poweroff = self.generate_action_handler('poweroff')
>>           self.shutdown = self.generate_action_handler('shutdown')
>>           self.reset = self.generate_action_handler('reset')
>> diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py
>> index f789259..6edc595 100644
>> --- a/src/kimchi/i18n.py
>> +++ b/src/kimchi/i18n.py
>> @@ -101,6 +101,16 @@ messages = {
>>       "KCHVM0030E": _("Unable to get access metadata of virtual 
>> machine %(name)s. Details: %(err)s"),
>>       "KCHVM0031E": _("The guest console password must be a string."),
>>       "KCHVM0032E": _("The life time for the guest console password 
>> must be a number."),
>> +    "KCHVM0033E": _("Can't migrate to localhost."),
>> +    "KCHVM0034E": _("Can't migrate vm %(name)s on Hypervisor arch 
>> %(srcarch)s.to a different Hypervisor arch %(destarch)s."),
>> +    "KCHVM0035E": _("Can't migrate from %(srchyp)s to a different 
>> Hypervisor type %(desthyp)s."),
>> +    "KCHVM0037E": _("Can't migrate vm %(name)s,which isn't in 
>> running state."),
>> +    "KCHVM0038E": _("Can't migrate vm %(name)s, vm has passthrough 
>> device %(hostdevs)s."),
>> +    "KCHVM0039E": _("Can't migrate vm %(name)s, vm already exist on 
>> destination server %(remoteHost)s."),
>> +    "KCHVM0040E": _("Failed Migrate vm %(name)s due error: %(err)s"),
>> +    "KCHVM0041E": _("Failed resume vm %(name)s from migration 
>> recover. Details: %(err)s"),
>> +    "KCHVM0042E": _("Failed destory vm %(name)s from migration 
>> recover. Details: %(err)s"),
>> +    "KCHVM0043E": _("Failed disconnect %(remoteuri)s when finish 
>> migrate vm %(name)s. Details: %(err)s"),
>>         "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."),
>> diff --git a/src/kimchi/model/vms.py b/src/kimchi/model/vms.py
>> index f2e4ae3..c6709ca 100644
>> --- a/src/kimchi/model/vms.py
>> +++ b/src/kimchi/model/vms.py
>> @@ -30,16 +30,20 @@ from xml.etree import ElementTree
>>   import libvirt
>>   from cherrypy.process.plugins import BackgroundTask
>>   +from kimchi.utils import add_task
>>   from kimchi import vnc
>>   from kimchi.config import READONLY_POOL_TYPE
>>   from kimchi.exception import InvalidOperation, InvalidParameter
>> +from kimchi.model.libvirtconnection import LibvirtConnection
>>   from kimchi.exception import NotFoundError, OperationFailed
>>   from kimchi.model.config import CapabilitiesModel
>>   from kimchi.model.templates import TemplateModel
>> +from kimchi.model.templates import TemplateModel
>>   from kimchi.model.utils import get_vm_name
>>   from kimchi.model.utils import get_metadata_node
>>   from kimchi.model.utils import set_metadata_node
>>   from kimchi.screenshot import VMScreenshot
>> +#from kimchi.model.vmhostdevs import VMHostDevsModel
>>   from kimchi.utils import import_class, kimchi_log, 
>> run_setfacl_set_attr
>>   from kimchi.utils import template_name_from_uri
>>   from kimchi.xmlutils.utils import xpath_get_text, xml_item_update
>> @@ -514,6 +518,147 @@ class VMModel(object):
>>           except libvirt.libvirtError as e:
>>               raise OperationFailed("KCHVM0019E",
>>                                     {'name': name, 'err': 
>> e.get_error_message()})
>> +
>> +    def _preCheck(self, name, destConn, remoteHost):
>> +        kimchi_log.debug('precheck migrate %s' % name)
>> +
>> +        _destConn =  destConn.get()
>> +        if remoteHost in ['localhost', '127.0.0.1']:
>> +            kimchi_log.debug('vm %s is not in running, only running 
>> vm can be migrated' % name)
>> +            raise OperationFailed("KCHVM00033E", {'name': name,
>> +                                                 'remoteHost': 
>> remoteHost})
>> +
>> +
>> +        if self.conn.get().getInfo()[0] != _destConn.getInfo()[0]:
>> +            kimchi_log.debug('vm %s can not migrate to different 
>> arch server.' % (name, _destConn.getInfo()[0]))
>> +            raise OperationFailed("KCHVM00034E", {'name': name,
>> +                                          'srcarch': 
>> self.conn.get().getType(),
>> +                                          'destarch': 
>> _destConn.getType()})
>> +
>> +        if self.conn.get().getType() != _destConn.getType():
>> +            kimchi_log.debug('vm %s can not migrate to a different 
>> hypervisor' % name)
>> +            raise OperationFailed("KCHVM00035E", {'name': self.name,
>> + 'srchyp':self.conn.get().getType(),
>> +                                            'desthyp': 
>> _destConn.getType()})
>> +
>> +
>> +        #model.host import DOM_STATE_MAP, vmhostdevs import host,
>> +        #to void loop import DOM_STATE_MAP, move import 
>> VMHostDevsModel here
>> +        from kimchi.model.vmhostdevs import VMHostDevsModel
>> +        #check if there is any passthrough devices belong to this vm
>> +        vmhostdevs = VMHostDevsModel(conn = self.conn)
>> +        hostdevs = vmhostdevs.get_list(name)
>> +        if hostdevs:
>> +            raise OperationFailed("KCHVM00038E", {'name' : name,
>> +                                                 'hostdevs': hostdevs})
>> +        #try: #FIXME
>> +        #    self.get_vm(name, destConn)
>> +        #except NotFoundError as e:
>> +        #    if e != NotFoundError:
>> +        #        kimchi_log.debug('migrate vm %s already exist on 
>> %s' % (name, destConn.getURI()))
>> +                #raise OperationFailed("KCHVM00039E", {'name' : name,
>> +                # 'remoteHost': remoteHost})
>> +
>> +    def _getRemoteConn(self, remoteHost, user):
>> +        #open destination connect to migrate
>> +        transport = 'ssh'
>> +        duri = 'qemu+%s://%s@%s/system' % (transport,
>> +                                user,remoteHost)
>> +
>> +        conn = LibvirtConnection(duri)
>> +        return conn
>> +
>> +    def _preMigrate(self, name):
>> +        #can't update VM while in Migration source/destination
>> +        kimchi_log.debug('prepare migrate %s' % name)
>> +        dom = self.get_vm(name, self.conn)
>> +        try:
>> +            dom.migrateSetMaxDowntime(0)
>> +        except libvirt.libvirtError as e:
>> +            print e.message
>> +
>> +    def _do_migrate(self, name, destConn, remoteHost, cb):
>> +        _destConn = destConn.get()
>> +        cb('starting a migration')
>> +        kimchi_log.debug('migrate %s start' % name)
>> +        #import pdb
>> +        #pdb.set_trace()
>> +        dom = self.get_vm(name, self.conn)
>> +        flag = 0
>> +        flag |= (libvirt.VIR_MIGRATE_PEER2PEER |
>> +                 libvirt.VIR_MIGRATE_TUNNELLED)
>> +
>> +        state = DOM_STATE_MAP[dom.info()[0]]
>> +        if state == 'running':
>> +            flag |= libvirt.VIR_MIGRATE_LIVE
>> +            try:
>> +                dom.migrateSetMaxDowntime(0)
>> +            except libvirt.libvirtError as e:
>> +                print e.message #FIXME
>> +
>> +        elif state == 'shutoff':
>> +            flag |= libvirt.VIR_MIGRATE_OFFLINE
>> +        else: #FIXME any other state will prevend migration ?
>> +            kimchi_log.debug('Can not migrate vm %s when its in %s.' 
>> % (name, state))
>> +            raise OperationFailed("KCHVM00037E", {'name': self.name})
>> +
>> +        try:
>> +            dom.migrate(_destConn, flag, None, None, 0)
>> +        except libvirt.libvirtError as e:
>> +            kimchi_log.error('migrate %s to %s failed' % (name,
>> + remoteHost))
>> +            self._recover(cb)
>> +            raise OperationFailed('KCHPOOL0040E', {'err': e.message,
>> +                                                   'name': name})
>> +        finally:
>> +            self._finish(name, destConn, cb)
>> +
>> +    def migrate(self, name, remoteHost, user, passwd ):
>> +        destconn = self._getRemoteConn(remoteHost, user)
>> +
>> +        flag = self._preCheck(name, destconn, remoteHost)
>> +        self._preMigrate(name)
>> +        def cb(message):
>> +           print message
>> +
>> +
>> +        self._do_migrate(name, destconn, remoteHost,cb)
>> +
>> +        task_id = add_task('/vms/%s/migrate' % name, 
>> self._do_migrate, self.objstore,
>> +                                               {'name' : name, 
>> 'destconn': destconn,
>> +                                                'remoteHost': 
>> remoteHost})
>> +
>> +        # Record vm migrate task for future querying
>> +        try:
>> +            with self.objstore as session:
>> +                session.store('migrate', name, task_id)
>> +            return task_id
>> +        except Exception as e:
>> +            raise OperationFailed('KCHPOOL0037E', {'err': e.message})
>> +
>> +    def _recover(self, name, destconn, cb):
>> +        destvm = self.get_vm(name, destconn)
>> +        #recover
>> +        cb('recover from a failed migration',False)
>> +        kimchi_log.debug('recover from a failed migrate %s' % name)
>> +        try:
>> +            destvm.destroy()
>> +            kimchi_log.error("destory migrate vm on destination 
>> server")
>> +        except libvirt.libvirtError as e:
>> +            raise OperationFailed('KCHPOOL0042E', {'name' : name,
>> +                                                  'err': e.message})
>> +
>> +    def _finish(self, name, destConn, cb):
>> +        _destConn = destConn.get()
>> +        kimchi_log.debug('finished migrate %s' % name)
>> +        try:
>> +            _destConn.close()
>> +        except libvirt.libvirtError as e:
>> +            raise OperationFailed('KCHPOOL0043E', {'name' : name,
>> +                                                  'remoteuri' : 
>> _destConn.getURI(),
>> +                                                  'err': e.message})
>> +        finally:
>> +            cb('Migrate finished', True)
>>         def poweroff(self, name):
>>           dom = self.get_vm(name, self.conn)
>
> _______________________________________________
> Kimchi-devel mailing list
> Kimchi-devel at ovirt.org
> http://lists.ovirt.org/mailman/listinfo/kimchi-devel




More information about the Kimchi-devel mailing list