[XEND] Merge VFB support for PV and HVM guests.

This patch merges the way VFB are represented inside Xend by making
HVM VNC consoles use the VFB as its configuration object.

It preserves the way options are specified through xm but will create
a new VFB device that is used by image.py to put vnc config into
qemu-dm's command line. The parsing is moved into image.py's
parseDeviceModel() rather than in configVNC().

Through the Xen API, you can create a 'console' of protocol 'rfb' and
end up with a VNC console, on both HVM and PV guests. The location of
the connecting port is stored in the location attribute of the console
object. This is updated on each XendDomainInfo.update() if it
changes.

Also fixes missing read of the vnclisten and HVM_boot from the
config when initialised via xm. Makes sure bootable attribute for VBD
is store as an int when making SXP config.

Signed-off-by: Alastair Tse <atse@xensource.com>
This commit is contained in:
Alastair Tse committed 2007-01-31 13:20:36 +00:00
1 parent 6501ac6994
commit f492dd97fb
9 files changed
+342 -89

No files matched your search

+12
View File
@@ -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))
+12 -1
View File
@@ -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))
+2 -2
View File
@@ -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:
+21 -4
View File
@@ -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
# ----------------------------------------------------------------
+135 -23
View File
@@ -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
+42 -17
View File
@@ -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')
+47 -38
View File
@@ -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
@@ -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)
+66 -3
View File
@@ -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)
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.