ia64/xen-unstable

view tools/python/xen/xend/XendConfig.py @ 13813:1fb0302683d3

Fix handling of HVM boot.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author Ewan Mellor <ewan@xensource.com>
date Thu Feb 01 15:32:16 2007 +0000 (2007-02-01)
parents 868babf6b1dd
children e0b7ab2a1d56
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 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.XendError import VmError
26 from xen.xend.XendDevices import XendDevices
27 from xen.xend.PrettyPrint import prettyprintstring
28 from xen.xend.XendConstants import DOM_STATE_HALTED
30 log = logging.getLogger("xend.XendConfig")
31 log.setLevel(logging.DEBUG)
34 """
35 XendConfig API
37 XendConfig will try to mirror as closely the Xen API VM Struct
38 with extra parameters for those options that are not supported.
40 """
42 def reverse_dict(adict):
43 """Return the reverse mapping of a dictionary."""
44 return dict([(v, k) for k, v in adict.items()])
46 def bool0(v):
47 return v != '0' and bool(v)
49 # Recursively copy a data struct, scrubbing out VNC passwords.
50 # Will scrub any dict entry with a key of 'vncpasswd' or any
51 # 2-element list whose first member is 'vncpasswd'. It will
52 # also scrub a string matching '(vncpasswd XYZ)'. Everything
53 # else is no-op passthrough
54 def scrub_password(data):
55 if type(data) == dict or type(data) == XendConfig:
56 scrubbed = {}
57 for key in data.keys():
58 if key == "vncpasswd":
59 scrubbed[key] = "XXXXXXXX"
60 else:
61 scrubbed[key] = scrub_password(data[key])
62 return scrubbed
63 elif type(data) == list:
64 if len(data) == 2 and type(data[0]) == str and data[0] == 'vncpasswd':
65 return ['vncpasswd', 'XXXXXXXX']
66 else:
67 scrubbed = []
68 for entry in data:
69 scrubbed.append(scrub_password(entry))
70 return scrubbed
71 elif type(data) == tuple:
72 scrubbed = []
73 for entry in data:
74 scrubbed.append(scrub_password(entry))
75 return tuple(scrubbed)
76 elif type(data) == str:
77 return re.sub(r'\(vncpasswd\s+[^\)]+\)','(vncpasswd XXXXXX)', data)
78 else:
79 return data
81 #
82 # CPU fields:
83 #
84 # vcpus_number -- the maximum number of vcpus that this domain may ever have.
85 # aka XendDomainInfo.getVCpuCount().
86 # vcpus -- the legacy configuration name for above.
87 # max_vcpu_id -- vcpus_number - 1. This is given to us by Xen.
88 #
89 # cpus -- the list of pCPUs available to each vCPU.
90 #
91 # vcpu_avail: a bitmap telling the guest domain whether it may use each of
92 # its VCPUs. This is translated to
93 # <dompath>/cpu/<id>/availability = {online,offline} for use
94 # by the guest domain.
95 # online_vpcus -- the number of VCPUs currently up, as reported by Xen. This
96 # is changed by changing vcpu_avail, and waiting for the
97 # domain to respond.
98 #
101 # Mapping from XendConfig configuration keys to the old
102 # legacy configuration keys that map directly.
104 XENAPI_CFG_TO_LEGACY_CFG = {
105 'uuid': 'uuid',
106 'vcpus_number': 'vcpus',
107 'cpus': 'cpus',
108 'memory_static_min': 'memory',
109 'memory_static_max': 'maxmem',
110 'name_label': 'name',
111 'actions_after_shutdown': 'on_poweroff',
112 'actions_after_reboot': 'on_reboot',
113 'actions_after_crash': 'on_crash',
114 'platform_localtime': 'localtime',
115 'PV_bootloader': 'bootloader',
116 'PV_bootloader_args': 'bootloader_args',
117 }
119 LEGACY_CFG_TO_XENAPI_CFG = reverse_dict(XENAPI_CFG_TO_LEGACY_CFG)
121 # Mapping from XendConfig configuration keys to the old
122 # legacy configuration keys that are found in the 'image'
123 # SXP object.
124 XENAPI_HVM_CFG = {
125 'platform_std_vga': 'stdvga',
126 'platform_serial' : 'serial',
127 'platform_localtime': 'localtime',
128 'platform_keymap' : 'keymap'
129 }
131 # List of XendConfig configuration keys that have no direct equivalent
132 # in the old world.
134 XENAPI_CFG_TYPES = {
135 'uuid': str,
136 'power_state': str,
137 'name_label': str,
138 'name_description': str,
139 'user_version': str,
140 'is_a_template': bool0,
141 'resident_on': str,
142 'memory_static_min': int,
143 'memory_static_max': int,
144 'memory_dynamic_min': int,
145 'memory_dynamic_max': int,
146 'memory_actual': int,
147 'cpus': list,
148 'vcpus_policy': str,
149 'vcpus_params': str,
150 'vcpus_number': int,
151 'vcpus_features_required': list,
152 'vcpus_features_can_use': list,
153 'vcpus_features_force_on': list,
154 'vcpus_features_force_off': list,
155 'actions_after_shutdown': str,
156 'actions_after_reboot': str,
157 'actions_after_suspend': str,
158 'actions_after_crash': str,
159 'tpm_instance': int,
160 'tpm_backend': int,
161 'PV_bootloader': str,
162 'PV_kernel': str,
163 'PV_ramdisk': str,
164 'PV_args': str,
165 'PV_bootloader_args': str,
166 'HVM_boot_policy': str,
167 'HVM_boot_params': dict,
168 'platform_std_vga': bool0,
169 'platform_serial': str,
170 'platform_localtime': bool0,
171 'platform_clock_offset': bool0,
172 'platform_enable_audio': bool0,
173 'platform_keymap': str,
174 'pci_bus': str,
175 'tools_version': dict,
176 'otherconfig': dict,
177 }
179 # List of legacy configuration keys that have no equivalent in the
180 # Xen API, but are still stored in XendConfig.
182 LEGACY_UNSUPPORTED_BY_XENAPI_CFG = [
183 # roundtripped (dynamic, unmodified)
184 'shadow_memory',
185 'security',
186 'vcpu_avail',
187 'cpu_weight',
188 'cpu_cap',
189 'features',
190 # read/write
191 'on_xend_start',
192 'on_xend_stop',
193 # read-only
194 'domid',
195 'start_time',
196 'cpu_time',
197 'online_vcpus',
198 # write-once
199 'cpu',
200 'cpus',
201 ]
203 LEGACY_CFG_TYPES = {
204 'uuid': str,
205 'name': str,
206 'vcpus': int,
207 'vcpu_avail': long,
208 'memory': int,
209 'shadow_memory': int,
210 'maxmem': int,
211 'start_time': float,
212 'cpu_cap': int,
213 'cpu_weight': int,
214 'cpu_time': float,
215 'features': str,
216 'localtime': int,
217 'name': str,
218 'on_poweroff': str,
219 'on_reboot': str,
220 'on_crash': str,
221 'on_xend_stop': str,
222 'on_xend_start': str,
223 'online_vcpus': int,
224 }
226 # Values that should be stored in xenstore's /vm/<uuid> that is used
227 # by Xend. Used in XendDomainInfo to restore running VM state from
228 # xenstore.
229 LEGACY_XENSTORE_VM_PARAMS = [
230 'uuid',
231 'name',
232 'vcpus',
233 'vcpu_avail',
234 'memory',
235 'shadow_memory',
236 'maxmem',
237 'start_time',
238 'name',
239 'on_poweroff',
240 'on_crash',
241 'on_reboot',
242 'on_xend_start',
243 'on_xend_stop',
244 ]
246 LEGACY_IMAGE_CFG = [
247 ('root', str),
248 ('ip', str),
249 ('nographic', int),
250 ('vnc', int),
251 ('sdl', int),
252 ('vncdisplay', int),
253 ('vncunused', int),
254 ('vncpasswd', str),
255 ('vnclisten', str),
256 ]
258 LEGACY_IMAGE_HVM_CFG = [
259 ('device_model', str),
260 ('display', str),
261 ('xauthority', str),
262 ('vncconsole', int),
263 ('pae', int),
264 ('apic', int),
265 ]
267 LEGACY_IMAGE_HVM_DEVICES_CFG = [
268 ('acpi', int),
269 ('boot', str),
270 ('fda', str),
271 ('fdb', str),
272 ('isa', int),
273 ('keymap', str),
274 ('localtime', int),
275 ('serial', str),
276 ('stdvga', int),
277 ('soundhw', str),
278 ('usb', int),
279 ('usbdevice', str),
280 ('vcpus', int),
281 ]
283 LEGACY_DM = '/usr/lib/xen/bin/qemu-dm'
285 ##
286 ## Config Choices
287 ##
289 CONFIG_RESTART_MODES = ('restart', 'destroy', 'preserve', 'rename-restart')
290 CONFIG_OLD_DOM_STATES = ('running', 'blocked', 'paused', 'shutdown',
291 'crashed', 'dying')
293 class XendConfigError(VmError):
294 def __str__(self):
295 return 'Invalid Configuration: %s' % str(self.value)
297 ##
298 ## XendConfig Class (an extended dictionary)
299 ##
301 class XendConfig(dict):
302 """ The new Xend VM Configuration.
304 Stores the configuration in xenapi compatible format but retains
305 import and export functions for SXP.
306 """
307 def __init__(self, filename = None, sxp_obj = None,
308 xapi = None, dominfo = None):
310 dict.__init__(self)
311 self.update(self._defaults())
313 if filename:
314 try:
315 sxp_obj = sxp.parse(open(filename,'r'))
316 sxp_obj = sxp_obj[0]
317 except IOError, e:
318 raise XendConfigError("Unable to read file: %s" % filename)
320 if sxp_obj:
321 self._sxp_to_xapi(sxp_obj)
322 self._sxp_to_xapi_unsupported(sxp_obj)
323 elif xapi:
324 self.update_with_xenapi_config(xapi)
325 self._add_xapi_unsupported(xapi)
326 elif dominfo:
327 # output from xc.domain_getinfo
328 self._dominfo_to_xapi(dominfo)
330 log.debug('XendConfig.init: %s' % scrub_password(self))
332 # validators go here
333 self.validate()
335 """ In time, we should enable this type checking addition. It is great
336 also for tracking bugs and unintended writes to XendDomainInfo.info
337 def __setitem__(self, key, value):
338 type_conv = XENAPI_CFG_TYPES.get(key)
339 if callable(type_conv):
340 try:
341 dict.__setitem__(self, key, type_conv(value))
342 except (ValueError, TypeError):
343 raise XendConfigError("Wrong type for configuration value " +
344 "%s. Expected %s" %
345 (key, type_conv.__name__))
346 else:
347 dict.__setitem__(self, key, value)
348 """
350 def _defaults(self):
351 defaults = {
352 'uuid': uuid.createString(),
353 'name_label': 'Domain-Unnamed',
354 'actions_after_shutdown': 'destroy',
355 'actions_after_reboot': 'restart',
356 'actions_after_crash': 'restart',
357 'actions_after_suspend': '',
358 'features': '',
359 'PV_bootloader': '',
360 'PV_kernel': '',
361 'PV_ramdisk': '',
362 'PV_args': '',
363 'PV_bootloader_args': '',
364 'HVM_boot_policy': '',
365 'HVM_boot_params': {},
366 'memory_static_min': 0,
367 'memory_dynamic_min': 0,
368 'shadow_memory': 0,
369 'memory_static_max': 0,
370 'memory_dynamic_max': 0,
371 'memory_actual': 0,
372 'devices': {},
373 'image': {},
374 'security': None,
375 'on_xend_start': 'ignore',
376 'on_xend_stop': 'ignore',
377 'cpus': [],
378 'cpu_weight': 256,
379 'cpu_cap': 0,
380 'vcpus_number': 1,
381 'console_refs': [],
382 'vif_refs': [],
383 'vbd_refs': [],
384 'vtpm_refs': [],
385 'other_config': {},
386 }
388 defaults['name_label'] = 'Domain-' + defaults['uuid']
389 return defaults
391 def _memory_sanity_check(self):
392 if self['memory_static_min'] == 0:
393 self['memory_static_min'] = self['memory_dynamic_min']
395 # If the static max is not set, let's set it to dynamic max.
396 # If the static max is smaller than static min, then fix it!
397 self['memory_static_max'] = max(self['memory_static_max'],
398 self['memory_dynamic_max'],
399 self['memory_static_min'])
401 for mem_type in ('memory_static_min', 'memory_static_max'):
402 if self[mem_type] <= 0:
403 raise XendConfigError('Memory value too low for %s: %d' %
404 (mem_type, self[mem_type]))
406 def _actions_sanity_check(self):
407 for event in ['shutdown', 'reboot', 'crash']:
408 if self['actions_after_' + event] not in CONFIG_RESTART_MODES:
409 raise XendConfigError('Invalid event handling mode: ' +
410 event)
412 def _vcpus_sanity_check(self):
413 if 'vcpus_number' in self and 'vcpu_avail' not in self:
414 self['vcpu_avail'] = (1 << self['vcpus_number']) - 1
416 def _uuid_sanity_check(self):
417 """Make sure UUID is in proper string format with hyphens."""
418 self['uuid'] = uuid.toString(uuid.fromString(self['uuid']))
420 def validate(self):
421 self._memory_sanity_check()
422 self._actions_sanity_check()
423 self._vcpus_sanity_check()
424 self._uuid_sanity_check()
426 def _dominfo_to_xapi(self, dominfo):
427 self['domid'] = dominfo['domid']
428 self['online_vcpus'] = dominfo['online_vcpus']
429 self['vcpus_number'] = dominfo['max_vcpu_id'] + 1
430 self['memory_dynamic_min'] = (dominfo['mem_kb'] + 1023)/1024
431 self['memory_dynamic_max'] = (dominfo['maxmem_kb'] + 1023)/1024
432 self['cpu_time'] = dominfo['cpu_time']/1e9
433 # TODO: i don't know what the security stuff expects here
434 if dominfo.get('ssidref'):
435 self['security'] = [['ssidref', dominfo['ssidref']]]
436 self['shutdown_reason'] = dominfo['shutdown_reason']
438 # parse state into Xen API states
439 self['running'] = dominfo['running']
440 self['crashed'] = dominfo['crashed']
441 self['dying'] = dominfo['dying']
442 self['shutdown'] = dominfo['shutdown']
443 self['paused'] = dominfo['paused']
444 self['blocked'] = dominfo['blocked']
446 if 'name' in dominfo:
447 self['name_label'] = dominfo['name']
449 if 'handle' in dominfo:
450 self['uuid'] = uuid.toString(dominfo['handle'])
452 def _parse_sxp(self, sxp_cfg):
453 """ Populate this XendConfig using the parsed SXP.
455 @param sxp_cfg: Parsed SXP Configuration
456 @type sxp_cfg: list of lists
457 @rtype: dictionary
458 @return: A dictionary containing the parsed options of the SXP.
459 """
460 cfg = {}
462 for key, typ in XENAPI_CFG_TYPES.items():
463 val = sxp.child_value(sxp_cfg, key)
464 if val is not None:
465 cfg[key] = typ(val)
467 # Convert deprecated options to current equivalents.
469 restart = sxp.child_value(sxp_cfg, 'restart')
470 if restart:
471 if restart == 'onreboot':
472 cfg['on_poweroff'] = 'destroy'
473 cfg['on_reboot'] = 'restart'
474 cfg['on_crash'] = 'destroy'
475 elif restart == 'always':
476 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
477 cfg[opt] = 'restart'
478 elif restart == 'never':
479 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
480 cfg[opt] = 'never'
481 else:
482 log.warn('Ignoring unrecognised value for deprecated option:'
483 'restart = \'%s\'', restart)
485 # Only extract options we know about.
486 extract_keys = LEGACY_UNSUPPORTED_BY_XENAPI_CFG
487 extract_keys += XENAPI_CFG_TO_LEGACY_CFG.values()
489 for key in extract_keys:
490 val = sxp.child_value(sxp_cfg, key)
491 if val != None:
492 try:
493 cfg[key] = LEGACY_CFG_TYPES[key](val)
494 except KeyError:
495 cfg[key] = val
496 except (TypeError, ValueError), e:
497 log.warn("Unable to parse key %s: %s: %s" %
498 (key, str(val), e))
500 # Compatibility hack -- can go soon.
501 boot_order = sxp.child_value(sxp_cfg, 'HVM_boot')
502 if boot_order:
503 cfg['HVM_boot_policy'] = 'BIOS order'
504 cfg['HVM_boot_params'] = { 'order' : boot_order }
506 # Parsing the device SXP's. In most cases, the SXP looks
507 # like this:
508 #
509 # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]]
510 #
511 # However, for PCI devices it looks like this:
512 #
513 # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1]]]]
514 #
515 # It seems the reasoning for this difference is because
516 # pciif.py needs all the PCI device configurations at
517 # the same time when creating the devices.
518 #
519 # To further complicate matters, Xen 2.0 configuration format
520 # uses the following for pci device configuration:
521 #
522 # [device, [pci, [domain, 0], [bus, 0], [dev, 1], [func, 2]]]
523 #
524 # Hence we deal with pci device configurations outside of
525 # the regular device parsing.
527 cfg['devices'] = {}
528 for dev in sxp.children(sxp_cfg, 'device'):
529 config = sxp.child0(dev)
530 dev_type = sxp.name(config)
531 dev_info = {}
533 if dev_type == 'pci':
534 pci_devs_uuid = sxp.child_value(config, 'uuid',
535 uuid.createString())
536 pci_devs = []
537 for pci_dev in sxp.children(config, 'dev'):
538 pci_dev_info = {}
539 for opt_val in pci_dev[1:]:
540 try:
541 opt, val = opt_val
542 pci_dev_info[opt] = val
543 except TypeError:
544 pass
545 pci_devs.append(pci_dev_info)
547 cfg['devices'][pci_devs_uuid] = (dev_type,
548 {'devs': pci_devs,
549 'uuid': pci_devs_uuid})
551 log.debug("XendConfig: reading device: %s" % pci_devs)
552 else:
553 self.device_add(dev_type, cfg_sxp = config, target = cfg)
554 log.debug("XendConfig: reading device: %s" % scrub_password(dev_info))
556 # Extract missing data from configuration entries
557 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
558 if image_sxp:
559 image_vcpus = sxp.child_value(image_sxp, 'vcpus')
560 if image_vcpus != None:
561 try:
562 if 'vcpus_number' not in cfg:
563 cfg['vcpus_number'] = int(image_vcpus)
564 elif cfg['vcpus_number'] != int(image_vcpus):
565 cfg['vcpus_number'] = int(image_vcpus)
566 log.warn('Overriding vcpus from %d to %d using image'
567 'vcpus value.', cfg['vcpus_number'])
568 except ValueError, e:
569 raise XendConfigError('integer expeceted: %s: %s' %
570 image_sxp, e)
572 # Deprecated cpu configuration
573 if 'cpu' in cfg:
574 if 'cpus' in cfg:
575 cfg['cpus'] = "%s,%s" % (str(cfg['cpu']), cfg['cpus'])
576 else:
577 cfg['cpus'] = str(cfg['cpu'])
579 # convert 'cpus' string to list of ints
580 # 'cpus' supports a list of ranges (0-3), seperated by
581 # commas, and negation, (^1).
582 # Precedence is settled by order of the string:
583 # "0-3,^1" -> [0,2,3]
584 # "0-3,^1,1" -> [0,1,2,3]
585 try:
586 if 'cpus' in cfg and type(cfg['cpus']) != list:
587 cpus = []
588 for c in cfg['cpus'].split(','):
589 if c.find('-') != -1:
590 (x, y) = c.split('-')
591 for i in range(int(x), int(y)+1):
592 cpus.append(int(i))
593 else:
594 # remove this element from the list
595 if c[0] == '^':
596 cpus = [x for x in cpus if x != int(c[1:])]
597 else:
598 cpus.append(int(c))
600 cfg['cpus'] = cpus
601 except ValueError, e:
602 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
604 if 'security' in cfg and isinstance(cfg['security'], str):
605 cfg['security'] = sxp.from_string(cfg['security'])
607 old_state = sxp.child_value(sxp_cfg, 'state')
608 if old_state:
609 for i in range(len(CONFIG_OLD_DOM_STATES)):
610 cfg[CONFIG_OLD_DOM_STATES[i]] = int(old_state[i] != '-')
612 return cfg
615 def _sxp_to_xapi(self, sxp_cfg):
616 """Read in an SXP Configuration object and
617 populate at much of the Xen API with valid values.
618 """
619 log.debug('_sxp_to_xapi(%s)' % scrub_password(sxp_cfg))
621 cfg = self._parse_sxp(sxp_cfg)
623 for key, typ in XENAPI_CFG_TYPES.items():
624 val = cfg.get(key)
625 if val is not None:
626 self[key] = typ(val)
628 # Convert parameters that can be directly mapped from
629 # the Legacy Config to Xen API Config
631 for apikey, cfgkey in XENAPI_CFG_TO_LEGACY_CFG.items():
632 try:
633 type_conv = XENAPI_CFG_TYPES.get(apikey)
634 if callable(type_conv):
635 self[apikey] = type_conv(cfg[cfgkey])
636 else:
637 log.warn("Unconverted key: " + apikey)
638 self[apikey] = cfg[cfgkey]
639 except KeyError:
640 pass
642 def update_with(n, o):
643 if not self.get(n):
644 self[n] = cfg.get(o, '')
646 update_with('PV_bootloader', 'bootloader')
647 update_with('PV_bootloader_args', 'bootloader_args')
649 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
650 if image_sxp:
651 self.update_with_image_sxp(image_sxp)
653 # Convert Legacy HVM parameters to Xen API configuration
654 self['platform_std_vga'] = bool0(cfg.get('stdvga', 0))
655 self['platform_serial'] = str(cfg.get('serial', ''))
656 self['platform_localtime'] = bool0(cfg.get('localtime', 0))
657 self['platform_enable_audio'] = bool0(cfg.get('soundhw', 0))
659 # make sure a sane maximum is set
660 if self['memory_static_max'] <= 0:
661 self['memory_static_max'] = self['memory_static_min']
663 self['memory_dynamic_max'] = self['memory_static_max']
664 self['memory_dynamic_min'] = self['memory_static_min']
666 # set device references in the configuration
667 self['devices'] = cfg.get('devices', {})
668 self['console_refs'] = cfg.get('console_refs', [])
669 self['vif_refs'] = cfg.get('vif_refs', [])
670 self['vbd_refs'] = cfg.get('vbd_refs', [])
671 self['vtpm_refs'] = cfg.get('vtpm_refs', [])
673 # coalesce hvm vnc frame buffer with vfb config
674 if self['image']['type'] == 'hvm' and self['image'].get('vnc', 0):
675 # add vfb device if it isn't there already
676 has_rfb = False
677 for console_uuid in self['console_refs']:
678 if self['devices'][console_uuid][1].get('protocol') == 'rfb':
679 has_rfb = True
680 break
681 if self['devices'][console_uuid][0] == 'vfb':
682 has_rfb = True
683 break
685 if not has_rfb:
686 dev_config = ['vfb']
687 # copy VNC related params from image config to vfb dev conf
688 for key in ['vncpasswd', 'vncunused', 'vncdisplay',
689 'vnclisten']:
690 if key in self['image']:
691 dev_config.append([key, self['image'][key]])
693 self.device_add('vfb', cfg_sxp = dev_config)
696 def _sxp_to_xapi_unsupported(self, sxp_cfg):
697 """Read in an SXP configuration object and populate
698 values are that not related directly supported in
699 the Xen API.
700 """
702 log.debug('_sxp_to_xapi_unsupported(%s)' % scrub_password(sxp_cfg))
704 # Parse and convert parameters used to configure
705 # the image (as well as HVM images)
706 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
707 if image_sxp:
708 image = {}
709 image['type'] = sxp.name(image_sxp)
710 for arg, conv in LEGACY_IMAGE_CFG:
711 val = sxp.child_value(image_sxp, arg, None)
712 if val != None:
713 image[arg] = conv(val)
715 image_hvm = {}
716 for arg, conv in LEGACY_IMAGE_HVM_CFG:
717 val = sxp.child_value(image_sxp, arg, None)
718 if val != None:
719 image_hvm[arg] = conv(val)
721 image_hvm_devices = {}
722 for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
723 val = sxp.child_value(image_sxp, arg, None)
724 if val != None:
725 image_hvm_devices[arg] = conv(val)
727 if image_hvm or image_hvm_devices:
728 image['hvm'] = image_hvm
729 image['hvm']['devices'] = image_hvm_devices
731 self['image'] = image
733 for apikey, imgkey in XENAPI_HVM_CFG.items():
734 val = sxp.child_value(image_sxp, imgkey, None)
735 if val != None:
736 self[apikey] = val
737 self._hvm_boot_params_from_sxp(image_sxp)
739 # extract backend value
741 backend = []
742 for c in sxp.children(sxp_cfg, 'backend'):
743 backend.append(sxp.name(sxp.child0(c)))
744 if backend:
745 self['backend'] = backend
747 # Parse and convert other Non Xen API parameters.
748 def _set_cfg_if_exists(sxp_arg):
749 val = sxp.child_value(sxp_cfg, sxp_arg)
750 if val != None:
751 if LEGACY_CFG_TYPES.get(sxp_arg):
752 self[sxp_arg] = LEGACY_CFG_TYPES[sxp_arg](val)
753 else:
754 self[sxp_arg] = val
756 _set_cfg_if_exists('shadow_memory')
757 _set_cfg_if_exists('security')
758 _set_cfg_if_exists('features')
759 _set_cfg_if_exists('on_xend_stop')
760 _set_cfg_if_exists('on_xend_start')
761 _set_cfg_if_exists('vcpu_avail')
762 _set_cfg_if_exists('cpu_weight')
763 _set_cfg_if_exists('cpu_cap')
765 # Parse and store runtime configuration
766 _set_cfg_if_exists('start_time')
767 _set_cfg_if_exists('cpu_time')
768 _set_cfg_if_exists('shutdown_reason')
769 _set_cfg_if_exists('up_time')
770 _set_cfg_if_exists('status') # TODO, deprecated
772 def _add_xapi_unsupported(self, xapi_dict):
773 """Updates the configuration object with entries that are not
774 officially supported by the Xen API but is required for
775 the rest of Xend to function.
776 """
778 # populate image
779 if 'image' in xapi_dict:
780 self['image'].update(xapi_dict['image'])
781 else:
782 hvm = self['HVM_boot_policy'] != ''
783 self['image']['type'] = hvm and 'hvm' or 'linux'
784 if hvm:
785 self['image']['hvm'] = {'devices': {}}
786 for xapi, cfgapi in XENAPI_HVM_CFG.items():
787 if xapi in self:
788 self['image']['hvm']['devices'][cfgapi] = self[xapi]
790 # currently unsupported options
791 self['image']['hvm']['device_model'] = LEGACY_DM
792 self['image']['vnc'] = 0
793 self['image']['hvm']['pae'] = 1
795 if self['platform_enable_audio']:
796 self['image']['hvm']['devices']['soundhw'] = 'sb16'
799 def _get_old_state_string(self):
800 """Returns the old xm state string.
801 @rtype: string
802 @return: old state string
803 """
804 state_string = ''
805 for state_name in CONFIG_OLD_DOM_STATES:
806 on_off = self.get(state_name, 0)
807 if on_off:
808 state_string += state_name[0]
809 else:
810 state_string += '-'
812 return state_string
815 def update_config(self, dominfo):
816 """Update configuration with the output from xc.domain_getinfo().
818 @param dominfo: Domain information via xc.domain_getinfo()
819 @type dominfo: dict
820 """
821 self._dominfo_to_xapi(dominfo)
822 self.validate()
824 def update_with_xenapi_config(self, xapi):
825 """Update configuration with a Xen API VM struct
827 @param xapi: Xen API VM Struct
828 @type xapi: dict
829 """
831 log.debug('update_with_xenapi_config: %s' % scrub_password(xapi))
833 for key, val in xapi.items():
834 type_conv = XENAPI_CFG_TYPES.get(key)
835 if type_conv is None:
836 key = key.lower()
837 type_conv = XENAPI_CFG_TYPES.get(key)
838 if callable(type_conv):
839 self[key] = type_conv(val)
840 else:
841 self[key] = val
843 self.validate()
845 def to_sxp(self, domain = None, ignore_devices = False, ignore = [],
846 legacy_only = True):
847 """ Get SXP representation of this config object.
849 Incompat: removed store_mfn, console_mfn
851 @keyword domain: (optional) XendDomainInfo to get extra information
852 from such as domid and running devices.
853 @type domain: XendDomainInfo
854 @keyword ignore: (optional) list of 'keys' that we do not want
855 to export.
856 @type ignore: list of strings
857 @rtype: list of list (SXP representation)
858 """
859 sxpr = ['domain']
861 # TODO: domid/dom is the same thing but called differently
862 # depending if it is from xenstore or sxpr.
864 if domain.getDomid() is not None:
865 sxpr.append(['domid', domain.getDomid()])
867 if not legacy_only:
868 for name in XENAPI_CFG_TYPES.keys():
869 if name in self and self[name] not in (None, []):
870 sxpr.append([name, str(self[name])])
872 for xenapi, legacy in XENAPI_CFG_TO_LEGACY_CFG.items():
873 if self.has_key(xenapi) and self[xenapi] not in (None, []):
874 if type(self[xenapi]) == bool:
875 # convert booleans to ints before making an sxp item
876 sxpr.append([legacy, int(self[xenapi])])
877 else:
878 sxpr.append([legacy, self[xenapi]])
880 for legacy in LEGACY_UNSUPPORTED_BY_XENAPI_CFG:
881 if legacy in ('domid', 'uuid'): # skip these
882 continue
883 if self.has_key(legacy) and self[legacy] not in (None, []):
884 sxpr.append([legacy, self[legacy]])
886 if 'image' in self and self['image']:
887 sxpr.append(['image', self.image_sxpr()])
889 sxpr.append(['status', domain.state])
890 sxpr.append(['memory_dynamic_min', self.get('memory_dynamic_min')])
891 sxpr.append(['memory_dynamic_max', self.get('memory_dynamic_max')])
893 if domain.getDomid() is not None:
894 sxpr.append(['state', self._get_old_state_string()])
896 if domain:
897 if domain.store_mfn:
898 sxpr.append(['store_mfn', domain.store_mfn])
899 if domain.console_mfn:
900 sxpr.append(['console_mfn', domain.console_mfn])
903 # Marshall devices (running or from configuration)
904 if not ignore_devices:
905 for cls in XendDevices.valid_devices():
906 found = False
908 # figure if there is a dev controller is valid and running
909 if domain and domain.getDomid() != None:
910 try:
911 controller = domain.getDeviceController(cls)
912 configs = controller.configurations()
913 for config in configs:
914 if sxp.name(config) in ('vbd', 'tap'):
915 # The bootable flag is never written to the
916 # store as part of the device config.
917 dev_uuid = sxp.child_value(config, 'uuid')
918 dev_type, dev_cfg = self['devices'][dev_uuid]
919 is_bootable = dev_cfg.get('bootable', 0)
920 config.append(['bootable', int(is_bootable)])
922 sxpr.append(['device', config])
924 found = True
925 except:
926 log.exception("dumping sxp from device controllers")
927 pass
929 # if we didn't find that device, check the existing config
930 # for a device in the same class
931 if not found:
932 for dev_type, dev_info in self.all_devices_sxpr():
933 if dev_type == cls:
934 sxpr.append(['device', dev_info])
936 return sxpr
938 def device_add(self, dev_type, cfg_sxp = None, cfg_xenapi = None,
939 target = None):
940 """Add a device configuration in SXP format or XenAPI struct format.
942 For SXP, it could be either:
944 [device, [vbd, [uname ...]]
946 or:
948 [vbd, [uname ..]]
950 @type cfg_sxp: list of lists (parsed sxp object)
951 @param cfg_sxp: SXP configuration object
952 @type cfg_xenapi: dict
953 @param cfg_xenapi: A device configuration from Xen API (eg. vbd,vif)
954 @param target: write device information to
955 @type target: None or a dictionary
956 @rtype: string
957 @return: Assigned UUID of the device.
958 """
959 if target == None:
960 target = self
962 if dev_type not in XendDevices.valid_devices():
963 raise XendConfigError("XendConfig: %s not a valid device type" %
964 dev_type)
966 if cfg_sxp == None and cfg_xenapi == None:
967 raise XendConfigError("XendConfig: device_add requires some "
968 "config.")
970 #if cfg_sxp:
971 # log.debug("XendConfig.device_add: %s" % str(cfg_sxp))
972 #if cfg_xenapi:
973 # log.debug("XendConfig.device_add: %s" % str(cfg_xenapi))
975 if cfg_sxp:
976 if sxp.child0(cfg_sxp) == 'device':
977 config = sxp.child0(cfg_sxp)
978 else:
979 config = cfg_sxp
981 dev_type = sxp.name(config)
982 dev_info = {}
984 for opt_val in config[1:]:
985 try:
986 opt, val = opt_val
987 dev_info[opt] = val
988 except (TypeError, ValueError): # unpack error
989 pass
991 if dev_type == 'vbd':
992 dev_info['bootable'] = 0
993 if dev_info.get('dev', '').startswith('ioemu:'):
994 dev_info['driver'] = 'ioemu'
995 else:
996 dev_info['driver'] = 'paravirtualised'
998 # create uuid if it doesn't exist
999 dev_uuid = dev_info.get('uuid', uuid.createString())
1000 dev_info['uuid'] = dev_uuid
1002 # store dev references by uuid for certain device types
1003 target['devices'][dev_uuid] = (dev_type, dev_info)
1004 if dev_type in ('vif', 'vbd', 'vtpm'):
1005 param = '%s_refs' % dev_type
1006 if param not in target:
1007 target[param] = []
1008 if dev_uuid not in target[param]:
1009 if dev_type == 'vbd' and not target[param]:
1010 # Compat hack -- this is the first disk, so mark it
1011 # bootable.
1012 dev_info['bootable'] = 1
1013 target[param].append(dev_uuid)
1014 elif dev_type == 'tap':
1015 if 'vbd_refs' not in target:
1016 target['vbd_refs'] = []
1017 if dev_uuid not in target['vbd_refs']:
1018 if not target['vbd_refs']:
1019 # Compat hack -- this is the first disk, so mark it
1020 # bootable.
1021 dev_info['bootable'] = 1
1022 target['vbd_refs'].append(dev_uuid)
1024 elif dev_type == 'vfb':
1025 # Populate other config with aux data that is associated
1026 # with vfb
1028 other_config = {}
1029 for key in ['vncunused', 'vncdisplay', 'vnclisten',
1030 'vncpasswd', 'type', 'display', 'xauthority',
1031 'keymap']:
1032 if key in dev_info:
1033 other_config[key] = dev_info[key]
1034 target['devices'][dev_uuid][1]['other_config'] = other_config
1037 if 'console_refs' not in target:
1038 target['console_refs'] = []
1040 # Treat VFB devices as console devices so they are found
1041 # through Xen API
1042 if dev_uuid not in target['console_refs']:
1043 target['console_refs'].append(dev_uuid)
1045 elif dev_type == 'console':
1046 if 'console_refs' not in target:
1047 target['console_refs'] = []
1048 if dev_uuid not in target['console_refs']:
1049 target['console_refs'].append(dev_uuid)
1051 return dev_uuid
1053 if cfg_xenapi:
1054 dev_info = {}
1055 dev_uuid = ''
1056 if dev_type == 'vif':
1057 if cfg_xenapi.get('MAC'): # don't add if blank
1058 dev_info['mac'] = cfg_xenapi.get('MAC')
1059 # vifname is the name on the guest, not dom0
1060 # TODO: we don't have the ability to find that out or
1061 # change it from dom0
1062 #if cfg_xenapi.get('device'): # don't add if blank
1063 # dev_info['vifname'] = cfg_xenapi.get('device')
1064 if cfg_xenapi.get('type'):
1065 dev_info['type'] = cfg_xenapi.get('type')
1066 if cfg_xenapi.get('name'):
1067 dev_info['name'] = cfg_xenapi.get('name')
1069 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
1070 dev_info['uuid'] = dev_uuid
1071 target['devices'][dev_uuid] = (dev_type, dev_info)
1072 target['vif_refs'].append(dev_uuid)
1074 elif dev_type in ('vbd', 'tap'):
1075 dev_info['type'] = cfg_xenapi.get('type', 'Disk')
1076 if dev_info['type'] == 'CD':
1077 old_vbd_type = 'cdrom'
1078 else:
1079 old_vbd_type = 'disk'
1081 dev_info['uname'] = cfg_xenapi.get('image', '')
1082 dev_info['dev'] = '%s:%s' % (cfg_xenapi.get('device'),
1083 old_vbd_type)
1084 dev_info['bootable'] = int(cfg_xenapi.get('bootable', 0))
1085 dev_info['driver'] = cfg_xenapi.get('driver', '')
1086 dev_info['VDI'] = cfg_xenapi.get('VDI', '')
1088 if cfg_xenapi.get('mode') == 'RW':
1089 dev_info['mode'] = 'w'
1090 else:
1091 dev_info['mode'] = 'r'
1093 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
1094 dev_info['uuid'] = dev_uuid
1095 target['devices'][dev_uuid] = (dev_type, dev_info)
1096 target['vbd_refs'].append(dev_uuid)
1098 elif dev_type == 'vtpm':
1099 if cfg_xenapi.get('type'):
1100 dev_info['type'] = cfg_xenapi.get('type')
1102 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
1103 dev_info['uuid'] = dev_uuid
1104 target['devices'][dev_uuid] = (dev_type, dev_info)
1105 target['vtpm_refs'].append(dev_uuid)
1107 elif dev_type == 'console':
1108 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
1109 dev_info['uuid'] = dev_uuid
1110 dev_info['protocol'] = cfg_xenapi.get('protocol', 'rfb')
1111 dev_info['other_config'] = cfg_xenapi.get('other_config', {})
1112 if dev_info['protocol'] == 'rfb':
1113 # collapse other config into devinfo for things
1114 # such as vncpasswd, vncunused, etc.
1115 dev_info.update(cfg_xenapi.get('other_config', {}))
1116 dev_info['type'] = 'vnc'
1117 target['devices'][dev_uuid] = ('vfb', dev_info)
1118 target['console_refs'].append(dev_uuid)
1120 # Finally, if we are a pvfb, we need to make a vkbd
1121 # as well that is not really exposed to Xen API
1122 vkbd_uuid = uuid.createString()
1123 target['devices'][vkbd_uuid] = ('vkbd', {})
1125 elif dev_info['protocol'] == 'vt100':
1126 # if someone tries to create a VT100 console
1127 # via the Xen API, we'll have to ignore it
1128 # because we create one automatically in
1129 # XendDomainInfo._update_consoles
1130 raise XendConfigError('Creating vt100 consoles via '
1131 'Xen API is unsupported')
1133 return dev_uuid
1135 # no valid device to add
1136 return ''
1138 def console_add(self, protocol, location, other_config = {}):
1139 dev_uuid = uuid.createString()
1140 if protocol == 'vt100':
1141 dev_info = {
1142 'uuid': dev_uuid,
1143 'protocol': protocol,
1144 'location': location,
1145 'other_config': other_config,
1148 if 'devices' not in self:
1149 self['devices'] = {}
1151 self['devices'][dev_uuid] = ('console', dev_info)
1152 self['console_refs'].append(dev_uuid)
1153 return dev_info
1155 return {}
1157 def console_update(self, console_uuid, key, value):
1158 for dev_uuid, (dev_type, dev_info) in self['devices'].items():
1159 if dev_uuid == console_uuid:
1160 dev_info[key] = value
1161 break
1163 def console_get_all(self, protocol):
1164 if protocol == 'vt100':
1165 consoles = [dinfo for dtype, dinfo in self['devices'].values()
1166 if dtype == 'console']
1167 return [c for c in consoles if c.get('protocol') == protocol]
1169 elif protocol == 'rfb':
1170 vfbs = [dinfo for dtype, dinfo in self['devices'].values()
1171 if dtype == 'vfb']
1173 # move all non-console key values to other_config before
1174 # returning console config
1175 valid_keys = ['uuid', 'location']
1176 for vfb in vfbs:
1177 other_config = {}
1178 for key, val in vfb.items():
1179 if key not in valid_keys:
1180 other_config[key] = vfb[key]
1181 del vfb[key]
1182 vfb['other_config'] = other_config
1183 vfb['protocol'] = 'rfb'
1185 return vfbs
1187 else:
1188 return []
1190 def device_update(self, dev_uuid, cfg_sxp = [], cfg_xenapi = {}):
1191 """Update an existing device with the new configuration.
1193 @rtype: boolean
1194 @return: Returns True if succesfully found and updated a device conf
1195 """
1196 if dev_uuid in self['devices'] and cfg_sxp:
1197 if sxp.child0(cfg_sxp) == 'device':
1198 config = sxp.child0(cfg_sxp)
1199 else:
1200 config = cfg_sxp
1202 dev_type, dev_info = self['devices'][dev_uuid]
1203 for opt_val in config[1:]:
1204 try:
1205 opt, val = opt_val
1206 dev_info[opt] = val
1207 except (TypeError, ValueError):
1208 pass # no value for this config option
1210 self['devices'][dev_uuid] = (dev_type, dev_info)
1211 return True
1213 elif dev_uuid in self['devices'] and cfg_xenapi:
1214 dev_type, dev_info = self['devices'][dev_uuid]
1215 for key, val in cfg_xenapi.items():
1216 dev_info[key] = val
1217 self['devices'][dev_uuid] = (dev_type, dev_info)
1219 return False
1222 def device_sxpr(self, dev_uuid = None, dev_type = None, dev_info = None):
1223 """Get Device SXPR by either giving the device UUID or (type, config).
1225 @rtype: list of lists
1226 @return: device config sxpr
1227 """
1228 sxpr = []
1229 if dev_uuid != None and dev_uuid in self['devices']:
1230 dev_type, dev_info = self['devices'][dev_uuid]
1232 if dev_type == None or dev_info == None:
1233 raise XendConfigError("Required either UUID or device type and "
1234 "configuration dictionary.")
1236 sxpr.append(dev_type)
1237 if dev_type in ('console', 'vfb'):
1238 config = [(opt, val) for opt, val in dev_info.items()
1239 if opt != 'other_config']
1240 else:
1241 config = [(opt, val) for opt, val in dev_info.items()]
1243 sxpr += config
1245 return sxpr
1247 def ordered_device_refs(self):
1248 result = (self['console_refs'] +
1249 self['vbd_refs'] +
1250 self['vif_refs'] +
1251 self['vtpm_refs'])
1252 result.extend([u for u in self['devices'].keys() if u not in result])
1253 return result
1255 def all_devices_sxpr(self):
1256 """Returns the SXPR for all devices in the current configuration."""
1257 sxprs = []
1258 pci_devs = []
1260 if 'devices' not in self:
1261 return sxprs
1263 ordered_refs = self.ordered_device_refs()
1264 for dev_uuid in ordered_refs:
1265 dev_type, dev_info = self['devices'][dev_uuid]
1266 if dev_type == 'pci': # special case for pci devices
1267 sxpr = [['uuid', dev_info['uuid']]]
1268 for pci_dev_info in dev_info['devs']:
1269 pci_dev_sxpr = ['dev']
1270 for opt, val in pci_dev_info.items():
1271 pci_dev_sxpr.append([opt, val])
1272 sxpr.append(pci_dev_sxpr)
1273 sxprs.append((dev_type, sxpr))
1274 else:
1275 sxpr = self.device_sxpr(dev_type = dev_type,
1276 dev_info = dev_info)
1277 sxprs.append((dev_type, sxpr))
1279 return sxprs
1281 def image_sxpr(self):
1282 """Returns a backwards compatible image SXP expression that is
1283 used in xenstore's /vm/<uuid>/image value and xm list."""
1284 image = [self['image'].get('type', 'linux')]
1285 if self.has_key('PV_kernel'):
1286 image.append(['kernel', self['PV_kernel']])
1287 if self.has_key('PV_ramdisk') and self['PV_ramdisk']:
1288 image.append(['ramdisk', self['PV_ramdisk']])
1289 if self.has_key('PV_args') and self['PV_args']:
1290 image.append(['args', self['PV_args']])
1292 for arg, conv in LEGACY_IMAGE_CFG:
1293 if self['image'].has_key(arg):
1294 image.append([arg, self['image'][arg]])
1296 if 'hvm' in self['image']:
1297 for arg, conv in LEGACY_IMAGE_HVM_CFG:
1298 if self['image']['hvm'].get(arg):
1299 image.append([arg, conv(self['image']['hvm'][arg])])
1301 if 'hvm' in self['image'] and 'devices' in self['image']['hvm']:
1302 for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
1303 val = self['image']['hvm']['devices'].get(arg)
1304 if val != None:
1305 try:
1306 if conv: val = conv(val)
1307 except (ValueError, TypeError):
1308 if type(val) == bool: val = int(val)
1310 image.append([arg, val])
1312 return image
1314 def update_with_image_sxp(self, image_sxp, bootloader = False):
1315 # Convert Legacy "image" config to Xen API PV_*
1316 # configuration
1317 log.debug("update_with_image_sxp(%s)" % scrub_password(image_sxp))
1319 # user-specified args must come last: previous releases did this and
1320 # some domU kernels rely upon the ordering.
1321 kernel_args = sxp.child_value(image_sxp, 'args', '')
1323 # attempt to extract extra arguments from SXP config
1324 arg_ip = sxp.child_value(image_sxp, 'ip')
1325 if arg_ip and not re.search(r'ip=[^ ]+', kernel_args):
1326 kernel_args = 'ip=%s ' % arg_ip + kernel_args
1327 arg_root = sxp.child_value(image_sxp, 'root')
1328 if arg_root and not re.search(r'root=', kernel_args):
1329 kernel_args = 'root=%s ' % arg_root + kernel_args
1331 if bootloader:
1332 self['_temp_using_bootloader'] = '1'
1333 self['_temp_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1334 self['_temp_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1335 self['_temp_args'] = kernel_args
1336 else:
1337 self['PV_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1338 self['PV_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1339 self['PV_args'] = kernel_args
1341 # Store image SXP in python dictionary format
1342 image = {}
1343 image['type'] = sxp.name(image_sxp)
1344 for arg, conv in LEGACY_IMAGE_CFG:
1345 val = sxp.child_value(image_sxp, arg, None)
1346 if val != None:
1347 image[arg] = conv(val)
1349 image_hvm = {}
1350 for arg, conv in LEGACY_IMAGE_HVM_CFG:
1351 val = sxp.child_value(image_sxp, arg, None)
1352 if val != None:
1353 image_hvm[arg] = conv(val)
1355 image_hvm_devices = {}
1356 for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
1357 val = sxp.child_value(image_sxp, arg, None)
1358 if val != None:
1359 try:
1360 image_hvm_devices[arg] = conv(val)
1361 except (ValueError, TypeError):
1362 image_hvm_devices[arg] = val
1365 if image_hvm or image_hvm_devices:
1366 image['hvm'] = image_hvm
1367 image['hvm']['devices'] = image_hvm_devices
1369 self['image'] = image
1371 for apikey, imgkey in XENAPI_HVM_CFG.items():
1372 val = sxp.child_value(image_sxp, imgkey, None)
1373 if val != None:
1374 type_conv = XENAPI_CFG_TYPES[apikey]
1375 if callable(type_conv):
1376 self[apikey] = type_conv(val)
1377 else:
1378 self[apikey] = val
1379 self._hvm_boot_params_from_sxp(image_sxp)
1382 def _hvm_boot_params_from_sxp(self, image_sxp):
1383 boot = sxp.child_value(image_sxp, 'boot', None)
1384 if boot is not None:
1385 self['HVM_boot_policy'] = 'BIOS order'
1386 self['HVM_boot_params'] = { 'order' : boot }
1390 # debugging
1393 if __name__ == "__main__":
1394 pass