ia64/xen-unstable

view tools/python/xen/xend/XendConfig.py @ 14131:64d80037e524

Save elfnotes in VM sxpr under image/notes, and load them on restore.
Signed-off-by: Brendan Cully <brendan@cs.ubc.ca>
author kfraser@localhost.localdomain
date Mon Feb 26 09:59:56 2007 +0000 (2007-02-26)
parents 6524e02edbeb
children 620212e48908
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': dict,
150 'vcpus_number': int,
151 'actions_after_shutdown': str,
152 'actions_after_reboot': str,
153 'actions_after_crash': str,
154 'tpm_backend': int,
155 'PV_bootloader': str,
156 'PV_kernel': str,
157 'PV_ramdisk': str,
158 'PV_args': str,
159 'PV_bootloader_args': str,
160 'HVM_boot_policy': str,
161 'HVM_boot_params': dict,
162 'platform_std_vga': bool0,
163 'platform_serial': str,
164 'platform_localtime': bool0,
165 'platform_clock_offset': bool0,
166 'platform_enable_audio': bool0,
167 'platform_keymap': str,
168 'pci_bus': str,
169 'tools_version': dict,
170 'otherconfig': dict,
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 'security',
180 'vcpu_avail',
181 'cpu_weight',
182 'cpu_cap',
183 'features',
184 # read/write
185 'on_xend_start',
186 'on_xend_stop',
187 # read-only
188 'domid',
189 'start_time',
190 'cpu_time',
191 'online_vcpus',
192 # write-once
193 'cpu',
194 'cpus',
195 ]
197 LEGACY_CFG_TYPES = {
198 'uuid': str,
199 'name': str,
200 'vcpus': int,
201 'vcpu_avail': long,
202 'memory': int,
203 'shadow_memory': int,
204 'maxmem': int,
205 'start_time': float,
206 'cpu_cap': int,
207 'cpu_weight': int,
208 'cpu_time': float,
209 'features': str,
210 'localtime': int,
211 'name': str,
212 'on_poweroff': str,
213 'on_reboot': str,
214 'on_crash': str,
215 'on_xend_stop': str,
216 'on_xend_start': str,
217 'online_vcpus': int,
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 LEGACY_IMAGE_CFG = [
241 ('root', str),
242 ('ip', str),
243 ('nographic', int),
244 ('vnc', int),
245 ('sdl', int),
246 ('vncdisplay', int),
247 ('vncunused', int),
248 ('vncpasswd', str),
249 ('vnclisten', str),
250 ]
252 LEGACY_IMAGE_HVM_CFG = [
253 ('device_model', str),
254 ('display', str),
255 ('xauthority', str),
256 ('vncconsole', int),
257 ('pae', int),
258 ('apic', int),
259 ]
261 LEGACY_IMAGE_HVM_DEVICES_CFG = [
262 ('acpi', int),
263 ('boot', str),
264 ('fda', str),
265 ('fdb', str),
266 ('isa', int),
267 ('keymap', str),
268 ('localtime', int),
269 ('serial', str),
270 ('stdvga', int),
271 ('soundhw', str),
272 ('usb', int),
273 ('usbdevice', str),
274 ('vcpus', int),
275 ]
277 LEGACY_DM = '/usr/lib/xen/bin/qemu-dm'
279 ##
280 ## Config Choices
281 ##
283 CONFIG_RESTART_MODES = ('restart', 'destroy', 'preserve', 'rename-restart')
284 CONFIG_OLD_DOM_STATES = ('running', 'blocked', 'paused', 'shutdown',
285 'crashed', 'dying')
287 class XendConfigError(VmError):
288 def __str__(self):
289 return 'Invalid Configuration: %s' % str(self.value)
291 ##
292 ## XendConfig Class (an extended dictionary)
293 ##
295 class XendConfig(dict):
296 """ The new Xend VM Configuration.
298 Stores the configuration in xenapi compatible format but retains
299 import and export functions for SXP.
300 """
301 def __init__(self, filename = None, sxp_obj = None,
302 xapi = None, dominfo = None):
304 dict.__init__(self)
305 self.update(self._defaults())
307 if filename:
308 try:
309 sxp_obj = sxp.parse(open(filename,'r'))
310 sxp_obj = sxp_obj[0]
311 except IOError, e:
312 raise XendConfigError("Unable to read file: %s" % filename)
314 if sxp_obj:
315 self._sxp_to_xapi(sxp_obj)
316 self._sxp_to_xapi_unsupported(sxp_obj)
317 elif xapi:
318 self.update_with_xenapi_config(xapi)
319 self._add_xapi_unsupported(xapi)
320 elif dominfo:
321 # output from xc.domain_getinfo
322 self._dominfo_to_xapi(dominfo)
324 log.debug('XendConfig.init: %s' % scrub_password(self))
326 # validators go here
327 self.validate()
329 """ In time, we should enable this type checking addition. It is great
330 also for tracking bugs and unintended writes to XendDomainInfo.info
331 def __setitem__(self, key, value):
332 type_conv = XENAPI_CFG_TYPES.get(key)
333 if callable(type_conv):
334 try:
335 dict.__setitem__(self, key, type_conv(value))
336 except (ValueError, TypeError):
337 raise XendConfigError("Wrong type for configuration value " +
338 "%s. Expected %s" %
339 (key, type_conv.__name__))
340 else:
341 dict.__setitem__(self, key, value)
342 """
344 def _defaults(self):
345 defaults = {
346 'name_label': 'Domain-Unnamed',
347 'actions_after_shutdown': 'destroy',
348 'actions_after_reboot': 'restart',
349 'actions_after_crash': 'restart',
350 'actions_after_suspend': '',
351 'features': '',
352 'PV_bootloader': '',
353 'PV_kernel': '',
354 'PV_ramdisk': '',
355 'PV_args': '',
356 'PV_bootloader_args': '',
357 'HVM_boot_policy': '',
358 'HVM_boot_params': {},
359 'memory_static_min': 0,
360 'memory_dynamic_min': 0,
361 'shadow_memory': 0,
362 'memory_static_max': 0,
363 'memory_dynamic_max': 0,
364 'memory_actual': 0,
365 'devices': {},
366 'image': {},
367 'security': None,
368 'on_xend_start': 'ignore',
369 'on_xend_stop': 'ignore',
370 'cpus': [],
371 'cpu_weight': 256,
372 'cpu_cap': 0,
373 'vcpus_number': 1,
374 'vcpus_params': {},
375 'console_refs': [],
376 'vif_refs': [],
377 'vbd_refs': [],
378 'vtpm_refs': [],
379 'other_config': {},
380 }
382 return defaults
384 def _memory_sanity_check(self):
385 if self['memory_static_min'] == 0:
386 self['memory_static_min'] = self['memory_dynamic_min']
388 # If the static max is not set, let's set it to dynamic max.
389 # If the static max is smaller than static min, then fix it!
390 self['memory_static_max'] = max(self['memory_static_max'],
391 self['memory_dynamic_max'],
392 self['memory_static_min'])
394 for mem_type in ('memory_static_min', 'memory_static_max'):
395 if self[mem_type] <= 0:
396 raise XendConfigError('Memory value too low for %s: %d' %
397 (mem_type, self[mem_type]))
399 def _actions_sanity_check(self):
400 for event in ['shutdown', 'reboot', 'crash']:
401 if self['actions_after_' + event] not in CONFIG_RESTART_MODES:
402 raise XendConfigError('Invalid event handling mode: ' +
403 event)
405 def _vcpus_sanity_check(self):
406 if 'vcpus_number' in self and 'vcpu_avail' not in self:
407 self['vcpu_avail'] = (1 << self['vcpus_number']) - 1
409 def _uuid_sanity_check(self):
410 """Make sure UUID is in proper string format with hyphens."""
411 if 'uuid' not in self or not self['uuid']:
412 self['uuid'] = uuid.createString()
413 else:
414 self['uuid'] = uuid.toString(uuid.fromString(self['uuid']))
416 def _name_sanity_check(self):
417 if 'name_label' not in self:
418 self['name_label'] = 'Domain-' + self['uuid']
420 def validate(self):
421 self._uuid_sanity_check()
422 self._name_sanity_check()
423 self._memory_sanity_check()
424 self._actions_sanity_check()
425 self._vcpus_sanity_check()
427 def _dominfo_to_xapi(self, dominfo):
428 self['domid'] = dominfo['domid']
429 self['online_vcpus'] = dominfo['online_vcpus']
430 self['vcpus_number'] = dominfo['max_vcpu_id'] + 1
431 self['memory_dynamic_min'] = (dominfo['mem_kb'] + 1023)/1024
432 self['memory_dynamic_max'] = (dominfo['maxmem_kb'] + 1023)/1024
433 self['cpu_time'] = dominfo['cpu_time']/1e9
434 # TODO: i don't know what the security stuff expects here
435 if dominfo.get('ssidref'):
436 self['security'] = [['ssidref', dominfo['ssidref']]]
437 self['shutdown_reason'] = dominfo['shutdown_reason']
439 # parse state into Xen API states
440 self['running'] = dominfo['running']
441 self['crashed'] = dominfo['crashed']
442 self['dying'] = dominfo['dying']
443 self['shutdown'] = dominfo['shutdown']
444 self['paused'] = dominfo['paused']
445 self['blocked'] = dominfo['blocked']
447 if 'name' in dominfo:
448 self['name_label'] = dominfo['name']
450 if 'handle' in dominfo:
451 self['uuid'] = uuid.toString(dominfo['handle'])
453 def _parse_sxp(self, sxp_cfg):
454 """ Populate this XendConfig using the parsed SXP.
456 @param sxp_cfg: Parsed SXP Configuration
457 @type sxp_cfg: list of lists
458 @rtype: dictionary
459 @return: A dictionary containing the parsed options of the SXP.
460 """
461 cfg = {}
463 for key, typ in XENAPI_CFG_TYPES.items():
464 val = sxp.child_value(sxp_cfg, key)
465 if val is not None:
466 cfg[key] = typ(val)
468 # Convert deprecated options to current equivalents.
470 restart = sxp.child_value(sxp_cfg, 'restart')
471 if restart:
472 if restart == 'onreboot':
473 cfg['on_poweroff'] = 'destroy'
474 cfg['on_reboot'] = 'restart'
475 cfg['on_crash'] = 'destroy'
476 elif restart == 'always':
477 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
478 cfg[opt] = 'restart'
479 elif restart == 'never':
480 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
481 cfg[opt] = 'never'
482 else:
483 log.warn('Ignoring unrecognised value for deprecated option:'
484 'restart = \'%s\'', restart)
486 # Only extract options we know about.
487 extract_keys = LEGACY_UNSUPPORTED_BY_XENAPI_CFG
488 extract_keys += XENAPI_CFG_TO_LEGACY_CFG.values()
490 for key in extract_keys:
491 val = sxp.child_value(sxp_cfg, key)
492 if val != None:
493 try:
494 cfg[key] = LEGACY_CFG_TYPES[key](val)
495 except KeyError:
496 cfg[key] = val
497 except (TypeError, ValueError), e:
498 log.warn("Unable to parse key %s: %s: %s" %
499 (key, str(val), e))
501 # Compatibility hack -- can go soon.
502 boot_order = sxp.child_value(sxp_cfg, 'HVM_boot')
503 if boot_order:
504 cfg['HVM_boot_policy'] = 'BIOS order'
505 cfg['HVM_boot_params'] = { 'order' : boot_order }
507 # Parsing the device SXP's. In most cases, the SXP looks
508 # like this:
509 #
510 # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]]
511 #
512 # However, for PCI devices it looks like this:
513 #
514 # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1]]]]
515 #
516 # It seems the reasoning for this difference is because
517 # pciif.py needs all the PCI device configurations at
518 # the same time when creating the devices.
519 #
520 # To further complicate matters, Xen 2.0 configuration format
521 # uses the following for pci device configuration:
522 #
523 # [device, [pci, [domain, 0], [bus, 0], [dev, 1], [func, 2]]]
524 #
525 # Hence we deal with pci device configurations outside of
526 # the regular device parsing.
528 cfg['devices'] = {}
529 for dev in sxp.children(sxp_cfg, 'device'):
530 config = sxp.child0(dev)
531 dev_type = sxp.name(config)
532 dev_info = {}
534 if dev_type == 'pci':
535 pci_devs_uuid = sxp.child_value(config, 'uuid',
536 uuid.createString())
537 pci_devs = []
538 for pci_dev in sxp.children(config, 'dev'):
539 pci_dev_info = {}
540 for opt_val in pci_dev[1:]:
541 try:
542 opt, val = opt_val
543 pci_dev_info[opt] = val
544 except TypeError:
545 pass
546 pci_devs.append(pci_dev_info)
548 cfg['devices'][pci_devs_uuid] = (dev_type,
549 {'devs': pci_devs,
550 'uuid': pci_devs_uuid})
552 log.debug("XendConfig: reading device: %s" % pci_devs)
553 else:
554 self.device_add(dev_type, cfg_sxp = config, target = cfg)
555 log.debug("XendConfig: reading device: %s" % scrub_password(dev_info))
557 # Extract missing data from configuration entries
558 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
559 if image_sxp:
560 image_vcpus = sxp.child_value(image_sxp, 'vcpus')
561 if image_vcpus != None:
562 try:
563 if 'vcpus_number' not in cfg:
564 cfg['vcpus_number'] = int(image_vcpus)
565 elif cfg['vcpus_number'] != int(image_vcpus):
566 cfg['vcpus_number'] = int(image_vcpus)
567 log.warn('Overriding vcpus from %d to %d using image'
568 'vcpus value.', cfg['vcpus_number'])
569 except ValueError, e:
570 raise XendConfigError('integer expeceted: %s: %s' %
571 image_sxp, e)
573 # Deprecated cpu configuration
574 if 'cpu' in cfg:
575 if 'cpus' in cfg:
576 cfg['cpus'] = "%s,%s" % (str(cfg['cpu']), cfg['cpus'])
577 else:
578 cfg['cpus'] = str(cfg['cpu'])
580 # convert 'cpus' string to list of ints
581 # 'cpus' supports a list of ranges (0-3), seperated by
582 # commas, and negation, (^1).
583 # Precedence is settled by order of the string:
584 # "0-3,^1" -> [0,2,3]
585 # "0-3,^1,1" -> [0,1,2,3]
586 try:
587 if 'cpus' in cfg and type(cfg['cpus']) != list:
588 cpus = []
589 for c in cfg['cpus'].split(','):
590 if c.find('-') != -1:
591 (x, y) = c.split('-')
592 for i in range(int(x), int(y)+1):
593 cpus.append(int(i))
594 else:
595 # remove this element from the list
596 if c[0] == '^':
597 cpus = [x for x in cpus if x != int(c[1:])]
598 else:
599 cpus.append(int(c))
601 cfg['cpus'] = cpus
602 except ValueError, e:
603 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
605 if 'security' in cfg and isinstance(cfg['security'], str):
606 cfg['security'] = sxp.from_string(cfg['security'])
608 old_state = sxp.child_value(sxp_cfg, 'state')
609 if old_state:
610 for i in range(len(CONFIG_OLD_DOM_STATES)):
611 cfg[CONFIG_OLD_DOM_STATES[i]] = int(old_state[i] != '-')
613 return cfg
616 def _sxp_to_xapi(self, sxp_cfg):
617 """Read in an SXP Configuration object and
618 populate at much of the Xen API with valid values.
619 """
620 log.debug('_sxp_to_xapi(%s)' % scrub_password(sxp_cfg))
622 cfg = self._parse_sxp(sxp_cfg)
624 for key, typ in XENAPI_CFG_TYPES.items():
625 val = cfg.get(key)
626 if val is not None:
627 self[key] = typ(val)
629 # Convert parameters that can be directly mapped from
630 # the Legacy Config to Xen API Config
632 for apikey, cfgkey in XENAPI_CFG_TO_LEGACY_CFG.items():
633 try:
634 type_conv = XENAPI_CFG_TYPES.get(apikey)
635 if callable(type_conv):
636 self[apikey] = type_conv(cfg[cfgkey])
637 else:
638 log.warn("Unconverted key: " + apikey)
639 self[apikey] = cfg[cfgkey]
640 except KeyError:
641 pass
643 def update_with(n, o):
644 if not self.get(n):
645 self[n] = cfg.get(o, '')
647 update_with('PV_bootloader', 'bootloader')
648 update_with('PV_bootloader_args', 'bootloader_args')
650 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
651 if image_sxp:
652 self.update_with_image_sxp(image_sxp)
654 # Convert Legacy HVM parameters to Xen API configuration
655 self['platform_std_vga'] = bool0(cfg.get('stdvga', 0))
656 self['platform_serial'] = str(cfg.get('serial', ''))
657 self['platform_localtime'] = bool0(cfg.get('localtime', 0))
658 self['platform_enable_audio'] = bool0(cfg.get('soundhw', 0))
660 # make sure a sane maximum is set
661 if self['memory_static_max'] <= 0:
662 self['memory_static_max'] = self['memory_static_min']
664 self['memory_dynamic_max'] = self['memory_static_max']
665 self['memory_dynamic_min'] = self['memory_static_min']
667 # set device references in the configuration
668 self['devices'] = cfg.get('devices', {})
669 self['console_refs'] = cfg.get('console_refs', [])
670 self['vif_refs'] = cfg.get('vif_refs', [])
671 self['vbd_refs'] = cfg.get('vbd_refs', [])
672 self['vtpm_refs'] = cfg.get('vtpm_refs', [])
674 # coalesce hvm vnc frame buffer with vfb config
675 if self['image']['type'] == 'hvm' and self['image'].get('vnc', 0):
676 # add vfb device if it isn't there already
677 has_rfb = False
678 for console_uuid in self['console_refs']:
679 if self['devices'][console_uuid][1].get('protocol') == 'rfb':
680 has_rfb = True
681 break
682 if self['devices'][console_uuid][0] == 'vfb':
683 has_rfb = True
684 break
686 if not has_rfb:
687 dev_config = ['vfb']
688 # copy VNC related params from image config to vfb dev conf
689 for key in ['vncpasswd', 'vncunused', 'vncdisplay',
690 'vnclisten']:
691 if key in self['image']:
692 dev_config.append([key, self['image'][key]])
694 self.device_add('vfb', cfg_sxp = dev_config)
697 def _sxp_to_xapi_unsupported(self, sxp_cfg):
698 """Read in an SXP configuration object and populate
699 values are that not related directly supported in
700 the Xen API.
701 """
703 log.debug('_sxp_to_xapi_unsupported(%s)' % scrub_password(sxp_cfg))
705 # Parse and convert parameters used to configure
706 # the image (as well as HVM images)
707 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
708 if image_sxp:
709 image = {}
710 image['type'] = sxp.name(image_sxp)
711 for arg, conv in LEGACY_IMAGE_CFG:
712 val = sxp.child_value(image_sxp, arg, None)
713 if val != None:
714 image[arg] = conv(val)
716 image_hvm = {}
717 for arg, conv in LEGACY_IMAGE_HVM_CFG:
718 val = sxp.child_value(image_sxp, arg, None)
719 if val != None:
720 image_hvm[arg] = conv(val)
722 image_hvm_devices = {}
723 for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
724 val = sxp.child_value(image_sxp, arg, None)
725 if val != None:
726 image_hvm_devices[arg] = conv(val)
728 if image_hvm or image_hvm_devices:
729 image['hvm'] = image_hvm
730 image['hvm']['devices'] = image_hvm_devices
732 notes = sxp.children(image_sxp, 'notes')
733 if notes:
734 image['notes'] = self.notes_from_sxp(notes[0])
736 self['image'] = image
738 for apikey, imgkey in XENAPI_HVM_CFG.items():
739 val = sxp.child_value(image_sxp, imgkey, None)
740 if val != None:
741 self[apikey] = val
742 self._hvm_boot_params_from_sxp(image_sxp)
744 # extract backend value
746 backend = []
747 for c in sxp.children(sxp_cfg, 'backend'):
748 backend.append(sxp.name(sxp.child0(c)))
749 if backend:
750 self['backend'] = backend
752 # Parse and convert other Non Xen API parameters.
753 def _set_cfg_if_exists(sxp_arg):
754 val = sxp.child_value(sxp_cfg, sxp_arg)
755 if val != None:
756 if LEGACY_CFG_TYPES.get(sxp_arg):
757 self[sxp_arg] = LEGACY_CFG_TYPES[sxp_arg](val)
758 else:
759 self[sxp_arg] = val
761 _set_cfg_if_exists('shadow_memory')
762 _set_cfg_if_exists('security')
763 _set_cfg_if_exists('features')
764 _set_cfg_if_exists('on_xend_stop')
765 _set_cfg_if_exists('on_xend_start')
766 _set_cfg_if_exists('vcpu_avail')
767 _set_cfg_if_exists('cpu_weight')
768 _set_cfg_if_exists('cpu_cap')
770 # Parse and store runtime configuration
771 _set_cfg_if_exists('start_time')
772 _set_cfg_if_exists('cpu_time')
773 _set_cfg_if_exists('shutdown_reason')
774 _set_cfg_if_exists('up_time')
775 _set_cfg_if_exists('status') # TODO, deprecated
777 def _add_xapi_unsupported(self, xapi_dict):
778 """Updates the configuration object with entries that are not
779 officially supported by the Xen API but is required for
780 the rest of Xend to function.
781 """
783 # populate image
784 if 'image' in xapi_dict:
785 self['image'].update(xapi_dict['image'])
786 else:
787 hvm = self['HVM_boot_policy'] != ''
788 self['image']['type'] = hvm and 'hvm' or 'linux'
789 if hvm:
790 self['image']['hvm'] = {'devices': {}}
791 for xapi, cfgapi in XENAPI_HVM_CFG.items():
792 if xapi in self:
793 self['image']['hvm']['devices'][cfgapi] = self[xapi]
795 # currently unsupported options
796 self['image']['hvm']['device_model'] = LEGACY_DM
797 self['image']['vnc'] = 0
798 self['image']['hvm']['pae'] = 1
800 if self['platform_enable_audio']:
801 self['image']['hvm']['devices']['soundhw'] = 'sb16'
804 def _get_old_state_string(self):
805 """Returns the old xm state string.
806 @rtype: string
807 @return: old state string
808 """
809 state_string = ''
810 for state_name in CONFIG_OLD_DOM_STATES:
811 on_off = self.get(state_name, 0)
812 if on_off:
813 state_string += state_name[0]
814 else:
815 state_string += '-'
817 return state_string
820 def update_config(self, dominfo):
821 """Update configuration with the output from xc.domain_getinfo().
823 @param dominfo: Domain information via xc.domain_getinfo()
824 @type dominfo: dict
825 """
826 self._dominfo_to_xapi(dominfo)
827 self.validate()
829 def update_with_xenapi_config(self, xapi):
830 """Update configuration with a Xen API VM struct
832 @param xapi: Xen API VM Struct
833 @type xapi: dict
834 """
836 log.debug('update_with_xenapi_config: %s' % scrub_password(xapi))
838 for key, val in xapi.items():
839 type_conv = XENAPI_CFG_TYPES.get(key)
840 if type_conv is None:
841 key = key.lower()
842 type_conv = XENAPI_CFG_TYPES.get(key)
843 if callable(type_conv):
844 self[key] = type_conv(val)
845 else:
846 self[key] = val
848 def to_sxp(self, domain = None, ignore_devices = False, ignore = [],
849 legacy_only = True):
850 """ Get SXP representation of this config object.
852 Incompat: removed store_mfn, console_mfn
854 @keyword domain: (optional) XendDomainInfo to get extra information
855 from such as domid and running devices.
856 @type domain: XendDomainInfo
857 @keyword ignore: (optional) list of 'keys' that we do not want
858 to export.
859 @type ignore: list of strings
860 @rtype: list of list (SXP representation)
861 """
862 sxpr = ['domain']
864 # TODO: domid/dom is the same thing but called differently
865 # depending if it is from xenstore or sxpr.
867 if domain.getDomid() is not None:
868 sxpr.append(['domid', domain.getDomid()])
870 if not legacy_only:
871 for name, typ in XENAPI_CFG_TYPES.items():
872 if name in self and self[name] not in (None, []):
873 if typ == dict:
874 s = self[name].items()
875 else:
876 s = str(self[name])
877 sxpr.append([name, s])
879 for xenapi, legacy in XENAPI_CFG_TO_LEGACY_CFG.items():
880 if self.has_key(xenapi) and self[xenapi] not in (None, []):
881 if type(self[xenapi]) == bool:
882 # convert booleans to ints before making an sxp item
883 sxpr.append([legacy, int(self[xenapi])])
884 else:
885 sxpr.append([legacy, self[xenapi]])
887 for legacy in LEGACY_UNSUPPORTED_BY_XENAPI_CFG:
888 if legacy in ('domid', 'uuid'): # skip these
889 continue
890 if self.has_key(legacy) and self[legacy] not in (None, []):
891 sxpr.append([legacy, self[legacy]])
893 if 'image' in self and self['image']:
894 sxpr.append(['image', self.image_sxpr()])
896 sxpr.append(['status', domain.state])
897 sxpr.append(['memory_dynamic_min', self.get('memory_dynamic_min')])
898 sxpr.append(['memory_dynamic_max', self.get('memory_dynamic_max')])
900 if domain.getDomid() is not None:
901 sxpr.append(['state', self._get_old_state_string()])
903 if domain:
904 if domain.store_mfn:
905 sxpr.append(['store_mfn', domain.store_mfn])
906 if domain.console_mfn:
907 sxpr.append(['console_mfn', domain.console_mfn])
910 # Marshall devices (running or from configuration)
911 if not ignore_devices:
912 for cls in XendDevices.valid_devices():
913 found = False
915 # figure if there is a dev controller is valid and running
916 if domain and domain.getDomid() != None:
917 try:
918 controller = domain.getDeviceController(cls)
919 configs = controller.configurations()
920 for config in configs:
921 if sxp.name(config) in ('vbd', 'tap'):
922 # The bootable flag is never written to the
923 # store as part of the device config.
924 dev_uuid = sxp.child_value(config, 'uuid')
925 dev_type, dev_cfg = self['devices'][dev_uuid]
926 is_bootable = dev_cfg.get('bootable', 0)
927 config.append(['bootable', int(is_bootable)])
929 sxpr.append(['device', config])
931 found = True
932 except:
933 log.exception("dumping sxp from device controllers")
934 pass
936 # if we didn't find that device, check the existing config
937 # for a device in the same class
938 if not found:
939 for dev_type, dev_info in self.all_devices_sxpr():
940 if dev_type == cls:
941 sxpr.append(['device', dev_info])
943 return sxpr
945 def device_add(self, dev_type, cfg_sxp = None, cfg_xenapi = None,
946 target = None):
947 """Add a device configuration in SXP format or XenAPI struct format.
949 For SXP, it could be either:
951 [device, [vbd, [uname ...]]
953 or:
955 [vbd, [uname ..]]
957 @type cfg_sxp: list of lists (parsed sxp object)
958 @param cfg_sxp: SXP configuration object
959 @type cfg_xenapi: dict
960 @param cfg_xenapi: A device configuration from Xen API (eg. vbd,vif)
961 @param target: write device information to
962 @type target: None or a dictionary
963 @rtype: string
964 @return: Assigned UUID of the device.
965 """
966 if target == None:
967 target = self
969 if dev_type not in XendDevices.valid_devices():
970 raise XendConfigError("XendConfig: %s not a valid device type" %
971 dev_type)
973 if cfg_sxp == None and cfg_xenapi == None:
974 raise XendConfigError("XendConfig: device_add requires some "
975 "config.")
977 #if cfg_sxp:
978 # log.debug("XendConfig.device_add: %s" % str(cfg_sxp))
979 #if cfg_xenapi:
980 # log.debug("XendConfig.device_add: %s" % str(cfg_xenapi))
982 if cfg_sxp:
983 if sxp.child0(cfg_sxp) == 'device':
984 config = sxp.child0(cfg_sxp)
985 else:
986 config = cfg_sxp
988 dev_type = sxp.name(config)
989 dev_info = {}
991 for opt_val in config[1:]:
992 try:
993 opt, val = opt_val
994 dev_info[opt] = val
995 except (TypeError, ValueError): # unpack error
996 pass
998 if dev_type == 'vbd':
999 dev_info['bootable'] = 0
1000 if dev_info.get('dev', '').startswith('ioemu:'):
1001 dev_info['driver'] = 'ioemu'
1002 else:
1003 dev_info['driver'] = 'paravirtualised'
1005 # create uuid if it doesn't exist
1006 dev_uuid = dev_info.get('uuid', None)
1007 if not dev_uuid:
1008 dev_uuid = uuid.createString()
1009 dev_info['uuid'] = dev_uuid
1011 # store dev references by uuid for certain device types
1012 target['devices'][dev_uuid] = (dev_type, dev_info)
1013 if dev_type in ('vif', 'vbd', 'vtpm'):
1014 param = '%s_refs' % dev_type
1015 if param not in target:
1016 target[param] = []
1017 if dev_uuid not in target[param]:
1018 if dev_type == 'vbd' and not target[param]:
1019 # Compat hack -- this is the first disk, so mark it
1020 # bootable.
1021 dev_info['bootable'] = 1
1022 target[param].append(dev_uuid)
1023 elif dev_type == 'tap':
1024 if 'vbd_refs' not in target:
1025 target['vbd_refs'] = []
1026 if dev_uuid not in target['vbd_refs']:
1027 if not target['vbd_refs']:
1028 # Compat hack -- this is the first disk, so mark it
1029 # bootable.
1030 dev_info['bootable'] = 1
1031 target['vbd_refs'].append(dev_uuid)
1033 elif dev_type == 'vfb':
1034 # Populate other config with aux data that is associated
1035 # with vfb
1037 other_config = {}
1038 for key in ['vncunused', 'vncdisplay', 'vnclisten',
1039 'vncpasswd', 'type', 'display', 'xauthority',
1040 'keymap']:
1041 if key in dev_info:
1042 other_config[key] = dev_info[key]
1043 target['devices'][dev_uuid][1]['other_config'] = other_config
1046 if 'console_refs' not in target:
1047 target['console_refs'] = []
1049 # Treat VFB devices as console devices so they are found
1050 # through Xen API
1051 if dev_uuid not in target['console_refs']:
1052 target['console_refs'].append(dev_uuid)
1054 elif dev_type == 'console':
1055 if 'console_refs' not in target:
1056 target['console_refs'] = []
1057 if dev_uuid not in target['console_refs']:
1058 target['console_refs'].append(dev_uuid)
1060 return dev_uuid
1062 if cfg_xenapi:
1063 dev_info = {}
1064 dev_uuid = ''
1065 if dev_type == 'vif':
1066 if cfg_xenapi.get('MAC'): # don't add if blank
1067 dev_info['mac'] = cfg_xenapi.get('MAC')
1068 # vifname is the name on the guest, not dom0
1069 # TODO: we don't have the ability to find that out or
1070 # change it from dom0
1071 #if cfg_xenapi.get('device'): # don't add if blank
1072 # dev_info['vifname'] = cfg_xenapi.get('device')
1073 if cfg_xenapi.get('type'):
1074 dev_info['type'] = cfg_xenapi.get('type')
1075 if cfg_xenapi.get('name'):
1076 dev_info['name'] = cfg_xenapi.get('name')
1078 dev_uuid = cfg_xenapi.get('uuid', None)
1079 if not dev_uuid:
1080 dev_uuid = uuid.createString()
1081 dev_info['uuid'] = dev_uuid
1082 target['devices'][dev_uuid] = (dev_type, dev_info)
1083 target['vif_refs'].append(dev_uuid)
1085 elif dev_type in ('vbd', 'tap'):
1086 dev_info['type'] = cfg_xenapi.get('type', 'Disk')
1087 if dev_info['type'] == 'CD':
1088 old_vbd_type = 'cdrom'
1089 else:
1090 old_vbd_type = 'disk'
1092 dev_info['uname'] = cfg_xenapi.get('image', '')
1093 dev_info['dev'] = '%s:%s' % (cfg_xenapi.get('device'),
1094 old_vbd_type)
1095 dev_info['bootable'] = int(cfg_xenapi.get('bootable', 0))
1096 dev_info['driver'] = cfg_xenapi.get('driver', '')
1097 dev_info['VDI'] = cfg_xenapi.get('VDI', '')
1099 if cfg_xenapi.get('mode') == 'RW':
1100 dev_info['mode'] = 'w'
1101 else:
1102 dev_info['mode'] = 'r'
1104 dev_uuid = cfg_xenapi.get('uuid', None)
1105 if not dev_uuid:
1106 dev_uuid = uuid.createString()
1107 dev_info['uuid'] = dev_uuid
1108 target['devices'][dev_uuid] = (dev_type, dev_info)
1109 target['vbd_refs'].append(dev_uuid)
1111 elif dev_type == 'vtpm':
1112 if cfg_xenapi.get('type'):
1113 dev_info['type'] = cfg_xenapi.get('type')
1115 dev_uuid = cfg_xenapi.get('uuid', None)
1116 if not dev_uuid:
1117 dev_uuid = uuid.createString()
1118 dev_info['uuid'] = dev_uuid
1119 target['devices'][dev_uuid] = (dev_type, dev_info)
1120 target['vtpm_refs'].append(dev_uuid)
1122 elif dev_type == 'console':
1123 dev_uuid = cfg_xenapi.get('uuid', None)
1124 if not dev_uuid:
1125 dev_uuid = uuid.createString()
1126 dev_info['uuid'] = dev_uuid
1127 dev_info['protocol'] = cfg_xenapi.get('protocol', 'rfb')
1128 dev_info['other_config'] = cfg_xenapi.get('other_config', {})
1129 if dev_info['protocol'] == 'rfb':
1130 # collapse other config into devinfo for things
1131 # such as vncpasswd, vncunused, etc.
1132 dev_info.update(cfg_xenapi.get('other_config', {}))
1133 dev_info['type'] = 'vnc'
1134 target['devices'][dev_uuid] = ('vfb', dev_info)
1135 target['console_refs'].append(dev_uuid)
1137 # Finally, if we are a pvfb, we need to make a vkbd
1138 # as well that is not really exposed to Xen API
1139 vkbd_uuid = uuid.createString()
1140 target['devices'][vkbd_uuid] = ('vkbd', {})
1142 elif dev_info['protocol'] == 'vt100':
1143 # if someone tries to create a VT100 console
1144 # via the Xen API, we'll have to ignore it
1145 # because we create one automatically in
1146 # XendDomainInfo._update_consoles
1147 raise XendConfigError('Creating vt100 consoles via '
1148 'Xen API is unsupported')
1150 return dev_uuid
1152 # no valid device to add
1153 return ''
1155 def phantom_device_add(self, dev_type, cfg_xenapi = None,
1156 target = None):
1157 """Add a phantom tap device configuration in XenAPI struct format.
1158 """
1160 if target == None:
1161 target = self
1163 if dev_type not in XendDevices.valid_devices() and \
1164 dev_type not in XendDevices.pseudo_devices():
1165 raise XendConfigError("XendConfig: %s not a valid device type" %
1166 dev_type)
1168 if cfg_xenapi == None:
1169 raise XendConfigError("XendConfig: device_add requires some "
1170 "config.")
1172 if cfg_xenapi:
1173 log.debug("XendConfig.phantom_device_add: %s" % str(cfg_xenapi))
1175 if cfg_xenapi:
1176 dev_info = {}
1177 if dev_type in ('vbd', 'tap'):
1178 if dev_type == 'vbd':
1179 dev_info['uname'] = cfg_xenapi.get('image', '')
1180 dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
1181 elif dev_type == 'tap':
1182 if cfg_xenapi.get('image').find('tap:') == -1:
1183 dev_info['uname'] = 'tap:qcow:%s' % cfg_xenapi.get('image')
1184 dev_info['dev'] = '/dev/%s' % cfg_xenapi.get('device')
1185 dev_info['uname'] = cfg_xenapi.get('image')
1186 dev_info['mode'] = cfg_xenapi.get('mode')
1187 dev_info['backend'] = '0'
1188 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
1189 dev_info['uuid'] = dev_uuid
1190 self['devices'][dev_uuid] = (dev_type, dev_info)
1191 self['vbd_refs'].append(dev_uuid)
1192 return dev_uuid
1194 return ''
1196 def console_add(self, protocol, location, other_config = {}):
1197 dev_uuid = uuid.createString()
1198 if protocol == 'vt100':
1199 dev_info = {
1200 'uuid': dev_uuid,
1201 'protocol': protocol,
1202 'location': location,
1203 'other_config': other_config,
1206 if 'devices' not in self:
1207 self['devices'] = {}
1209 self['devices'][dev_uuid] = ('console', dev_info)
1210 self['console_refs'].append(dev_uuid)
1211 return dev_info
1213 return {}
1215 def console_update(self, console_uuid, key, value):
1216 for dev_uuid, (dev_type, dev_info) in self['devices'].items():
1217 if dev_uuid == console_uuid:
1218 dev_info[key] = value
1219 break
1221 def console_get_all(self, protocol):
1222 if protocol == 'vt100':
1223 consoles = [dinfo for dtype, dinfo in self['devices'].values()
1224 if dtype == 'console']
1225 return [c for c in consoles if c.get('protocol') == protocol]
1227 elif protocol == 'rfb':
1228 vfbs = [dinfo for dtype, dinfo in self['devices'].values()
1229 if dtype == 'vfb']
1231 # move all non-console key values to other_config before
1232 # returning console config
1233 valid_keys = ['uuid', 'location']
1234 for vfb in vfbs:
1235 other_config = {}
1236 for key, val in vfb.items():
1237 if key not in valid_keys:
1238 other_config[key] = vfb[key]
1239 del vfb[key]
1240 vfb['other_config'] = other_config
1241 vfb['protocol'] = 'rfb'
1243 return vfbs
1245 else:
1246 return []
1248 def device_update(self, dev_uuid, cfg_sxp = [], cfg_xenapi = {}):
1249 """Update an existing device with the new configuration.
1251 @rtype: boolean
1252 @return: Returns True if succesfully found and updated a device conf
1253 """
1254 if dev_uuid in self['devices'] and cfg_sxp:
1255 if sxp.child0(cfg_sxp) == 'device':
1256 config = sxp.child0(cfg_sxp)
1257 else:
1258 config = cfg_sxp
1260 dev_type, dev_info = self['devices'][dev_uuid]
1261 for opt_val in config[1:]:
1262 try:
1263 opt, val = opt_val
1264 dev_info[opt] = val
1265 except (TypeError, ValueError):
1266 pass # no value for this config option
1268 self['devices'][dev_uuid] = (dev_type, dev_info)
1269 return True
1271 elif dev_uuid in self['devices'] and cfg_xenapi:
1272 dev_type, dev_info = self['devices'][dev_uuid]
1273 for key, val in cfg_xenapi.items():
1274 dev_info[key] = val
1275 self['devices'][dev_uuid] = (dev_type, dev_info)
1277 return False
1280 def device_sxpr(self, dev_uuid = None, dev_type = None, dev_info = None):
1281 """Get Device SXPR by either giving the device UUID or (type, config).
1283 @rtype: list of lists
1284 @return: device config sxpr
1285 """
1286 sxpr = []
1287 if dev_uuid != None and dev_uuid in self['devices']:
1288 dev_type, dev_info = self['devices'][dev_uuid]
1290 if dev_type == None or dev_info == None:
1291 raise XendConfigError("Required either UUID or device type and "
1292 "configuration dictionary.")
1294 sxpr.append(dev_type)
1295 if dev_type in ('console', 'vfb'):
1296 config = [(opt, val) for opt, val in dev_info.items()
1297 if opt != 'other_config']
1298 else:
1299 config = [(opt, val) for opt, val in dev_info.items()]
1301 sxpr += config
1303 return sxpr
1305 def ordered_device_refs(self):
1306 result = (self['console_refs'] +
1307 self['vbd_refs'] +
1308 self['vif_refs'] +
1309 self['vtpm_refs'])
1310 result.extend([u for u in self['devices'].keys() if u not in result])
1311 return result
1313 def all_devices_sxpr(self):
1314 """Returns the SXPR for all devices in the current configuration."""
1315 sxprs = []
1316 pci_devs = []
1318 if 'devices' not in self:
1319 return sxprs
1321 ordered_refs = self.ordered_device_refs()
1322 for dev_uuid in ordered_refs:
1323 dev_type, dev_info = self['devices'][dev_uuid]
1324 if dev_type == 'pci': # special case for pci devices
1325 sxpr = [['uuid', dev_info['uuid']]]
1326 for pci_dev_info in dev_info['devs']:
1327 pci_dev_sxpr = ['dev']
1328 for opt, val in pci_dev_info.items():
1329 pci_dev_sxpr.append([opt, val])
1330 sxpr.append(pci_dev_sxpr)
1331 sxprs.append((dev_type, sxpr))
1332 else:
1333 sxpr = self.device_sxpr(dev_type = dev_type,
1334 dev_info = dev_info)
1335 sxprs.append((dev_type, sxpr))
1337 return sxprs
1339 def image_sxpr(self):
1340 """Returns a backwards compatible image SXP expression that is
1341 used in xenstore's /vm/<uuid>/image value and xm list."""
1342 image = [self['image'].get('type', 'linux')]
1343 if self.has_key('PV_kernel'):
1344 image.append(['kernel', self['PV_kernel']])
1345 if self.has_key('PV_ramdisk') and self['PV_ramdisk']:
1346 image.append(['ramdisk', self['PV_ramdisk']])
1347 if self.has_key('PV_args') and self['PV_args']:
1348 image.append(['args', self['PV_args']])
1350 for arg, conv in LEGACY_IMAGE_CFG:
1351 if self['image'].has_key(arg):
1352 image.append([arg, self['image'][arg]])
1354 if 'hvm' in self['image']:
1355 for arg, conv in LEGACY_IMAGE_HVM_CFG:
1356 if self['image']['hvm'].get(arg):
1357 image.append([arg, conv(self['image']['hvm'][arg])])
1359 if 'hvm' in self['image'] and 'devices' in self['image']['hvm']:
1360 for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
1361 val = self['image']['hvm']['devices'].get(arg)
1362 if val != None:
1363 try:
1364 if conv: val = conv(val)
1365 except (ValueError, TypeError):
1366 if type(val) == bool: val = int(val)
1368 image.append([arg, val])
1370 if 'notes' in self['image']:
1371 image.append(self.notes_sxp(self['image']['notes']))
1373 return image
1375 def update_with_image_sxp(self, image_sxp, bootloader = False):
1376 # Convert Legacy "image" config to Xen API PV_*
1377 # configuration
1378 log.debug("update_with_image_sxp(%s)" % scrub_password(image_sxp))
1380 # user-specified args must come last: previous releases did this and
1381 # some domU kernels rely upon the ordering.
1382 kernel_args = sxp.child_value(image_sxp, 'args', '')
1384 # attempt to extract extra arguments from SXP config
1385 arg_ip = sxp.child_value(image_sxp, 'ip')
1386 if arg_ip and not re.search(r'ip=[^ ]+', kernel_args):
1387 kernel_args = 'ip=%s ' % arg_ip + kernel_args
1388 arg_root = sxp.child_value(image_sxp, 'root')
1389 if arg_root and not re.search(r'root=', kernel_args):
1390 kernel_args = 'root=%s ' % arg_root + kernel_args
1392 if bootloader:
1393 self['_temp_using_bootloader'] = '1'
1394 self['_temp_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1395 self['_temp_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1396 self['_temp_args'] = kernel_args
1397 else:
1398 self['PV_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1399 self['PV_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1400 self['PV_args'] = kernel_args
1402 # Store image SXP in python dictionary format
1403 image = {}
1404 image['type'] = sxp.name(image_sxp)
1405 for arg, conv in LEGACY_IMAGE_CFG:
1406 val = sxp.child_value(image_sxp, arg, None)
1407 if val != None:
1408 image[arg] = conv(val)
1410 image_hvm = {}
1411 for arg, conv in LEGACY_IMAGE_HVM_CFG:
1412 val = sxp.child_value(image_sxp, arg, None)
1413 if val != None:
1414 image_hvm[arg] = conv(val)
1416 image_hvm_devices = {}
1417 for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
1418 val = sxp.child_value(image_sxp, arg, None)
1419 if val != None:
1420 try:
1421 image_hvm_devices[arg] = conv(val)
1422 except (ValueError, TypeError):
1423 image_hvm_devices[arg] = val
1426 if image_hvm or image_hvm_devices:
1427 image['hvm'] = image_hvm
1428 image['hvm']['devices'] = image_hvm_devices
1430 notes = sxp.children(image_sxp, 'notes')
1431 if notes:
1432 image['notes'] = self.notes_from_sxp(notes[0])
1434 self['image'] = image
1436 for apikey, imgkey in XENAPI_HVM_CFG.items():
1437 val = sxp.child_value(image_sxp, imgkey, None)
1438 if val != None:
1439 type_conv = XENAPI_CFG_TYPES[apikey]
1440 if callable(type_conv):
1441 self[apikey] = type_conv(val)
1442 else:
1443 self[apikey] = val
1444 self._hvm_boot_params_from_sxp(image_sxp)
1446 def set_notes(self, notes):
1447 'Add parsed elfnotes to image'
1448 self['image']['notes'] = notes
1450 def get_notes(self):
1451 try:
1452 return self['image']['notes'] or {}
1453 except KeyError:
1454 return {}
1456 def notes_from_sxp(self, nsxp):
1457 notes = {}
1458 for note in sxp.children(nsxp):
1459 notes[note[0]] = note[1]
1460 return notes
1462 def notes_sxp(self, notes):
1463 nsxp = ['notes']
1464 for k, v in notes.iteritems():
1465 nsxp.append([k, str(v)])
1466 return nsxp
1468 def _hvm_boot_params_from_sxp(self, image_sxp):
1469 boot = sxp.child_value(image_sxp, 'boot', None)
1470 if boot is not None:
1471 self['HVM_boot_policy'] = 'BIOS order'
1472 self['HVM_boot_params'] = { 'order' : boot }
1476 # debugging
1479 if __name__ == "__main__":
1480 pass