ia64/xen-unstable

view tools/python/xen/xend/XendConfig.py @ 15874:d146097e049d

Add 'type vnc' to vfb device sexpr for HVM guest

This patch adds 'type vnc' to vfb device sexpr for HVM guests. PV
guests already contain this entry in sexpr, e.g.

(device
(vfb
(vncunused 1)
...
(type vnc)
...
)
)

Some tools, such as libvirt, look for device/vfb/type in sexpr before
publishing VNC port. More importantly, this patch provides
consistency in vfb device sexpr between HVM and PV guests.

Signed-off-by: Jim Fehlig <jfehlig@novell.com>
author kfraser@localhost.localdomain
date Tue Sep 11 13:40:44 2007 +0100 (2007-09-11)
parents 2f13d0f2b07c
children acfa9290746f
line source
1 #===========================================================================
2 # This library is free software; you can redistribute it and/or
3 # modify it under the terms of version 2.1 of the GNU Lesser General Public
4 # License as published by the Free Software Foundation.
5 #
6 # This library is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 # Lesser General Public License for more details.
10 #
11 # You should have received a copy of the GNU Lesser General Public
12 # License along with this library; if not, write to the Free Software
13 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 #============================================================================
15 # Copyright (C) 2006-2007 XenSource Ltd
16 #============================================================================
18 import logging
19 import re
20 import time
21 import types
23 from xen.xend import sxp
24 from xen.xend import uuid
25 from xen.xend import XendOptions
26 from xen.xend import XendAPIStore
27 from xen.xend.XendError import VmError
28 from xen.xend.XendDevices import XendDevices
29 from xen.xend.PrettyPrint import prettyprintstring
30 from xen.xend.XendConstants import DOM_STATE_HALTED
31 from xen.xend.server.BlktapController import blktap_disk_types
32 from xen.xend.server.netif import randomMAC
33 from xen.util.blkif import blkdev_name_to_number
34 from xen.util import xsconstants
36 log = logging.getLogger("xend.XendConfig")
37 log.setLevel(logging.WARN)
40 """
41 XendConfig API
43 XendConfig will try to mirror as closely the Xen API VM Struct
44 with extra parameters for those options that are not supported.
46 """
48 def reverse_dict(adict):
49 """Return the reverse mapping of a dictionary."""
50 return dict([(v, k) for k, v in adict.items()])
52 def bool0(v):
53 return v != '0' and v != 'False' and bool(v)
55 # Recursively copy a data struct, scrubbing out VNC passwords.
56 # Will scrub any dict entry with a key of 'vncpasswd' or any
57 # 2-element list whose first member is 'vncpasswd'. It will
58 # also scrub a string matching '(vncpasswd XYZ)'. Everything
59 # else is no-op passthrough
60 def scrub_password(data):
61 if type(data) == dict or type(data) == XendConfig:
62 scrubbed = {}
63 for key in data.keys():
64 if key == "vncpasswd":
65 scrubbed[key] = "XXXXXXXX"
66 else:
67 scrubbed[key] = scrub_password(data[key])
68 return scrubbed
69 elif type(data) == list:
70 if len(data) == 2 and type(data[0]) == str and data[0] == 'vncpasswd':
71 return ['vncpasswd', 'XXXXXXXX']
72 else:
73 scrubbed = []
74 for entry in data:
75 scrubbed.append(scrub_password(entry))
76 return scrubbed
77 elif type(data) == tuple:
78 scrubbed = []
79 for entry in data:
80 scrubbed.append(scrub_password(entry))
81 return tuple(scrubbed)
82 elif type(data) == str:
83 return re.sub(r'\(vncpasswd\s+[^\)]+\)','(vncpasswd XXXXXX)', data)
84 else:
85 return data
87 #
88 # CPU fields:
89 #
90 # VCPUs_max -- the maximum number of vcpus that this domain may ever have.
91 # aka XendDomainInfo.getVCpuCount().
92 # vcpus -- the legacy configuration name for above.
93 # max_vcpu_id -- vcpus_number - 1. This is given to us by Xen.
94 #
95 # cpus -- the list of pCPUs available to each vCPU.
96 #
97 # vcpu_avail -- a bitmap telling the guest domain whether it may use each of
98 # its VCPUs. This is translated to
99 # <dompath>/cpu/<id>/availability = {online,offline} for use
100 # by the guest domain.
101 # VCPUs_live -- the number of VCPUs currently up, as reported by Xen. This
102 # is changed by changing vcpu_avail, and waiting for the
103 # domain to respond.
104 #
107 # Mapping from XendConfig configuration keys to the old
108 # legacy configuration keys that map directly.
110 XENAPI_CFG_TO_LEGACY_CFG = {
111 'uuid': 'uuid',
112 'VCPUs_max': 'vcpus',
113 'cpus': 'cpus',
114 'name_label': 'name',
115 'actions_after_shutdown': 'on_poweroff',
116 'actions_after_reboot': 'on_reboot',
117 'actions_after_crash': 'on_crash',
118 'PV_bootloader': 'bootloader',
119 'PV_bootloader_args': 'bootloader_args',
120 }
122 LEGACY_CFG_TO_XENAPI_CFG = reverse_dict(XENAPI_CFG_TO_LEGACY_CFG)
124 # Platform configuration keys.
125 XENAPI_PLATFORM_CFG = [ 'acpi', 'apic', 'boot', 'device_model', 'display',
126 'fda', 'fdb', 'keymap', 'isa', 'localtime', 'monitor',
127 'nographic', 'pae', 'rtc_timeoffset', 'serial', 'sdl',
128 'soundhw','stdvga', 'usb', 'usbdevice', 'vnc',
129 'vncconsole', 'vncdisplay', 'vnclisten',
130 'vncpasswd', 'vncunused', 'xauthority']
132 # Xen API console 'other_config' keys.
133 XENAPI_CONSOLE_OTHER_CFG = ['vncunused', 'vncdisplay', 'vnclisten',
134 'vncpasswd', 'type', 'display', 'xauthority',
135 'keymap']
137 # List of XendConfig configuration keys that have no direct equivalent
138 # in the old world.
140 XENAPI_CFG_TYPES = {
141 'uuid': str,
142 'name_label': str,
143 'name_description': str,
144 'user_version': str,
145 'is_a_template': bool0,
146 'resident_on': str,
147 'memory_static_min': int, # note these are stored in bytes, not KB!
148 'memory_static_max': int,
149 'memory_dynamic_min': int,
150 'memory_dynamic_max': int,
151 'cpus': list,
152 'vcpus_params': dict,
153 'VCPUs_max': int,
154 'VCPUs_at_startup': int,
155 'VCPUs_live': int,
156 'actions_after_shutdown': str,
157 'actions_after_reboot': str,
158 'actions_after_crash': str,
159 'PV_bootloader': str,
160 'PV_kernel': str,
161 'PV_ramdisk': str,
162 'PV_args': str,
163 'PV_bootloader_args': str,
164 'HVM_boot_policy': str,
165 'HVM_boot_params': dict,
166 'PCI_bus': str,
167 'platform': dict,
168 'tools_version': dict,
169 'other_config': dict,
170 'security_label': str,
171 }
173 # List of legacy configuration keys that have no equivalent in the
174 # Xen API, but are still stored in XendConfig.
176 LEGACY_UNSUPPORTED_BY_XENAPI_CFG = [
177 # roundtripped (dynamic, unmodified)
178 'shadow_memory',
179 'vcpu_avail',
180 'cpu_weight',
181 'cpu_cap',
182 'features',
183 # read/write
184 'on_xend_start',
185 'on_xend_stop',
186 # read-only
187 'domid',
188 'start_time',
189 'cpu_time',
190 'online_vcpus',
191 # write-once
192 'cpu',
193 'cpus',
194 ]
196 LEGACY_CFG_TYPES = {
197 'uuid': str,
198 'name': str,
199 'vcpus': int,
200 'vcpu_avail': long,
201 'memory': int,
202 'shadow_memory': int,
203 'maxmem': int,
204 'start_time': float,
205 'cpu_cap': int,
206 'cpu_weight': int,
207 'cpu_time': float,
208 'features': str,
209 'localtime': int,
210 'name': str,
211 'on_poweroff': str,
212 'on_reboot': str,
213 'on_crash': str,
214 'on_xend_stop': str,
215 'on_xend_start': str,
216 'online_vcpus': int,
217 'rtc/timeoffset': str,
218 }
220 # Values that should be stored in xenstore's /vm/<uuid> that is used
221 # by Xend. Used in XendDomainInfo to restore running VM state from
222 # xenstore.
223 LEGACY_XENSTORE_VM_PARAMS = [
224 'uuid',
225 'name',
226 'vcpus',
227 'vcpu_avail',
228 'memory',
229 'shadow_memory',
230 'maxmem',
231 'start_time',
232 'name',
233 'on_poweroff',
234 'on_crash',
235 'on_reboot',
236 'on_xend_start',
237 'on_xend_stop',
238 ]
240 DEFAULT_DM = '/usr/lib/xen/bin/qemu-dm'
242 ##
243 ## Config Choices
244 ##
246 CONFIG_RESTART_MODES = ('restart', 'destroy', 'preserve', 'rename-restart')
247 CONFIG_OLD_DOM_STATES = ('running', 'blocked', 'paused', 'shutdown',
248 'crashed', 'dying')
250 class XendConfigError(VmError):
251 def __str__(self):
252 return 'Invalid Configuration: %s' % str(self.value)
254 ##
255 ## XendConfig Class (an extended dictionary)
256 ##
258 class XendConfig(dict):
259 """ The new Xend VM Configuration.
261 Stores the configuration in xenapi compatible format but retains
262 import and export functions for SXP.
263 """
264 def __init__(self, filename = None, sxp_obj = None,
265 xapi = None, dominfo = None):
267 dict.__init__(self)
268 self.update(self._defaults())
270 if filename:
271 try:
272 sxp_obj = sxp.parse(open(filename,'r'))
273 sxp_obj = sxp_obj[0]
274 except IOError, e:
275 raise XendConfigError("Unable to read file: %s" % filename)
277 if sxp_obj:
278 self._sxp_to_xapi(sxp_obj)
279 self._sxp_to_xapi_unsupported(sxp_obj)
280 elif xapi:
281 self.update_with_xenapi_config(xapi)
282 elif dominfo:
283 # output from xc.domain_getinfo
284 self._dominfo_to_xapi(dominfo, update_mem = True)
286 log.debug('XendConfig.init: %s' % scrub_password(self))
288 # validators go here
289 self.validate()
291 """ In time, we should enable this type checking addition. It is great
292 also for tracking bugs and unintended writes to XendDomainInfo.info
293 def __setitem__(self, key, value):
294 type_conv = XENAPI_CFG_TYPES.get(key)
295 if callable(type_conv):
296 try:
297 dict.__setitem__(self, key, type_conv(value))
298 except (ValueError, TypeError):
299 raise XendConfigError("Wrong type for configuration value " +
300 "%s. Expected %s" %
301 (key, type_conv.__name__))
302 else:
303 dict.__setitem__(self, key, value)
304 """
306 def _defaults(self):
307 defaults = {
308 'name_label': 'Domain-Unnamed',
309 'actions_after_shutdown': 'destroy',
310 'actions_after_reboot': 'restart',
311 'actions_after_crash': 'restart',
312 'actions_after_suspend': '',
313 'is_a_template': False,
314 'is_control_domain': False,
315 'features': '',
316 'PV_bootloader': '',
317 'PV_kernel': '',
318 'PV_ramdisk': '',
319 'PV_args': '',
320 'PV_bootloader_args': '',
321 'HVM_boot_policy': '',
322 'HVM_boot_params': {},
323 'memory_static_min': 0,
324 'memory_dynamic_min': 0,
325 'shadow_memory': 0,
326 'memory_static_max': 0,
327 'memory_dynamic_max': 0,
328 'devices': {},
329 'on_xend_start': 'ignore',
330 'on_xend_stop': 'ignore',
331 'cpus': [],
332 'cpu_weight': 256,
333 'cpu_cap': 0,
334 'VCPUs_max': 1,
335 'VCPUs_live': 1,
336 'VCPUs_at_startup': 1,
337 'vcpus_params': {},
338 'console_refs': [],
339 'vif_refs': [],
340 'vbd_refs': [],
341 'vtpm_refs': [],
342 'other_config': {},
343 'platform': {}
344 }
346 return defaults
348 #
349 # Here we assume these values exist in the dict.
350 # If they don't we have a bigger problem, lets not
351 # try and 'fix it up' but acutually fix the cause ;-)
352 #
353 def _memory_sanity_check(self):
354 log.trace("_memory_sanity_check memory_static_min: %s, "
355 "memory_static_max: %i, "
356 "memory_dynamic_min: %i, "
357 "memory_dynamic_max: %i",
358 self["memory_static_min"],
359 self["memory_static_max"],
360 self["memory_dynamic_min"],
361 self["memory_dynamic_max"])
363 if not self["memory_static_min"] <= self["memory_static_max"]:
364 raise XendConfigError("memory_static_min must be less " \
365 "than or equal to memory_static_max")
366 if not self["memory_static_min"] <= self["memory_dynamic_min"]:
367 raise XendConfigError("memory_static_min must be less " \
368 "than or equal to memory_dynamic_min")
369 if not self["memory_dynamic_max"] <= self["memory_static_max"]:
370 raise XendConfigError("memory_dynamic_max must be less " \
371 "than or equal to memory_static_max")
372 if not self["memory_dynamic_max"] > 0:
373 raise XendConfigError("memory_dynamic_max must be greater " \
374 "than zero")
375 if not self["memory_static_max"] > 0:
376 raise XendConfigError("memory_static_max must be greater " \
377 "than zero")
379 def _actions_sanity_check(self):
380 for event in ['shutdown', 'reboot', 'crash']:
381 if self['actions_after_' + event] not in CONFIG_RESTART_MODES:
382 raise XendConfigError('Invalid event handling mode: ' +
383 event)
385 def _vcpus_sanity_check(self):
386 if 'VCPUs_max' in self and 'vcpu_avail' not in self:
387 self['vcpu_avail'] = (1 << self['VCPUs_max']) - 1
389 def _uuid_sanity_check(self):
390 """Make sure UUID is in proper string format with hyphens."""
391 if 'uuid' not in self or not self['uuid']:
392 self['uuid'] = uuid.createString()
393 else:
394 self['uuid'] = uuid.toString(uuid.fromString(self['uuid']))
396 def _name_sanity_check(self):
397 if 'name_label' not in self:
398 self['name_label'] = 'Domain-' + self['uuid']
400 def _platform_sanity_check(self):
401 if self.is_hvm():
402 if 'keymap' not in self['platform'] and XendOptions.instance().get_keymap():
403 self['platform']['keymap'] = XendOptions.instance().get_keymap()
405 if 'device_model' not in self['platform']:
406 self['platform']['device_model'] = DEFAULT_DM
408 # Compatibility hack, can go away soon.
409 if 'soundhw' not in self['platform'] and \
410 self['platform'].get('enable_audio'):
411 self['platform']['soundhw'] = 'sb16'
413 def validate(self):
414 self._uuid_sanity_check()
415 self._name_sanity_check()
416 self._memory_sanity_check()
417 self._actions_sanity_check()
418 self._vcpus_sanity_check()
419 self._platform_sanity_check()
421 def _dominfo_to_xapi(self, dominfo, update_mem = False):
422 self['domid'] = dominfo['domid']
423 self['online_vcpus'] = dominfo['online_vcpus']
424 self['VCPUs_max'] = dominfo['max_vcpu_id'] + 1
426 if update_mem:
427 self['memory_dynamic_min'] = dominfo['mem_kb'] * 1024
428 self['memory_dynamic_max'] = dominfo['mem_kb'] * 1024
429 self['memory_static_min'] = 0
430 self['memory_static_max'] = dominfo['maxmem_kb'] * 1024
431 self._memory_sanity_check()
433 self['cpu_time'] = dominfo['cpu_time']/1e9
434 if dominfo.get('ssidref'):
435 ssidref = int(dominfo.get('ssidref'))
436 import xen.util.xsm.xsm as security
437 self['security_label'] = security.ssidref2security_label(ssidref)
439 self['shutdown_reason'] = dominfo['shutdown_reason']
441 # parse state into Xen API states
442 self['running'] = dominfo['running']
443 self['crashed'] = dominfo['crashed']
444 self['dying'] = dominfo['dying']
445 self['shutdown'] = dominfo['shutdown']
446 self['paused'] = dominfo['paused']
447 self['blocked'] = dominfo['blocked']
449 if 'name' in dominfo:
450 self['name_label'] = dominfo['name']
452 if 'handle' in dominfo:
453 self['uuid'] = uuid.toString(dominfo['handle'])
455 def _parse_sxp(self, sxp_cfg):
456 """ Populate this XendConfig using the parsed SXP.
458 @param sxp_cfg: Parsed SXP Configuration
459 @type sxp_cfg: list of lists
460 @rtype: dictionary
461 @return: A dictionary containing the parsed options of the SXP.
462 """
463 cfg = {}
465 for key, typ in XENAPI_CFG_TYPES.items():
466 val = sxp.child_value(sxp_cfg, key)
467 if val is not None:
468 try:
469 cfg[key] = typ(val)
470 except (ValueError, TypeError), e:
471 log.warn('Unable to convert type value for key: %s' % key)
473 # Convert deprecated options to current equivalents.
475 restart = sxp.child_value(sxp_cfg, 'restart')
476 if restart:
477 if restart == 'onreboot':
478 cfg['on_poweroff'] = 'destroy'
479 cfg['on_reboot'] = 'restart'
480 cfg['on_crash'] = 'destroy'
481 elif restart == 'always':
482 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
483 cfg[opt] = 'restart'
484 elif restart == 'never':
485 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
486 cfg[opt] = 'never'
487 else:
488 log.warn('Ignoring unrecognised value for deprecated option:'
489 'restart = \'%s\'', restart)
491 # Handle memory, passed in as MiB
493 if sxp.child_value(sxp_cfg, "memory") != None:
494 cfg["memory"] = int(sxp.child_value(sxp_cfg, "memory"))
495 if sxp.child_value(sxp_cfg, "maxmem") != None:
496 cfg["maxmem"] = int(sxp.child_value(sxp_cfg, "maxmem"))
498 # Only extract options we know about.
499 extract_keys = LEGACY_UNSUPPORTED_BY_XENAPI_CFG
500 extract_keys += XENAPI_CFG_TO_LEGACY_CFG.values()
502 for key in extract_keys:
503 val = sxp.child_value(sxp_cfg, key)
504 if val != None:
505 try:
506 cfg[key] = LEGACY_CFG_TYPES[key](val)
507 except KeyError:
508 cfg[key] = val
509 except (TypeError, ValueError), e:
510 log.warn("Unable to parse key %s: %s: %s" %
511 (key, str(val), e))
513 if 'platform' not in cfg:
514 cfg['platform'] = {}
515 localtime = sxp.child_value(sxp_cfg, 'localtime')
516 if localtime is not None:
517 cfg['platform']['localtime'] = localtime
519 # Compatibility hack -- can go soon.
520 for key in XENAPI_PLATFORM_CFG:
521 val = sxp.child_value(sxp_cfg, "platform_" + key, None)
522 if val is not None:
523 self['platform'][key] = val
525 # Compatibility hack -- can go soon.
526 boot_order = sxp.child_value(sxp_cfg, 'HVM_boot')
527 if boot_order:
528 cfg['HVM_boot_policy'] = 'BIOS order'
529 cfg['HVM_boot_params'] = { 'order' : boot_order }
531 # Parsing the device SXP's. In most cases, the SXP looks
532 # like this:
533 #
534 # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]]
535 #
536 # However, for PCI devices it looks like this:
537 #
538 # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1]]]]
539 #
540 # It seems the reasoning for this difference is because
541 # pciif.py needs all the PCI device configurations at
542 # the same time when creating the devices.
543 #
544 # To further complicate matters, Xen 2.0 configuration format
545 # uses the following for pci device configuration:
546 #
547 # [device, [pci, [domain, 0], [bus, 0], [dev, 1], [func, 2]]]
548 #
549 # Hence we deal with pci device configurations outside of
550 # the regular device parsing.
552 cfg['devices'] = {}
553 for dev in sxp.children(sxp_cfg, 'device'):
554 config = sxp.child0(dev)
555 dev_type = sxp.name(config)
556 dev_info = {}
558 if dev_type == 'pci':
559 pci_devs_uuid = sxp.child_value(config, 'uuid',
560 uuid.createString())
561 pci_devs = []
562 for pci_dev in sxp.children(config, 'dev'):
563 pci_dev_info = {}
564 for opt_val in pci_dev[1:]:
565 try:
566 opt, val = opt_val
567 pci_dev_info[opt] = val
568 except TypeError:
569 pass
570 pci_devs.append(pci_dev_info)
572 cfg['devices'][pci_devs_uuid] = (dev_type,
573 {'devs': pci_devs,
574 'uuid': pci_devs_uuid})
576 log.debug("XendConfig: reading device: %s" % pci_devs)
577 else:
578 self.device_add(dev_type, cfg_sxp = config, target = cfg)
579 log.debug("XendConfig: reading device: %s" % scrub_password(dev_info))
581 # Extract missing data from configuration entries
582 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
583 if image_sxp:
584 image_vcpus = sxp.child_value(image_sxp, 'vcpus')
585 if image_vcpus != None:
586 try:
587 if 'VCPUs_max' not in cfg:
588 cfg['VCPUs_max'] = int(image_vcpus)
589 elif cfg['VCPUs_max'] != int(image_vcpus):
590 cfg['VCPUs_max'] = int(image_vcpus)
591 log.warn('Overriding vcpus from %d to %d using image'
592 'vcpus value.', cfg['VCPUs_max'])
593 except ValueError, e:
594 raise XendConfigError('integer expeceted: %s: %s' %
595 image_sxp, e)
597 # Deprecated cpu configuration
598 if 'cpu' in cfg:
599 if 'cpus' in cfg:
600 cfg['cpus'] = "%s,%s" % (str(cfg['cpu']), cfg['cpus'])
601 else:
602 cfg['cpus'] = str(cfg['cpu'])
604 # Convert 'cpus' to list of ints
605 if 'cpus' in cfg:
606 cpus = []
607 if type(cfg['cpus']) == list:
608 # If sxp_cfg was created from config.sxp,
609 # the form of 'cpus' is list of string.
610 # Convert 'cpus' to list of ints.
611 # ['1'] -> [1]
612 # ['0','2','3'] -> [0,2,3]
613 try:
614 for c in cfg['cpus']:
615 cpus.append(int(c))
617 cfg['cpus'] = cpus
618 except ValueError, e:
619 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
620 else:
621 # Convert 'cpus' string to list of ints
622 # 'cpus' supports a list of ranges (0-3),
623 # seperated by commas, and negation, (^1).
624 # Precedence is settled by order of the
625 # string:
626 # "0-3,^1" -> [0,2,3]
627 # "0-3,^1,1" -> [0,1,2,3]
628 try:
629 for c in cfg['cpus'].split(','):
630 if c.find('-') != -1:
631 (x, y) = c.split('-')
632 for i in range(int(x), int(y)+1):
633 cpus.append(int(i))
634 else:
635 # remove this element from the list
636 if c[0] == '^':
637 cpus = [x for x in cpus if x != int(c[1:])]
638 else:
639 cpus.append(int(c))
641 cfg['cpus'] = cpus
642 except ValueError, e:
643 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
645 if not 'security' in cfg and sxp.child_value(sxp_cfg, 'security'):
646 cfg['security'] = sxp.child_value(sxp_cfg, 'security')
647 if 'security' in cfg and not cfg.get('security_label'):
648 secinfo = cfg['security']
649 if isinstance(secinfo, list):
650 # The xm command sends a list formatted like this:
651 # [['access_control', ['policy', 'xm-test'],['label', 'red']],
652 # ['ssidref', 196611]]
653 policy = ""
654 label = ""
655 for idx in range(0, len(secinfo)):
656 if secinfo[idx][0] == "access_control":
657 for aidx in range(1, len(secinfo[idx])):
658 if secinfo[idx][aidx][0] == "policy":
659 policy = secinfo[idx][aidx][1]
660 if secinfo[idx][aidx][0] == "label":
661 label = secinfo[idx][aidx][1]
662 import xen.util.xsm.xsm as security
663 cfg['security_label'] = \
664 security.set_security_label(policy, label)
665 if not sxp.child_value(sxp_cfg, 'security_label'):
666 del cfg['security']
668 old_state = sxp.child_value(sxp_cfg, 'state')
669 if old_state:
670 for i in range(len(CONFIG_OLD_DOM_STATES)):
671 cfg[CONFIG_OLD_DOM_STATES[i]] = int(old_state[i] != '-')
673 return cfg
676 def _sxp_to_xapi(self, sxp_cfg):
677 """Read in an SXP Configuration object and
678 populate at much of the Xen API with valid values.
679 """
680 log.debug('_sxp_to_xapi(%s)' % scrub_password(sxp_cfg))
682 cfg = self._parse_sxp(sxp_cfg)
684 for key, typ in XENAPI_CFG_TYPES.items():
685 val = cfg.get(key)
686 if val is not None:
687 self[key] = typ(val)
689 # Convert parameters that can be directly mapped from
690 # the Legacy Config to Xen API Config
692 for apikey, cfgkey in XENAPI_CFG_TO_LEGACY_CFG.items():
693 try:
694 type_conv = XENAPI_CFG_TYPES.get(apikey)
695 if callable(type_conv):
696 self[apikey] = type_conv(cfg[cfgkey])
697 else:
698 log.warn("Unconverted key: " + apikey)
699 self[apikey] = cfg[cfgkey]
700 except KeyError:
701 pass
703 # Lets try and handle memory correctly
705 MiB = 1024 * 1024
707 if "memory" in cfg:
708 self["memory_static_min"] = 0
709 self["memory_static_max"] = int(cfg["memory"]) * MiB
710 self["memory_dynamic_min"] = int(cfg["memory"]) * MiB
711 self["memory_dynamic_max"] = int(cfg["memory"]) * MiB
713 if "maxmem" in cfg:
714 self["memory_static_max"] = int(cfg["maxmem"]) * MiB
716 self._memory_sanity_check()
718 def update_with(n, o):
719 if not self.get(n):
720 self[n] = cfg.get(o, '')
722 update_with('PV_bootloader', 'bootloader')
723 update_with('PV_bootloader_args', 'bootloader_args')
725 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
726 if image_sxp:
727 self.update_with_image_sxp(image_sxp)
729 # Convert Legacy HVM parameters to Xen API configuration
730 for key in XENAPI_PLATFORM_CFG:
731 if key in cfg:
732 self['platform'][key] = cfg[key]
734 # set device references in the configuration
735 self['devices'] = cfg.get('devices', {})
736 self['console_refs'] = cfg.get('console_refs', [])
737 self['vif_refs'] = cfg.get('vif_refs', [])
738 self['vbd_refs'] = cfg.get('vbd_refs', [])
739 self['vtpm_refs'] = cfg.get('vtpm_refs', [])
741 # coalesce hvm vnc frame buffer with vfb config
742 if self.is_hvm() and int(self['platform'].get('vnc', 0)) != 0:
743 # add vfb device if it isn't there already
744 has_rfb = False
745 for console_uuid in self['console_refs']:
746 if self['devices'][console_uuid][1].get('protocol') == 'rfb':
747 has_rfb = True
748 break
749 if self['devices'][console_uuid][0] == 'vfb':
750 has_rfb = True
751 break
753 if not has_rfb:
754 dev_config = ['vfb']
755 dev_config.append(['type', 'vnc'])
756 # copy VNC related params from platform config to vfb dev conf
757 for key in ['vncpasswd', 'vncunused', 'vncdisplay',
758 'vnclisten']:
759 if key in self['platform']:
760 dev_config.append([key, self['platform'][key]])
762 self.device_add('vfb', cfg_sxp = dev_config)
765 def _sxp_to_xapi_unsupported(self, sxp_cfg):
766 """Read in an SXP configuration object and populate
767 values are that not related directly supported in
768 the Xen API.
769 """
771 log.debug('_sxp_to_xapi_unsupported(%s)' % scrub_password(sxp_cfg))
773 # Parse and convert parameters used to configure
774 # the image (as well as HVM images)
775 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
776 if image_sxp:
777 image_type = sxp.name(image_sxp)
778 if image_type != 'hvm' and image_type != 'linux':
779 self['platform']['image_type'] = image_type
781 for key in XENAPI_PLATFORM_CFG:
782 val = sxp.child_value(image_sxp, key, None)
783 if val is not None and val != '':
784 self['platform'][key] = val
786 notes = sxp.children(image_sxp, 'notes')
787 if notes:
788 self['notes'] = self.notes_from_sxp(notes[0])
790 self._hvm_boot_params_from_sxp(image_sxp)
792 # extract backend value
794 backend = []
795 for c in sxp.children(sxp_cfg, 'backend'):
796 backend.append(sxp.name(sxp.child0(c)))
797 if backend:
798 self['backend'] = backend
800 # Parse and convert other Non Xen API parameters.
801 def _set_cfg_if_exists(sxp_arg):
802 val = sxp.child_value(sxp_cfg, sxp_arg)
803 if val != None:
804 if LEGACY_CFG_TYPES.get(sxp_arg):
805 self[sxp_arg] = LEGACY_CFG_TYPES[sxp_arg](val)
806 else:
807 self[sxp_arg] = val
809 _set_cfg_if_exists('shadow_memory')
810 _set_cfg_if_exists('features')
811 _set_cfg_if_exists('on_xend_stop')
812 _set_cfg_if_exists('on_xend_start')
813 _set_cfg_if_exists('vcpu_avail')
814 _set_cfg_if_exists('cpu_weight')
815 _set_cfg_if_exists('cpu_cap')
817 # Parse and store runtime configuration
818 _set_cfg_if_exists('start_time')
819 _set_cfg_if_exists('cpu_time')
820 _set_cfg_if_exists('shutdown_reason')
821 _set_cfg_if_exists('up_time')
822 _set_cfg_if_exists('status') # TODO, deprecated
824 def _get_old_state_string(self):
825 """Returns the old xm state string.
826 @rtype: string
827 @return: old state string
828 """
829 state_string = ''
830 for state_name in CONFIG_OLD_DOM_STATES:
831 on_off = self.get(state_name, 0)
832 if on_off:
833 state_string += state_name[0]
834 else:
835 state_string += '-'
837 return state_string
840 def update_config(self, dominfo):
841 """Update configuration with the output from xc.domain_getinfo().
843 @param dominfo: Domain information via xc.domain_getinfo()
844 @type dominfo: dict
845 """
846 self._dominfo_to_xapi(dominfo)
847 self.validate()
849 def update_with_xenapi_config(self, xapi):
850 """Update configuration with a Xen API VM struct
852 @param xapi: Xen API VM Struct
853 @type xapi: dict
854 """
856 log.debug('update_with_xenapi_config: %s' % scrub_password(xapi))
858 for key, val in xapi.items():
859 type_conv = XENAPI_CFG_TYPES.get(key)
860 if type_conv is None:
861 key = key.lower()
862 type_conv = XENAPI_CFG_TYPES.get(key)
863 if callable(type_conv):
864 self[key] = type_conv(val)
865 else:
866 self[key] = val
868 def to_sxp(self, domain = None, ignore_devices = False, ignore = [],
869 legacy_only = True):
870 """ Get SXP representation of this config object.
872 Incompat: removed store_mfn, console_mfn
874 @keyword domain: (optional) XendDomainInfo to get extra information
875 from such as domid and running devices.
876 @type domain: XendDomainInfo
877 @keyword ignore: (optional) list of 'keys' that we do not want
878 to export.
879 @type ignore: list of strings
880 @rtype: list of list (SXP representation)
881 """
882 sxpr = ['domain']
884 # TODO: domid/dom is the same thing but called differently
885 # depending if it is from xenstore or sxpr.
887 if domain.getDomid() is not None:
888 sxpr.append(['domid', domain.getDomid()])
890 if not legacy_only:
891 for name, typ in XENAPI_CFG_TYPES.items():
892 if name in self and self[name] not in (None, []):
893 if typ == dict:
894 s = self[name].items()
895 elif typ == list:
896 s = self[name]
897 else:
898 s = str(self[name])
899 sxpr.append([name, s])
901 for xenapi, legacy in XENAPI_CFG_TO_LEGACY_CFG.items():
902 if legacy in ('cpus'): # skip this
903 continue
904 if self.has_key(xenapi) and self[xenapi] not in (None, []):
905 if type(self[xenapi]) == bool:
906 # convert booleans to ints before making an sxp item
907 sxpr.append([legacy, int(self[xenapi])])
908 else:
909 sxpr.append([legacy, self[xenapi]])
911 MiB = 1024*1024
913 sxpr.append(["maxmem", int(self["memory_static_max"])/MiB])
914 sxpr.append(["memory", int(self["memory_dynamic_max"])/MiB])
916 for legacy in LEGACY_UNSUPPORTED_BY_XENAPI_CFG:
917 if legacy in ('domid', 'uuid', 'cpus'): # skip these
918 continue
919 if self.has_key(legacy) and self[legacy] not in (None, []):
920 sxpr.append([legacy, self[legacy]])
922 if self.has_key('security_label'):
923 sxpr.append(['security_label', self['security_label']])
925 sxpr.append(['image', self.image_sxpr()])
926 sxpr.append(['status', domain._stateGet()])
928 if domain.getDomid() is not None:
929 sxpr.append(['state', self._get_old_state_string()])
931 if domain:
932 if domain.store_mfn:
933 sxpr.append(['store_mfn', domain.store_mfn])
934 if domain.console_mfn:
935 sxpr.append(['console_mfn', domain.console_mfn])
938 # Marshall devices (running or from configuration)
939 if not ignore_devices:
940 for cls in XendDevices.valid_devices():
941 found = False
943 # figure if there is a dev controller is valid and running
944 if domain and domain.getDomid() != None:
945 try:
946 controller = domain.getDeviceController(cls)
947 configs = controller.configurations()
948 for config in configs:
949 if sxp.name(config) in ('vbd', 'tap'):
950 # The bootable flag is never written to the
951 # store as part of the device config.
952 dev_uuid = sxp.child_value(config, 'uuid')
953 dev_type, dev_cfg = self['devices'][dev_uuid]
954 is_bootable = dev_cfg.get('bootable', 0)
955 config.append(['bootable', int(is_bootable)])
957 sxpr.append(['device', config])
959 found = True
960 except:
961 log.exception("dumping sxp from device controllers")
962 pass
964 # if we didn't find that device, check the existing config
965 # for a device in the same class
966 if not found:
967 for dev_type, dev_info in self.all_devices_sxpr():
968 if dev_type == cls:
969 sxpr.append(['device', dev_info])
971 return sxpr
973 def _blkdev_name_to_number(self, dev):
974 if 'ioemu:' in dev:
975 _, dev = dev.split(':', 1)
976 try:
977 dev, _ = dev.split(':', 1)
978 except ValueError:
979 pass
981 try:
982 devid = int(dev)
983 except ValueError:
984 # devid is not a number but a string containing either device
985 # name (e.g. xvda) or device_type/device_id (e.g. vbd/51728)
986 dev2 = type(dev) is str and dev.split('/')[-1] or None
987 if dev2 == None:
988 log.debug("Could not check the device %s", dev)
989 return None
990 try:
991 devid = int(dev2)
992 except ValueError:
993 devid = blkdev_name_to_number(dev2)
994 if devid == None:
995 log.debug("The device %s is not device name", dev2)
996 return None
997 return devid
999 def device_duplicate_check(self, dev_type, dev_info, defined_config):
1000 defined_devices_sxpr = self.all_devices_sxpr(target = defined_config)
1002 if dev_type == 'vbd':
1003 dev_uname = dev_info.get('uname')
1004 blkdev_name = dev_info.get('dev')
1005 devid = self._blkdev_name_to_number(blkdev_name)
1006 if devid == None:
1007 return
1009 for o_dev_type, o_dev_info in defined_devices_sxpr:
1010 if dev_type == o_dev_type:
1011 if dev_uname == sxp.child_value(o_dev_info, 'uname'):
1012 raise XendConfigError('The uname "%s" is already defined' %
1013 dev_uname)
1014 o_blkdev_name = sxp.child_value(o_dev_info, 'dev')
1015 o_devid = self._blkdev_name_to_number(o_blkdev_name)
1016 if o_devid != None and devid == o_devid:
1017 raise XendConfigError('The device "%s" is already defined' %
1018 blkdev_name)
1020 elif dev_type == 'vif':
1021 dev_mac = dev_info.get('mac')
1023 for o_dev_type, o_dev_info in defined_devices_sxpr:
1024 if dev_type == o_dev_type:
1025 if dev_mac == sxp.child_value(o_dev_info, 'mac'):
1026 raise XendConfigError('The mac "%s" is already defined' %
1027 dev_mac)
1029 def device_add(self, dev_type, cfg_sxp = None, cfg_xenapi = None,
1030 target = None):
1031 """Add a device configuration in SXP format or XenAPI struct format.
1033 For SXP, it could be either:
1035 [device, [vbd, [uname ...]]
1037 or:
1039 [vbd, [uname ..]]
1041 @type cfg_sxp: list of lists (parsed sxp object)
1042 @param cfg_sxp: SXP configuration object
1043 @type cfg_xenapi: dict
1044 @param cfg_xenapi: A device configuration from Xen API (eg. vbd,vif)
1045 @param target: write device information to
1046 @type target: None or a dictionary
1047 @rtype: string
1048 @return: Assigned UUID of the device.
1049 """
1050 if target == None:
1051 target = self
1053 if dev_type not in XendDevices.valid_devices():
1054 raise XendConfigError("XendConfig: %s not a valid device type" %
1055 dev_type)
1057 if cfg_sxp == None and cfg_xenapi == None:
1058 raise XendConfigError("XendConfig: device_add requires some "
1059 "config.")
1061 #if cfg_sxp:
1062 # log.debug("XendConfig.device_add: %s" % str(cfg_sxp))
1063 #if cfg_xenapi:
1064 # log.debug("XendConfig.device_add: %s" % str(cfg_xenapi))
1066 if cfg_sxp:
1067 if sxp.child0(cfg_sxp) == 'device':
1068 config = sxp.child0(cfg_sxp)
1069 else:
1070 config = cfg_sxp
1072 dev_type = sxp.name(config)
1073 dev_info = {}
1075 for opt_val in config[1:]:
1076 try:
1077 opt, val = opt_val
1078 dev_info[opt] = val
1079 except (TypeError, ValueError): # unpack error
1080 pass
1082 if dev_type == 'vbd':
1083 dev_info['bootable'] = 0
1084 if dev_info.get('dev', '').startswith('ioemu:'):
1085 dev_info['driver'] = 'ioemu'
1086 else:
1087 dev_info['driver'] = 'paravirtualised'
1089 if dev_type == 'tap':
1090 if dev_info['uname'].split(':')[1] not in blktap_disk_types:
1091 raise XendConfigError("tap:%s not a valid disk type" %
1092 dev_info['uname'].split(':')[1])
1094 if dev_type == 'vif':
1095 if not dev_info.get('mac'):
1096 dev_info['mac'] = randomMAC()
1098 self.device_duplicate_check(dev_type, dev_info, target)
1100 if dev_type == 'vif':
1101 if dev_info.get('policy') and dev_info.get('label'):
1102 dev_info['security_label'] = "%s:%s:%s" % \
1103 (xsconstants.ACM_POLICY_ID,
1104 dev_info['policy'],dev_info['label'])
1106 # create uuid if it doesn't exist
1107 dev_uuid = dev_info.get('uuid', None)
1108 if not dev_uuid:
1109 dev_uuid = uuid.createString()
1110 dev_info['uuid'] = dev_uuid
1112 # store dev references by uuid for certain device types
1113 target['devices'][dev_uuid] = (dev_type, dev_info)
1114 if dev_type in ('vif', 'vbd', 'vtpm'):
1115 param = '%s_refs' % dev_type
1116 if param not in target:
1117 target[param] = []
1118 if dev_uuid not in target[param]:
1119 if dev_type == 'vbd':
1120 # Compat hack -- mark first disk bootable
1121 dev_info['bootable'] = int(not target[param])
1122 target[param].append(dev_uuid)
1123 elif dev_type == 'tap':
1124 if 'vbd_refs' not in target:
1125 target['vbd_refs'] = []
1126 if dev_uuid not in target['vbd_refs']:
1127 # Compat hack -- mark first disk bootable
1128 dev_info['bootable'] = int(not target['vbd_refs'])
1129 target['vbd_refs'].append(dev_uuid)
1131 elif dev_type == 'vfb':
1132 # Populate other config with aux data that is associated
1133 # with vfb
1135 other_config = {}
1136 for key in XENAPI_CONSOLE_OTHER_CFG:
1137 if key in dev_info:
1138 other_config[key] = dev_info[key]
1139 target['devices'][dev_uuid][1]['other_config'] = other_config
1142 if 'console_refs' not in target:
1143 target['console_refs'] = []
1145 # Treat VFB devices as console devices so they are found
1146 # through Xen API
1147 if dev_uuid not in target['console_refs']:
1148 target['console_refs'].append(dev_uuid)
1150 elif dev_type == 'console':
1151 if 'console_refs' not in target:
1152 target['console_refs'] = []
1153 if dev_uuid not in target['console_refs']:
1154 target['console_refs'].append(dev_uuid)
1156 return dev_uuid
1158 if cfg_xenapi:
1159 dev_info = {}
1160 dev_uuid = ''
1161 if dev_type == 'vif':
1162 dev_info['mac'] = cfg_xenapi.get('MAC')
1163 if not dev_info['mac']:
1164 dev_info['mac'] = randomMAC()
1165 # vifname is the name on the guest, not dom0
1166 # TODO: we don't have the ability to find that out or
1167 # change it from dom0
1168 #if cfg_xenapi.get('device'): # don't add if blank
1169 # dev_info['vifname'] = cfg_xenapi.get('device')
1170 if cfg_xenapi.get('type'):
1171 dev_info['type'] = cfg_xenapi.get('type')
1172 if cfg_xenapi.get('name'):
1173 dev_info['name'] = cfg_xenapi.get('name')
1174 if cfg_xenapi.get('network'):
1175 network = XendAPIStore.get(
1176 cfg_xenapi.get('network'), 'network')
1177 dev_info['bridge'] = network.get_name_label()
1179 if cfg_xenapi.get('security_label'):
1180 dev_info['security_label'] = \
1181 cfg_xenapi.get('security_label')
1183 dev_uuid = cfg_xenapi.get('uuid', None)
1184 if not dev_uuid:
1185 dev_uuid = uuid.createString()
1186 dev_info['uuid'] = dev_uuid
1187 target['devices'][dev_uuid] = (dev_type, dev_info)
1188 target['vif_refs'].append(dev_uuid)
1190 elif dev_type in ('vbd', 'tap'):
1191 dev_info['type'] = cfg_xenapi.get('type', 'Disk')
1192 if dev_info['type'] == 'CD':
1193 old_vbd_type = 'cdrom'
1194 else:
1195 old_vbd_type = 'disk'
1197 dev_info['uname'] = cfg_xenapi.get('image', '')
1198 dev_info['dev'] = '%s:%s' % (cfg_xenapi.get('device'),
1199 old_vbd_type)
1200 dev_info['bootable'] = int(cfg_xenapi.get('bootable', 0))
1201 dev_info['driver'] = cfg_xenapi.get('driver', '')
1202 dev_info['VDI'] = cfg_xenapi.get('VDI', '')
1204 if cfg_xenapi.get('mode') == 'RW':
1205 dev_info['mode'] = 'w'
1206 else:
1207 dev_info['mode'] = 'r'
1209 dev_uuid = cfg_xenapi.get('uuid', None)
1210 if not dev_uuid:
1211 dev_uuid = uuid.createString()
1212 dev_info['uuid'] = dev_uuid
1213 target['devices'][dev_uuid] = (dev_type, dev_info)
1214 target['vbd_refs'].append(dev_uuid)
1216 elif dev_type == 'vtpm':
1217 if cfg_xenapi.get('type'):
1218 dev_info['type'] = cfg_xenapi.get('type')
1220 dev_uuid = cfg_xenapi.get('uuid', None)
1221 if not dev_uuid:
1222 dev_uuid = uuid.createString()
1223 dev_info['uuid'] = dev_uuid
1224 target['devices'][dev_uuid] = (dev_type, dev_info)
1225 target['vtpm_refs'].append(dev_uuid)
1227 elif dev_type == 'console':
1228 dev_uuid = cfg_xenapi.get('uuid', None)
1229 if not dev_uuid:
1230 dev_uuid = uuid.createString()
1231 dev_info['uuid'] = dev_uuid
1232 dev_info['protocol'] = cfg_xenapi.get('protocol', 'rfb')
1233 dev_info['other_config'] = cfg_xenapi.get('other_config', {})
1234 if dev_info['protocol'] == 'rfb':
1235 # collapse other config into devinfo for things
1236 # such as vncpasswd, vncunused, etc.
1237 dev_info.update(cfg_xenapi.get('other_config', {}))
1238 dev_info['type'] = 'vnc'
1239 target['devices'][dev_uuid] = ('vfb', dev_info)
1240 target['console_refs'].append(dev_uuid)
1242 # Finally, if we are a pvfb, we need to make a vkbd
1243 # as well that is not really exposed to Xen API
1244 vkbd_uuid = uuid.createString()
1245 target['devices'][vkbd_uuid] = ('vkbd', {})
1247 elif dev_info['protocol'] == 'vt100':
1248 # if someone tries to create a VT100 console
1249 # via the Xen API, we'll have to ignore it
1250 # because we create one automatically in
1251 # XendDomainInfo._update_consoles
1252 raise XendConfigError('Creating vt100 consoles via '
1253 'Xen API is unsupported')
1255 return dev_uuid
1257 # no valid device to add
1258 return ''
1260 def phantom_device_add(self, dev_type, cfg_xenapi = None,
1261 target = None):
1262 """Add a phantom tap device configuration in XenAPI struct format.
1263 """
1265 if target == None:
1266 target = self
1268 if dev_type not in XendDevices.valid_devices() and \
1269 dev_type not in XendDevices.pseudo_devices():
1270 raise XendConfigError("XendConfig: %s not a valid device type" %
1271 dev_type)
1273 if cfg_xenapi == None:
1274 raise XendConfigError("XendConfig: device_add requires some "
1275 "config.")
1277 if cfg_xenapi:
1278 log.debug("XendConfig.phantom_device_add: %s" % str(cfg_xenapi))
1280 if cfg_xenapi:
1281 dev_info = {}
1282 if dev_type in ('vbd', 'tap'):
1283 if dev_type == 'vbd':
1284 dev_info['uname'] = cfg_xenapi.get('image', '')
1285 dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
1286 elif dev_type == 'tap':
1287 if cfg_xenapi.get('image').find('tap:') == -1:
1288 dev_info['uname'] = 'tap:qcow:%s' % cfg_xenapi.get('image')
1289 dev_info['dev'] = '/dev/%s' % cfg_xenapi.get('device')
1290 dev_info['uname'] = cfg_xenapi.get('image')
1291 dev_info['mode'] = cfg_xenapi.get('mode')
1292 dev_info['backend'] = '0'
1293 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
1294 dev_info['uuid'] = dev_uuid
1295 self['devices'][dev_uuid] = (dev_type, dev_info)
1296 self['vbd_refs'].append(dev_uuid)
1297 return dev_uuid
1299 return ''
1301 def console_add(self, protocol, location, other_config = {}):
1302 dev_uuid = uuid.createString()
1303 if protocol == 'vt100':
1304 dev_info = {
1305 'uuid': dev_uuid,
1306 'protocol': protocol,
1307 'location': location,
1308 'other_config': other_config,
1311 if 'devices' not in self:
1312 self['devices'] = {}
1314 self['devices'][dev_uuid] = ('console', dev_info)
1315 self['console_refs'].append(dev_uuid)
1316 return dev_info
1318 return {}
1320 def console_update(self, console_uuid, key, value):
1321 for dev_uuid, (dev_type, dev_info) in self['devices'].items():
1322 if dev_uuid == console_uuid:
1323 dev_info[key] = value
1324 # collapse other_config into dev_info for things
1325 # such as vncpasswd, vncunused, etc.
1326 if key == 'other_config':
1327 for k in XENAPI_CONSOLE_OTHER_CFG:
1328 if k in dev_info and k not in value:
1329 del dev_info[k]
1330 dev_info.update(value)
1331 break
1333 def console_get_all(self, protocol):
1334 if protocol == 'vt100':
1335 consoles = [dinfo for dtype, dinfo in self['devices'].values()
1336 if dtype == 'console']
1337 return [c for c in consoles if c.get('protocol') == protocol]
1339 elif protocol == 'rfb':
1340 vfbs = [dinfo for dtype, dinfo in self['devices'].values()
1341 if dtype == 'vfb']
1343 # move all non-console key values to other_config before
1344 # returning console config
1345 valid_keys = ['uuid', 'location']
1346 for vfb in vfbs:
1347 other_config = {}
1348 for key, val in vfb.items():
1349 if key not in valid_keys:
1350 other_config[key] = vfb[key]
1351 del vfb[key]
1352 vfb['other_config'] = other_config
1353 vfb['protocol'] = 'rfb'
1355 return vfbs
1357 else:
1358 return []
1360 def device_update(self, dev_uuid, cfg_sxp = [], cfg_xenapi = {}):
1361 """Update an existing device with the new configuration.
1363 @rtype: boolean
1364 @return: Returns True if succesfully found and updated a device conf
1365 """
1366 if dev_uuid in self['devices'] and cfg_sxp:
1367 if sxp.child0(cfg_sxp) == 'device':
1368 config = sxp.child0(cfg_sxp)
1369 else:
1370 config = cfg_sxp
1372 dev_type, dev_info = self['devices'][dev_uuid]
1373 for opt_val in config[1:]:
1374 try:
1375 opt, val = opt_val
1376 dev_info[opt] = val
1377 except (TypeError, ValueError):
1378 pass # no value for this config option
1380 self['devices'][dev_uuid] = (dev_type, dev_info)
1381 return True
1383 elif dev_uuid in self['devices'] and cfg_xenapi:
1384 dev_type, dev_info = self['devices'][dev_uuid]
1385 for key, val in cfg_xenapi.items():
1386 dev_info[key] = val
1387 self['devices'][dev_uuid] = (dev_type, dev_info)
1389 return False
1392 def device_sxpr(self, dev_uuid = None, dev_type = None, dev_info = None, target = None):
1393 """Get Device SXPR by either giving the device UUID or (type, config).
1395 @rtype: list of lists
1396 @return: device config sxpr
1397 """
1398 sxpr = []
1400 if target == None:
1401 target = self
1403 if dev_uuid != None and dev_uuid in target['devices']:
1404 dev_type, dev_info = target['devices'][dev_uuid]
1406 if dev_type == None or dev_info == None:
1407 raise XendConfigError("Required either UUID or device type and "
1408 "configuration dictionary.")
1410 sxpr.append(dev_type)
1411 if dev_type in ('console', 'vfb'):
1412 config = [(opt, val) for opt, val in dev_info.items()
1413 if opt != 'other_config']
1414 else:
1415 config = [(opt, val) for opt, val in dev_info.items()]
1417 sxpr += config
1419 return sxpr
1421 def ordered_device_refs(self, target = None):
1422 result = []
1424 if target == None:
1425 target = self
1427 # vkbd devices *must* be before vfb devices, otherwise
1428 # there is a race condition when setting up devices
1429 # where the daemon spawned for the vfb may write stuff
1430 # into xenstore vkbd backend, before DevController has
1431 # setup permissions on the vkbd backend path. This race
1432 # results in domain creation failing with 'device already
1433 # connected' messages
1434 result.extend([u for u in target['devices'].keys() if target['devices'][u][0] == 'vkbd'])
1436 result.extend(target.get('console_refs', []) +
1437 target.get('vbd_refs', []) +
1438 target.get('vif_refs', []) +
1439 target.get('vtpm_refs', []))
1441 result.extend([u for u in target['devices'].keys() if u not in result])
1442 return result
1444 def all_devices_sxpr(self, target = None):
1445 """Returns the SXPR for all devices in the current configuration."""
1446 sxprs = []
1447 pci_devs = []
1449 if target == None:
1450 target = self
1452 if 'devices' not in target:
1453 return sxprs
1455 ordered_refs = self.ordered_device_refs(target = target)
1456 for dev_uuid in ordered_refs:
1457 dev_type, dev_info = target['devices'][dev_uuid]
1458 if dev_type == 'pci': # special case for pci devices
1459 sxpr = [['uuid', dev_info['uuid']]]
1460 for pci_dev_info in dev_info['devs']:
1461 pci_dev_sxpr = ['dev']
1462 for opt, val in pci_dev_info.items():
1463 pci_dev_sxpr.append([opt, val])
1464 sxpr.append(pci_dev_sxpr)
1465 sxprs.append((dev_type, sxpr))
1466 else:
1467 sxpr = self.device_sxpr(dev_type = dev_type,
1468 dev_info = dev_info,
1469 target = target)
1470 sxprs.append((dev_type, sxpr))
1472 return sxprs
1474 def image_sxpr(self):
1475 """Returns a backwards compatible image SXP expression that is
1476 used in xenstore's /vm/<uuid>/image value and xm list."""
1477 image = [self.image_type()]
1478 if self.has_key('PV_kernel'):
1479 image.append(['kernel', self['PV_kernel']])
1480 if self.has_key('PV_ramdisk') and self['PV_ramdisk']:
1481 image.append(['ramdisk', self['PV_ramdisk']])
1482 if self.has_key('PV_args') and self['PV_args']:
1483 image.append(['args', self['PV_args']])
1485 for key in XENAPI_PLATFORM_CFG:
1486 if key in self['platform']:
1487 image.append([key, self['platform'][key]])
1489 if 'notes' in self:
1490 image.append(self.notes_sxp(self['notes']))
1492 return image
1494 def update_with_image_sxp(self, image_sxp, bootloader = False):
1495 # Convert Legacy "image" config to Xen API PV_*
1496 # configuration
1497 log.debug("update_with_image_sxp(%s)" % scrub_password(image_sxp))
1499 # user-specified args must come last: previous releases did this and
1500 # some domU kernels rely upon the ordering.
1501 kernel_args = sxp.child_value(image_sxp, 'args', '')
1503 # attempt to extract extra arguments from SXP config
1504 arg_ip = sxp.child_value(image_sxp, 'ip')
1505 if arg_ip and not re.search(r'ip=[^ ]+', kernel_args):
1506 kernel_args = 'ip=%s ' % arg_ip + kernel_args
1507 arg_root = sxp.child_value(image_sxp, 'root')
1508 if arg_root and not re.search(r'root=', kernel_args):
1509 kernel_args = 'root=%s ' % arg_root + kernel_args
1511 if bootloader:
1512 self['_temp_using_bootloader'] = '1'
1513 self['_temp_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1514 self['_temp_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1515 self['_temp_args'] = kernel_args
1516 else:
1517 self['PV_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1518 self['PV_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1519 self['PV_args'] = kernel_args
1521 for key in XENAPI_PLATFORM_CFG:
1522 val = sxp.child_value(image_sxp, key, None)
1523 if val is not None and val != '':
1524 self['platform'][key] = val
1526 notes = sxp.children(image_sxp, 'notes')
1527 if notes:
1528 self['notes'] = self.notes_from_sxp(notes[0])
1530 self._hvm_boot_params_from_sxp(image_sxp)
1532 def set_notes(self, notes):
1533 'Add parsed elfnotes to image'
1534 self['notes'] = notes
1536 def get_notes(self):
1537 try:
1538 return self['notes'] or {}
1539 except KeyError:
1540 return {}
1542 def notes_from_sxp(self, nsxp):
1543 notes = {}
1544 for note in sxp.children(nsxp):
1545 notes[note[0]] = note[1]
1546 return notes
1548 def notes_sxp(self, notes):
1549 nsxp = ['notes']
1550 for k, v in notes.iteritems():
1551 nsxp.append([k, str(v)])
1552 return nsxp
1554 def _hvm_boot_params_from_sxp(self, image_sxp):
1555 boot = sxp.child_value(image_sxp, 'boot', None)
1556 if boot is not None:
1557 self['HVM_boot_policy'] = 'BIOS order'
1558 self['HVM_boot_params'] = { 'order' : boot }
1560 def is_hvm(self):
1561 return self['HVM_boot_policy'] != ''
1563 def image_type(self):
1564 stored_type = self['platform'].get('image_type')
1565 return stored_type or (self.is_hvm() and 'hvm' or 'linux')