diff --git a/tools/python/scripts/test_hvm_create.py b/tools/python/scripts/test_hvm_create.py index 5d2aca5944..8effd42db9 100644 --- a/tools/python/scripts/test_hvm_create.py +++ b/tools/python/scripts/test_hvm_create.py @@ -72,6 +72,12 @@ vif_cfg = { 'MTU': 1500, } +console_cfg = { + 'protocol': 'rfb', + 'other_config': {'vncunused': 1, 'vncpasswd': 'testing'}, +} + + import sys import time sys.path.append('/usr/lib/python') @@ -125,6 +131,12 @@ def test_vm_create(): vif_cfg['VM'] = vm_uuid vif_uuid = execute(server, 'VIF.create', (session, vif_cfg)) + # Create a console + console_cfg['VM'] = vm_uuid + console_uuid = execute(server, 'console.create', + (session, console_cfg)) + print console_uuid + # Start the VM execute(server, 'VM.start', (session, vm_uuid, False)) diff --git a/tools/python/scripts/test_vm_create.py b/tools/python/scripts/test_vm_create.py index 19a5135153..0d702aae69 100644 --- a/tools/python/scripts/test_vm_create.py +++ b/tools/python/scripts/test_vm_create.py @@ -91,6 +91,11 @@ vif_cfg = { 'network': '', 'MAC': '', 'MTU': 1500, +} + +console_cfg = { + 'protocol': 'rfb', + 'other_config': {'vncunused': 1, 'vncpasswd': 'testing'}, } import sys @@ -157,12 +162,18 @@ def test_vm_create(): vif_cfg['VM'] = vm_uuid vif_uuid = execute(server, 'VIF.create', (session, vif_cfg)) + # Create a console + console_cfg['VM'] = vm_uuid + console_uuid = execute(server, 'console.create', + (session, console_cfg)) + print console_uuid + # Start the VM execute(server, 'VM.start', (session, vm_uuid, False)) time.sleep(30) - test_suspend = True + test_suspend = False if test_suspend: print 'Suspending VM..' execute(server, 'VM.suspend', (session, vm_uuid)) diff --git a/tools/python/scripts/xapi.py b/tools/python/scripts/xapi.py index ab0938f111..b5fe8b3682 100644 --- a/tools/python/scripts/xapi.py +++ b/tools/python/scripts/xapi.py @@ -45,7 +45,7 @@ VDI_LIST_FORMAT = '%(name_label)-18s %(uuid)-36s %(virtual_size)-8s '\ VBD_LIST_FORMAT = '%(device)-6s %(uuid)-36s %(VDI)-8s' TASK_LIST_FORMAT = '%(name_label)-18s %(uuid)-36s %(status)-8s %(progress)-4s' VIF_LIST_FORMAT = '%(name)-8s %(device)-7s %(uuid)-36s %(MAC)-10s' -CONSOLE_LIST_FORMAT = '%(uuid)-36s %(protocol)-8s %(uri)-32s' +CONSOLE_LIST_FORMAT = '%(uuid)-36s %(protocol)-8s %(location)-32s' COMMANDS = { 'host-info': ('', 'Get Xen Host Info'), @@ -545,7 +545,7 @@ def xapi_console_list(args, async = False): if not is_long: print CONSOLE_LIST_FORMAT % {'protocol': 'Protocol', - 'uri': 'URI', + 'location': 'Location', 'uuid': 'UUID'} for console in consoles: diff --git a/tools/python/xen/xend/XendAPI.py b/tools/python/xen/xend/XendAPI.py index 7de21ad4d9..0b249f9449 100644 --- a/tools/python/xen/xend/XendAPI.py +++ b/tools/python/xen/xend/XendAPI.py @@ -1835,8 +1835,9 @@ class XendAPI(object): # ---------------------------------------------------------------- - console_attr_ro = ['uri', 'protocol', 'VM'] - console_attr_rw = [] + console_attr_ro = ['location', 'protocol', 'VM'] + console_attr_rw = ['other_config'] + console_funcs = [('create', 'console')] def console_get_all(self, session): xendom = XendDomain.instance() @@ -1844,10 +1845,10 @@ class XendAPI(object): cons = reduce(lambda x, y: x + y, cons) return xen_api_success(cons) - def console_get_uri(self, session, console_ref): + def console_get_location(self, session, console_ref): return xen_api_success(xendom.get_dev_property_by_uuid('console', console_ref, - 'uri')) + 'location')) def console_get_protocol(self, session, console_ref): return xen_api_success(xendom.get_dev_property_by_uuid('console', @@ -1879,6 +1880,22 @@ class XendAPI(object): return xen_api_success(return_cfg) + def console_create(self, session, console_struct): + xendom = XendDomain.instance() + if not xendom.is_valid_vm(console_struct['VM']): + return xen_api_error(['VM_HANDLE_INVALID', console_struct['VM']]) + + dom = xendom.get_vm_by_uuid(console_struct['VM']) + try: + if 'protocol' not in console_struct: + return xen_api_error(['CONSOLE_PROTOCOL_INVALID', + 'No protocol specified']) + + console_ref = dom.create_console(console_struct) + xendom.managed_config_save(dom) + return xen_api_success(console_ref) + except XendError, e: + return xen_api_error([XEND_ERROR_TODO, str(e)]) # Xen API: Class SR # ---------------------------------------------------------------- diff --git a/tools/python/xen/xend/XendConfig.py b/tools/python/xen/xend/XendConfig.py index 5a6555e9c7..f17a5a2295 100644 --- a/tools/python/xen/xend/XendConfig.py +++ b/tools/python/xen/xend/XendConfig.py @@ -126,6 +126,7 @@ XENAPI_HVM_CFG = { 'platform_serial' : 'serial', 'platform_localtime': 'localtime', 'platform_keymap' : 'keymap', + 'HVM_boot': 'boot', } # List of XendConfig configuration keys that have no direct equivalent @@ -250,7 +251,8 @@ LEGACY_IMAGE_CFG = [ ('sdl', int), ('vncdisplay', int), ('vncunused', int), - ('vncpasswd', str), + ('vncpasswd', str), + ('vnclisten', str), ] LEGACY_IMAGE_HVM_CFG = [ @@ -379,6 +381,7 @@ class XendConfig(dict): 'vif_refs': [], 'vbd_refs': [], 'vtpm_refs': [], + 'other_config': {}, } defaults['name_label'] = 'Domain-' + defaults['uuid'] @@ -660,6 +663,25 @@ class XendConfig(dict): self['vbd_refs'] = cfg.get('vbd_refs', []) self['vtpm_refs'] = cfg.get('vtpm_refs', []) + # coalesce hvm vnc frame buffer with vfb config + if self['image']['type'] == 'hvm' and self['image'].get('vnc', 0): + # add vfb device if it isn't there already + has_rfb = False + for console_uuid in self['console_refs']: + if self['devices'][console_uuid][1].get('protocol') == 'rfb': + has_rfb = True + break + + if not has_rfb: + dev_config = ['vfb'] + # copy VNC related params from image config to vfb dev conf + for key in ['vncpasswd', 'vncunused', 'vncdisplay', + 'vnclisten']: + if key in self['image']: + dev_config.append([key, self['image'][key]]) + + self.device_add('vfb', cfg_sxp = dev_config) + def _sxp_to_xapi_unsupported(self, sxp_cfg): """Read in an SXP configuration object and populate @@ -756,7 +778,7 @@ class XendConfig(dict): # currently unsupported options self['image']['hvm']['device_model'] = LEGACY_DM - self['image']['vnc'] = 1 + self['image']['vnc'] = 0 self['image']['hvm']['pae'] = 1 if self['platform_enable_audio']: @@ -883,8 +905,9 @@ class XendConfig(dict): # store as part of the device config. dev_uuid = sxp.child_value(config, 'uuid') dev_type, dev_cfg = self['devices'][dev_uuid] - config.append(['bootable', - int(dev_cfg['bootable'])]) + is_bootable = dev_cfg.get('bootable', False) + config.append(['bootable', int(is_bootable)]) + sxpr.append(['device', config]) found = True @@ -986,6 +1009,28 @@ class XendConfig(dict): # bootable. dev_info['bootable'] = True target['vbd_refs'].append(dev_uuid) + + elif dev_type == 'vfb': + # Populate other config with aux data that is associated + # with vfb + + other_config = {} + for key in ['vncunused', 'vncdisplay', 'vnclisten', + 'vncpasswd', 'type', 'display', 'xauthority', + 'keymap']: + if key in dev_info: + other_config[key] = dev_info[key] + target['devices'][dev_uuid][1]['other_config'] = other_config + + + if 'console_refs' not in target: + target['console_refs'] = [] + + # Treat VFB devices as console devices so they are found + # through Xen API + if dev_uuid not in target['console_refs']: + target['console_refs'].append(dev_uuid) + elif dev_type == 'console': if 'console_refs' not in target: target['console_refs'] = [] @@ -1048,50 +1093,117 @@ class XendConfig(dict): target['devices'][dev_uuid] = (dev_type, dev_info) target['vtpm_refs'].append(dev_uuid) + elif dev_type == 'console': + dev_uuid = cfg_xenapi.get('uuid', uuid.createString()) + dev_info['uuid'] = dev_uuid + dev_info['protocol'] = cfg_xenapi.get('protocol', 'rfb') + dev_info['other_config'] = cfg_xenapi.get('other_config', {}) + if dev_info['protocol'] == 'rfb': + # collapse other config into devinfo for things + # such as vncpasswd, vncunused, etc. + dev_info.update(cfg_xenapi.get('other_config', {})) + dev_info['type'] = 'vnc' + target['devices'][dev_uuid] = ('vfb', dev_info) + target['console_refs'].append(dev_uuid) + + # Finally, if we are a pvfb, we need to make a vkbd + # as well that is not really exposed to Xen API + vkbd_uuid = uuid.createString() + target['devices'][vkbd_uuid] = ('vkbd', {}) + + elif dev_info['protocol'] == 'vt100': + # if someone tries to create a VT100 console + # via the Xen API, we'll have to ignore it + # because we create one automatically in + # XendDomainInfo._update_consoles + raise XendConfigError('Creating vt100 consoles via ' + 'Xen API is unsupported') + return dev_uuid # no valid device to add return '' - def console_add(self, protocol, uri): + def console_add(self, protocol, location, other_config = {}): dev_uuid = uuid.createString() - dev_info = { - 'uuid': dev_uuid, - 'protocol': protocol, - 'uri': uri - } - if 'devices' not in self: - self['devices'] = {} + if protocol == 'vt100': + dev_info = { + 'uuid': dev_uuid, + 'protocol': protocol, + 'location': location, + 'other_config': other_config, + } + + if 'devices' not in self: + self['devices'] = {} - self['devices'][dev_uuid] = ('console', dev_info) - self['console_refs'].append(dev_uuid) - return dev_info + self['devices'][dev_uuid] = ('console', dev_info) + self['console_refs'].append(dev_uuid) + return dev_info + + return {} + + def console_update(self, console_uuid, key, value): + for dev_uuid, (dev_type, dev_info) in self['devices'].items(): + if dev_uuid == console_uuid: + dev_info[key] = value + break def console_get_all(self, protocol): - consoles = [dinfo for dtype, dinfo in self['devices'].values() - if dtype == 'console'] - return [c for c in consoles if c.get('protocol') == protocol] + if protocol == 'vt100': + consoles = [dinfo for dtype, dinfo in self['devices'].values() + if dtype == 'console'] + return [c for c in consoles if c.get('protocol') == protocol] - def device_update(self, dev_uuid, cfg_sxp): + elif protocol == 'rfb': + vfbs = [dinfo for dtype, dinfo in self['devices'].values() + if dtype == 'vfb'] + + # move all non-console key values to other_config before + # returning console config + valid_keys = ['uuid', 'location'] + for vfb in vfbs: + other_config = {} + for key, val in vfb.items(): + if key not in valid_keys: + other_config[key] = vfb[key] + del vfb[key] + vfb['other_config'] = other_config + vfb['protocol'] = 'rfb' + + return vfbs + + else: + return [] + + def device_update(self, dev_uuid, cfg_sxp = [], cfg_xenapi = {}): """Update an existing device with the new configuration. @rtype: boolean @return: Returns True if succesfully found and updated a device conf """ - if dev_uuid in self['devices']: + if dev_uuid in self['devices'] and cfg_sxp: if sxp.child0(cfg_sxp) == 'device': config = sxp.child0(cfg_sxp) else: config = cfg_sxp - + + dev_type, dev_info = self['devices'][dev_uuid] for opt_val in config[1:]: try: opt, val = opt_val - self['devices'][dev_uuid][opt] = val + dev_info[opt] = val except (TypeError, ValueError): pass # no value for this config option - + + self['devices'][dev_uuid] = (dev_type, dev_info) return True + + elif dev_uuid in self['devices'] and cfg_xenapi: + dev_type, dev_info = self['devices'][dev_uuid] + for key, val in cfg_xenapi.items(): + dev_info[key] = val + self['devices'][dev_uuid] = (dev_type, dev_info) return False diff --git a/tools/python/xen/xend/XendDomainInfo.py b/tools/python/xen/xend/XendDomainInfo.py index b8624140b9..d657d4a1f1 100644 --- a/tools/python/xen/xend/XendDomainInfo.py +++ b/tools/python/xen/xend/XendDomainInfo.py @@ -695,16 +695,29 @@ class XendDomainInfo: if not serial_consoles: cfg = self.info.console_add('vt100', self.console_port) self._createDevice('console', cfg) + else: + console_uuid = serial_consoles[0].get('uuid') + self.info.console_update(console_uuid, 'location', + self.console_port) + - # Update VNC port if it exists + # Update VNC port if it exists and write to xenstore vnc_port = self.readDom('console/vnc-port') if vnc_port is not None: - vnc_consoles = self.info.console_get_all('rfb') - if not vnc_consoles: - cfg = self.info.console_add('rfb', 'localhost:%s' % - str(vnc_port)) - self._createDevice('console', cfg) + for dev_uuid, (dev_type, dev_info) in self.info['devices'].items(): + if dev_type == 'vfb': + old_location = dev_info.get('location') + listen_host = dev_info.get('vnclisten', 'localhost') + new_location = '%s:%s' % (listen_host, str(vnc_port)) + if old_location == new_location: + break + dev_info['location'] = new_location + self.info.device_update(dev_uuid, cfg_xenapi = dev_info) + vfb_ctrl = self.getDeviceController('vfb') + vfb_ctrl.reconfigureDevice(0, dev_info) + break + # # Function to update xenstore /vm/* # @@ -2017,21 +2030,18 @@ class XendDomainInfo: @rtype: dictionary """ - dev_type_config = self.info['devices'].get(dev_uuid) + dev_type, dev_config = self.info['devices'].get(dev_uuid, (None, None)) # shortcut if the domain isn't started because # the devcontrollers will have no better information # than XendConfig. if self.state in (XEN_API_VM_POWER_STATE_HALTED,): - if dev_type_config: - return copy.deepcopy(dev_type_config[1]) + if dev_config: + return copy.deepcopy(dev_config) return None # instead of using dev_class, we use the dev_type # that is from XendConfig. - # This will accomdate 'tap' as well as 'vbd' - dev_type = dev_type_config[0] - controller = self.getDeviceController(dev_type) if not controller: return None @@ -2040,14 +2050,14 @@ class XendDomainInfo: if not all_configs: return None - dev_config = copy.deepcopy(dev_type_config[1]) + updated_dev_config = copy.deepcopy(dev_config) for _devid, _devcfg in all_configs.items(): if _devcfg.get('uuid') == dev_uuid: - dev_config.update(_devcfg) - dev_config['id'] = _devid - return dev_config + updated_dev_config.update(_devcfg) + updated_dev_config['id'] = _devid + return updated_dev_config - return dev_config + return updated_dev_config def get_dev_xenapi_config(self, dev_class, dev_uuid): config = self.get_dev_config_by_uuid(dev_class, dev_uuid) @@ -2230,6 +2240,21 @@ class XendDomainInfo: return dev_uuid + def create_console(self, xenapi_console): + """ Create a console device from a Xen API struct. + + @return: uuid of device + @rtype: string + """ + if self.state not in (DOM_STATE_HALTED,): + raise VmError("Can only add console to a halted domain.") + + dev_uuid = self.info.device_add('console', cfg_xenapi = xenapi_console) + if not dev_uuid: + raise XendError('Failed to create device') + + return dev_uuid + def destroy_device_by_uuid(self, dev_type, dev_uuid): if dev_uuid not in self.info['devices']: raise XendError('Device does not exist') diff --git a/tools/python/xen/xend/image.py b/tools/python/xen/xend/image.py index b1437d3678..eeb8bd0ebd 100644 --- a/tools/python/xen/xend/image.py +++ b/tools/python/xen/xend/image.py @@ -26,6 +26,7 @@ import xen.lowlevel.xc from xen.xend.XendConstants import REVERSE_DOMAIN_SHUTDOWN_REASONS from xen.xend.XendError import VmError, XendError from xen.xend.XendLogging import log +from xen.xend.XendOptions import instance as xenopts from xen.xend.server.netif import randomMAC from xen.xend.xenstore.xswatch import xswatch from xen.xend import arch @@ -340,8 +341,6 @@ class HVMImageHandler(ImageHandler): self.pid = None - self.dmargs += self.configVNC(imageConfig) - self.pae = imageConfig['hvm'].get('pae', 0) self.apic = imageConfig['hvm'].get('apic', 0) self.acpi = imageConfig['hvm']['devices'].get('acpi', 0) @@ -379,8 +378,8 @@ class HVMImageHandler(ImageHandler): dmargs = [ 'boot', 'fda', 'fdb', 'soundhw', 'localtime', 'serial', 'stdvga', 'isa', 'acpi', 'usb', 'usbdevice', 'keymap' ] + hvmDeviceConfig = vmConfig['image']['hvm']['devices'] - ret = ['-vcpus', str(self.vm.getVCpuCount())] for a in dmargs: @@ -439,49 +438,59 @@ class HVMImageHandler(ImageHandler): ret.append("-net") ret.append("tap,vlan=%d,bridge=%s" % (nics, bridge)) - return ret - def configVNC(self, imageConfig): - # Handle graphics library related options - vnc = imageConfig.get('vnc') - sdl = imageConfig.get('sdl') - ret = [] - nographic = imageConfig.get('nographic') - - # get password from VM config (if password omitted, None) - vncpasswd_vmconfig = imageConfig.get('vncpasswd') - - if nographic: + # + # Find RFB console device, and if it exists, make QEMU enable + # the VNC console. + # + if vmConfig['image'].get('nographic'): + # skip vnc init if nographic is set ret.append('-nographic') return ret - if vnc: - vncdisplay = imageConfig.get('vncdisplay', - int(self.vm.getDomid())) - vncunused = imageConfig.get('vncunused') + vnc_config = {} + has_vfb = False + has_vnc = int(vmConfig['image'].get('vnc')) != 0 + for dev_uuid in vmConfig['console_refs']: + dev_type, dev_info = vmConfig['devices'][devuuid] + if dev_type == 'rfb': + vnc_config = dev_info.get('other_config', {}) + has_vfb = True + break - if vncunused: - ret += ['-vncunused'] - else: - ret += ['-vnc', '%d' % vncdisplay] + if not vnc_config: + for key in ('vncunused', 'vnclisten', 'vncdisplay', 'vncpasswd'): + if key in vmConfig['image']: + vnc_config[key] = vmConfig['image'][key] - vnclisten = imageConfig.get('vnclisten') + if not has_vfb and not has_vnc: + ret.append('-nographic') + return ret - if not(vnclisten): - vnclisten = (xen.xend.XendOptions.instance(). - get_vnclisten_address()) - if vnclisten: - ret += ['-vnclisten', vnclisten] + + if not vnc_config.get('vncunused', 0) and \ + vnc_config.get('vncdisplay', 0): + ret.append('-vnc') + ret.append(str(vncdisplay)) + else: + ret.append('-vncunused') - vncpasswd = vncpasswd_vmconfig - if vncpasswd is None: - vncpasswd = (xen.xend.XendOptions.instance(). - get_vncpasswd_default()) - if vncpasswd is None: - raise VmError('vncpasswd is not set up in ' + - 'VMconfig and xend-config.') - if vncpasswd != '': - self.vm.storeVm("vncpasswd", vncpasswd) + vnclisten = vnc_config.get('vnclisten', + xenopts().get_vnclisten_address()) + ret.append('-vnclisten') + ret.append(str(vnclisten)) + + # Store vncpassword in xenstore + vncpasswd = vnc_config.get('vncpasswd') + if not vncpasswd: + vncpasswd = xenopts().get_vncpasswd_default() + + if vncpasswd is None: + raise VmError('vncpasswd is not setup in vmconfig or ' + 'xend-config.sxp') + + if vncpasswd != '': + self.vm.storeVm('vncpasswd', vncpasswd) return ret diff --git a/tools/python/xen/xend/server/ConsoleController.py b/tools/python/xen/xend/server/ConsoleController.py index 0cccf4e55d..5afa34d7e8 100644 --- a/tools/python/xen/xend/server/ConsoleController.py +++ b/tools/python/xen/xend/server/ConsoleController.py @@ -8,7 +8,7 @@ class ConsoleController(DevController): console devices with persistent UUIDs. """ - valid_cfg = ['uri', 'uuid', 'protocol'] + valid_cfg = ['location', 'uuid', 'protocol'] def __init__(self, vm): DevController.__init__(self, vm) @@ -29,3 +29,7 @@ class ConsoleController(DevController): def migrate(self, deviceConfig, network, dst, step, domName): return 0 + + def destroyDevice(self, devid, force): + DevController.destroyDevice(self, devid, True) + diff --git a/tools/python/xen/xend/server/vfbif.py b/tools/python/xen/xend/server/vfbif.py index 7bfe850593..0bd1b92bf3 100644 --- a/tools/python/xen/xend/server/vfbif.py +++ b/tools/python/xen/xend/server/vfbif.py @@ -14,7 +14,8 @@ def spawn_detached(path, args, env): os.waitpid(p, 0) CONFIG_ENTRIES = ['type', 'vncdisplay', 'vnclisten', 'vncpasswd', 'vncunused', - 'display', 'xauthority', 'keymap' ] + 'display', 'xauthority', 'keymap', + 'uuid', 'location', 'protocol'] class VfbifController(DevController): """Virtual frame buffer controller. Handles all vfb devices for a domain. @@ -27,10 +28,11 @@ class VfbifController(DevController): def getDeviceDetails(self, config): """@see DevController.getDeviceDetails""" - back = dict([(k, config[k]) for k in CONFIG_ENTRIES + back = dict([(k, str(config[k])) for k in CONFIG_ENTRIES if config.has_key(k)]) - return (0, back, {}) + devid = 0 + return (devid, back, {}) def getDeviceConfiguration(self, devid): @@ -44,6 +46,10 @@ class VfbifController(DevController): def createDevice(self, config): DevController.createDevice(self, config) + if self.vm.info.get('HVM_boot'): + # is HVM, so qemu-dm will handle the vfb. + return + std_args = [ "--domid", "%d" % self.vm.getDomid(), "--title", self.vm.getName() ] t = config.get("type", None) @@ -82,6 +88,42 @@ class VfbifController(DevController): else: raise VmError('Unknown vfb type %s (%s)' % (t, repr(config))) + + def waitForDevice(self, devid): + if self.vm.info.get('HVM_boot'): + log.debug('skip waiting for HVM vfb') + # is a qemu-dm managed device, don't wait for hotplug for these. + return + + DevController.waitForDevice(self, devid) + + + def reconfigureDevice(self, _, config): + """ Only allow appending location information of vnc port into + xenstore.""" + + if 'location' in config: + (devid, back, front) = self.getDeviceDetails(config) + self.writeBackend(devid, 'location', config['location']) + return back.get('uuid') + + raise VmError('Refusing to reconfigure device vfb:%d' % devid) + + def destroyDevice(self, devid, force): + if self.vm.info.get('HVM_boot'): + # remove the backend xenstore entries for HVM guests no matter + # what + DevController.destroyDevice(self, devid, True) + else: + DevController.destroyDevice(self, devid, force) + + + def migrate(self, deviceConfig, network, dst, step, domName): + if self.vm.info.get('HVM_boot'): + return 0 + return DevController.migrate(self, deviceConfig, network, dst, step, + domName) + class VkbdifController(DevController): """Virtual keyboard controller. Handles all vkbd devices for a domain. """ @@ -92,3 +134,24 @@ class VkbdifController(DevController): back = {} front = {} return (devid, back, front) + + def waitForDevice(self, config): + if self.vm.info.get('HVM_boot'): + # is a qemu-dm managed device, don't wait for hotplug for these. + return + + DevController.waitForDevice(self, config) + + def destroyDevice(self, devid, force): + if self.vm.info.get('HVM_boot'): + # remove the backend xenstore entries for HVM guests no matter + # what + DevController.destroyDevice(self, devid, True) + else: + DevController.destroyDevice(self, devid, force) + + def migrate(self, deviceConfig, network, dst, step, domName): + if self.vm.info.get('HVM_boot'): + return 0 + return DevController.migrate(self, deviceConfig, network, dst, step, + domName)