ia64/xen-unstable

view tools/python/xen/xend/XendConfig.py @ 13237:7cd6c032689e

Don't lose the image settings when rebooting domains. This fixes recent HVM
reboot problems -- we were passing 0 to shadow_mem_control, because Xend was
treating the domain as PV.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author Ewan Mellor <ewan@xensource.com>
date Tue Jan 02 13:04:01 2007 +0000 (2007-01-02)
parents 5c268a24e44b
children 48c9028e6f8e
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.WARN)
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
82 # Mapping from XendConfig configuration keys to the old
83 # legacy configuration keys that map directly.
85 XENAPI_CFG_TO_LEGACY_CFG = {
86 'uuid': 'uuid',
87 'vcpus_number': 'vcpus',
88 'cpus': 'cpus',
89 'memory_static_min': 'memory',
90 'memory_static_max': 'maxmem',
91 'name_label': 'name',
92 'actions_after_shutdown': 'on_poweroff',
93 'actions_after_reboot': 'on_reboot',
94 'actions_after_crash': 'on_crash',
95 'platform_localtime': 'localtime',
96 'PV_bootloader': 'bootloader',
97 'PV_bootloader_args': 'bootloader_args',
98 }
100 LEGACY_CFG_TO_XENAPI_CFG = reverse_dict(XENAPI_CFG_TO_LEGACY_CFG)
102 # Mapping from XendConfig configuration keys to the old
103 # legacy configuration keys that are found in the 'image'
104 # SXP object.
105 XENAPI_HVM_CFG = {
106 'platform_std_vga': 'stdvga',
107 'platform_serial' : 'serial',
108 'platform_localtime': 'localtime',
109 'platform_enable_audio': 'soundhw',
110 'platform_keymap' : 'keymap',
111 }
113 # List of XendConfig configuration keys that have no direct equivalent
114 # in the old world.
116 XENAPI_CFG_TYPES = {
117 'uuid': str,
118 'power_state': str,
119 'name_label': str,
120 'name_description': str,
121 'user_version': str,
122 'is_a_template': bool0,
123 'resident_on': str,
124 'memory_static_min': int,
125 'memory_static_max': int,
126 'memory_dynamic_min': int,
127 'memory_dynamic_max': int,
128 'memory_actual': int,
129 'vcpus_policy': str,
130 'vcpus_params': str,
131 'vcpus_number': int,
132 'vcpus_features_required': list,
133 'vcpus_features_can_use': list,
134 'vcpus_features_force_on': list,
135 'vcpus_features_force_off': list,
136 'actions_after_shutdown': str,
137 'actions_after_reboot': str,
138 'actions_after_suspend': str,
139 'actions_after_crash': str,
140 'tpm_instance': int,
141 'tpm_backend': int,
142 'PV_bootloader': str,
143 'PV_kernel': str,
144 'PV_ramdisk': str,
145 'PV_args': str,
146 'PV_bootloader_args': str,
147 'HVM_boot': str,
148 'platform_std_vga': bool0,
149 'platform_serial': str,
150 'platform_localtime': bool0,
151 'platform_clock_offset': bool0,
152 'platform_enable_audio': bool0,
153 'platform_keymap': str,
154 'pci_bus': str,
155 'tools_version': dict,
156 'otherconfig': dict,
157 }
159 # List of legacy configuration keys that have no equivalent in the
160 # Xen API, but are still stored in XendConfig.
162 LEGACY_UNSUPPORTED_BY_XENAPI_CFG = [
163 # roundtripped (dynamic, unmodified)
164 'shadow_memory',
165 'security',
166 'vcpu_avail',
167 'cpu_weight',
168 'cpu_cap',
169 'features',
170 # read/write
171 'on_xend_start',
172 'on_xend_stop',
173 # read-only
174 'domid',
175 'start_time',
176 'cpu_time',
177 'online_vcpus',
178 # write-once
179 'cpu',
180 'cpus',
181 ]
183 LEGACY_CFG_TYPES = {
184 'uuid': str,
185 'name': str,
186 'vcpus': int,
187 'vcpu_avail': int,
188 'memory': int,
189 'shadow_memory': int,
190 'maxmem': int,
191 'start_time': float,
192 'cpu_cap': int,
193 'cpu_weight': int,
194 'cpu_time': float,
195 'features': str,
196 'localtime': int,
197 'name': str,
198 'on_poweroff': str,
199 'on_reboot': str,
200 'on_crash': str,
201 'on_xend_stop': str,
202 'on_xend_start': str,
203 'online_vcpus': int,
204 }
206 # Values that should be stored in xenstore's /vm/<uuid> that is used
207 # by Xend. Used in XendDomainInfo to restore running VM state from
208 # xenstore.
209 LEGACY_XENSTORE_VM_PARAMS = [
210 'uuid',
211 'name',
212 'vcpus',
213 'vcpu_avail',
214 'memory',
215 'shadow_memory',
216 'maxmem',
217 'start_time',
218 'name',
219 'on_poweroff',
220 'on_crash',
221 'on_reboot',
222 'on_xend_start',
223 'on_xend_stop',
224 ]
226 LEGACY_IMAGE_CFG = [
227 ('root', str),
228 ('ip', str),
229 ('nographic', int),
230 ('vnc', int),
231 ('sdl', int),
232 ('vncdisplay', int),
233 ('vncunused', int),
234 ('vncpasswd', str),
235 ]
237 LEGACY_IMAGE_HVM_CFG = [
238 ('device_model', str),
239 ('display', str),
240 ('xauthority', str),
241 ('vncconsole', int),
242 ('pae', int),
243 ('apic', int),
244 ]
246 LEGACY_IMAGE_HVM_DEVICES_CFG = [
247 ('acpi', int),
248 ('boot', str),
249 ('fda', str),
250 ('fdb', str),
251 ('isa', str),
252 ('keymap', str),
253 ('localtime', str),
254 ('serial', str),
255 ('stdvga', int),
256 ('soundhw', str),
257 ('usb', str),
258 ('usbdevice', str),
259 ('vcpus', int),
260 ]
262 ##
263 ## Config Choices
264 ##
266 CONFIG_RESTART_MODES = ('restart', 'destroy', 'preserve', 'rename-restart')
267 CONFIG_OLD_DOM_STATES = ('running', 'blocked', 'paused', 'shutdown',
268 'crashed', 'dying')
270 class XendConfigError(VmError):
271 def __str__(self):
272 return 'Invalid Configuration: %s' % str(self.value)
274 ##
275 ## XendConfig Class (an extended dictionary)
276 ##
278 class XendConfig(dict):
279 """ The new Xend VM Configuration.
281 Stores the configuration in xenapi compatible format but retains
282 import and export functions for SXP.
283 """
284 def __init__(self, filename = None, sxp_obj = None,
285 xapi = None, dominfo = None):
287 dict.__init__(self)
288 self.update(self._defaults())
290 if filename:
291 try:
292 sxp_obj = sxp.parse(open(filename,'r'))
293 sxp_obj = sxp_obj[0]
294 except IOError, e:
295 raise XendConfigError("Unable to read file: %s" % filename)
297 if sxp_obj:
298 self._sxp_to_xapi(sxp_obj)
299 self._sxp_to_xapi_unsupported(sxp_obj)
300 elif xapi:
301 self.update_with_xenapi_config(xapi)
302 self._add_xapi_unsupported(xapi)
303 elif dominfo:
304 # output from xc.domain_getinfo
305 self._dominfo_to_xapi(dominfo)
307 log.debug('XendConfig.init: %s' % scrub_password(self))
309 # validators go here
310 self.validate()
312 """ In time, we should enable this type checking addition. It is great
313 also for tracking bugs and unintended writes to XendDomainInfo.info
314 def __setitem__(self, key, value):
315 type_conv = XENAPI_CFG_TYPES.get(key)
316 if callable(type_conv):
317 try:
318 dict.__setitem__(self, key, type_conv(value))
319 except (ValueError, TypeError):
320 raise XendConfigError("Wrong type for configuration value " +
321 "%s. Expected %s" %
322 (key, type_conv.__name__))
323 else:
324 dict.__setitem__(self, key, value)
325 """
327 def _defaults(self):
328 defaults = {
329 'uuid': uuid.createString(),
330 'name_label': 'Domain-Unnamed',
331 'actions_after_shutdown': 'destroy',
332 'actions_after_reboot': 'restart',
333 'actions_after_crash': 'restart',
334 'actions_after_suspend': '',
335 'features': '',
336 'PV_bootloader': '',
337 'PV_kernel': '',
338 'PV_ramdisk': '',
339 'PV_args': '',
340 'PV_bootloader_args': '',
341 'HVM_boot': '',
342 'memory_static_min': 0,
343 'memory_dynamic_min': 0,
344 'shadow_memory': 0,
345 'memory_static_max': 0,
346 'memory_dynamic_max': 0,
347 'memory_actual': 0,
348 'devices': {},
349 'image': {},
350 'security': None,
351 'on_xend_start': 'ignore',
352 'on_xend_stop': 'ignore',
353 'cpus': [],
354 'cpu_weight': 256,
355 'cpu_cap': 0,
356 'vcpus_number': 1,
357 'online_vcpus': 1,
358 'max_vcpu_id': 0,
359 'vcpu_avail': 1,
360 'console_refs': [],
361 'vif_refs': [],
362 'vbd_refs': [],
363 'vtpm_refs': [],
364 }
366 defaults['name_label'] = 'Domain-' + defaults['uuid']
367 return defaults
369 def _memory_sanity_check(self):
370 if self['memory_static_min'] == 0:
371 self['memory_static_min'] = self['memory_dynamic_min']
373 # If the static max is not set, let's set it to dynamic max.
374 # If the static max is smaller than static min, then fix it!
375 self['memory_static_max'] = max(self['memory_static_max'],
376 self['memory_dynamic_max'],
377 self['memory_static_min'])
379 for mem_type in ('memory_static_min', 'memory_static_max'):
380 if self[mem_type] <= 0:
381 raise XendConfigError('Memory value too low for %s: %d' %
382 (mem_type, self[mem_type]))
384 def _actions_sanity_check(self):
385 for event in ['shutdown', 'reboot', 'crash']:
386 if self['actions_after_' + event] not in CONFIG_RESTART_MODES:
387 raise XendConfigError('Invalid event handling mode: ' +
388 event)
390 def _vcpus_sanity_check(self):
391 if self.get('vcpus_number') != None:
392 self['vcpu_avail'] = (1 << self['vcpus_number']) - 1
394 def _uuid_sanity_check(self):
395 """Make sure UUID is in proper string format with hyphens."""
396 self['uuid'] = uuid.toString(uuid.fromString(self['uuid']))
398 def validate(self):
399 self._memory_sanity_check()
400 self._actions_sanity_check()
401 self._vcpus_sanity_check()
402 self._uuid_sanity_check()
404 def _dominfo_to_xapi(self, dominfo):
405 self['domid'] = dominfo['domid']
406 self['online_vcpus'] = dominfo['online_vcpus']
407 self['max_vcpu_id'] = dominfo['max_vcpu_id']
408 self['memory_dynamic_min'] = (dominfo['mem_kb'] + 1023)/1024
409 self['memory_dynamic_max'] = (dominfo['maxmem_kb'] + 1023)/1024
410 self['cpu_time'] = dominfo['cpu_time']/1e9
411 # TODO: i don't know what the security stuff expects here
412 if dominfo.get('ssidref'):
413 self['security'] = [['ssidref', dominfo['ssidref']]]
414 self['shutdown_reason'] = dominfo['shutdown_reason']
416 # parse state into Xen API states
417 self['running'] = dominfo['running']
418 self['crashed'] = dominfo['crashed']
419 self['dying'] = dominfo['dying']
420 self['shutdown'] = dominfo['shutdown']
421 self['paused'] = dominfo['paused']
422 self['blocked'] = dominfo['blocked']
424 if 'name' in dominfo:
425 self['name_label'] = dominfo['name']
427 if 'handle' in dominfo:
428 self['uuid'] = uuid.toString(dominfo['handle'])
430 def _parse_sxp(self, sxp_cfg):
431 """ Populate this XendConfig using the parsed SXP.
433 @param sxp_cfg: Parsed SXP Configuration
434 @type sxp_cfg: list of lists
435 @rtype: dictionary
436 @return: A dictionary containing the parsed options of the SXP.
437 """
438 cfg = {}
440 for key, typ in XENAPI_CFG_TYPES.items():
441 val = sxp.child_value(sxp_cfg, key)
442 if val is not None:
443 cfg[key] = typ(val)
445 # Convert deprecated options to current equivalents.
447 restart = sxp.child_value(sxp_cfg, 'restart')
448 if restart:
449 if restart == 'onreboot':
450 cfg['on_poweroff'] = 'destroy'
451 cfg['on_reboot'] = 'restart'
452 cfg['on_crash'] = 'destroy'
453 elif restart == 'always':
454 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
455 cfg[opt] = 'restart'
456 elif restart == 'never':
457 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
458 cfg[opt] = 'never'
459 else:
460 log.warn('Ignoring unrecognised value for deprecated option:'
461 'restart = \'%s\'', restart)
463 # Only extract options we know about.
464 extract_keys = LEGACY_UNSUPPORTED_BY_XENAPI_CFG
465 extract_keys += XENAPI_CFG_TO_LEGACY_CFG.values()
467 for key in extract_keys:
468 val = sxp.child_value(sxp_cfg, key)
469 if val != None:
470 try:
471 cfg[key] = LEGACY_CFG_TYPES[key](val)
472 except KeyError:
473 cfg[key] = val
474 except (TypeError, ValueError), e:
475 log.warn("Unable to parse key %s: %s: %s" %
476 (key, str(val), e))
478 # Parsing the device SXP's. In most cases, the SXP looks
479 # like this:
480 #
481 # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]]
482 #
483 # However, for PCI devices it looks like this:
484 #
485 # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1]]]]
486 #
487 # It seems the reasoning for this difference is because
488 # pciif.py needs all the PCI device configurations at
489 # the same time when creating the devices.
490 #
491 # To further complicate matters, Xen 2.0 configuration format
492 # uses the following for pci device configuration:
493 #
494 # [device, [pci, [domain, 0], [bus, 0], [dev, 1], [func, 2]]]
495 #
496 # Hence we deal with pci device configurations outside of
497 # the regular device parsing.
499 cfg['devices'] = {}
500 for dev in sxp.children(sxp_cfg, 'device'):
501 config = sxp.child0(dev)
502 dev_type = sxp.name(config)
503 dev_info = {}
505 if dev_type == 'pci':
506 pci_devs_uuid = sxp.child_value(config, 'uuid',
507 uuid.createString())
508 pci_devs = []
509 for pci_dev in sxp.children(config, 'dev'):
510 pci_dev_info = {}
511 for opt, val in pci_dev[1:]:
512 pci_dev_info[opt] = val
513 pci_devs.append(pci_dev_info)
515 cfg['devices'][pci_devs_uuid] = (dev_type,
516 {'devs': pci_devs,
517 'uuid': pci_devs_uuid})
519 log.debug("XendConfig: reading device: %s" % pci_devs)
520 else:
521 self.device_add(dev_type, cfg_sxp = config, target = cfg)
522 log.debug("XendConfig: reading device: %s" % scrub_password(dev_info))
524 # Extract missing data from configuration entries
525 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
526 if image_sxp:
527 image_vcpus = sxp.child_value(image_sxp, 'vcpus')
528 if image_vcpus != None:
529 try:
530 if 'vcpus_number' not in cfg:
531 cfg['vcpus_number'] = int(image_vcpus)
532 elif cfg['vcpus_number'] != int(image_vcpus):
533 cfg['vcpus_number'] = int(image_vcpus)
534 log.warn('Overriding vcpus from %d to %d using image'
535 'vcpus value.', cfg['vcpus_number'])
536 except ValueError, e:
537 raise XendConfigError('integer expeceted: %s: %s' %
538 image_sxp, e)
540 # Deprecated cpu configuration
541 if 'cpu' in cfg:
542 if 'cpus' in cfg:
543 cfg['cpus'] = "%s,%s" % (str(cfg['cpu']), cfg['cpus'])
544 else:
545 cfg['cpus'] = str(cfg['cpu'])
547 # convert 'cpus' string to list of ints
548 # 'cpus' supports a list of ranges (0-3), seperated by
549 # commas, and negation, (^1).
550 # Precedence is settled by order of the string:
551 # "0-3,^1" -> [0,2,3]
552 # "0-3,^1,1" -> [0,1,2,3]
553 try:
554 if 'cpus' in cfg:
555 cpus = []
556 for c in cfg['cpus'].split(','):
557 if c.find('-') != -1:
558 (x, y) = c.split('-')
559 for i in range(int(x), int(y)+1):
560 cpus.append(int(i))
561 else:
562 # remove this element from the list
563 if c[0] == '^':
564 cpus = [x for x in cpus if x != int(c[1:])]
565 else:
566 cpus.append(int(c))
568 cfg['cpus'] = cpus
569 except ValueError, e:
570 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
572 if 'security' in cfg and isinstance(cfg['security'], str):
573 cfg['security'] = sxp.from_string(cfg['security'])
575 # TODO: get states
576 old_state = sxp.child_value(sxp_cfg, 'state')
577 if old_state:
578 for i in range(len(CONFIG_OLD_DOM_STATES)):
579 cfg[CONFIG_OLD_DOM_STATES[i]] = int(old_state[i] != '-')
581 return cfg
584 def _sxp_to_xapi(self, sxp_cfg):
585 """Read in an SXP Configuration object and
586 populate at much of the Xen API with valid values.
587 """
588 log.debug('_sxp_to_xapi(%s)' % scrub_password(sxp_cfg))
590 cfg = self._parse_sxp(sxp_cfg)
592 for key, typ in XENAPI_CFG_TYPES.items():
593 val = cfg.get(key)
594 if val is not None:
595 self[key] = typ(val)
597 # Convert parameters that can be directly mapped from
598 # the Legacy Config to Xen API Config
600 for apikey, cfgkey in XENAPI_CFG_TO_LEGACY_CFG.items():
601 try:
602 type_conv = XENAPI_CFG_TYPES.get(apikey)
603 if callable(type_conv):
604 self[apikey] = type_conv(cfg[cfgkey])
605 else:
606 log.warn("Unconverted key: " + apikey)
607 self[apikey] = cfg[cfgkey]
608 except KeyError:
609 pass
611 def update_with(n, o):
612 if not self.get(n):
613 self[n] = cfg.get(o, '')
615 update_with('PV_bootloader', 'bootloader')
616 update_with('PV_bootloader_args', 'bootloader_args')
618 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
619 if image_sxp:
620 self.update_with_image_sxp(image_sxp)
622 # Convert Legacy HVM parameters to Xen API configuration
623 self['platform_std_vga'] = bool0(cfg.get('stdvga', 0))
624 self['platform_serial'] = str(cfg.get('serial', ''))
625 self['platform_localtime'] = bool0(cfg.get('localtime', 0))
626 self['platform_enable_audio'] = bool0(cfg.get('soundhw', 0))
628 # make sure a sane maximum is set
629 if self['memory_static_max'] <= 0:
630 self['memory_static_max'] = self['memory_static_min']
632 self['memory_dynamic_max'] = self['memory_static_max']
633 self['memory_dynamic_min'] = self['memory_static_min']
635 # make sure max_vcpu_id is set correctly
636 self['max_vcpu_id'] = self['vcpus_number'] - 1
638 # set device references in the configuration
639 self['devices'] = cfg.get('devices', {})
641 self['console_refs'] = []
642 self['vif_refs'] = []
643 self['vbd_refs'] = []
644 self['vtpm_refs'] = []
645 for dev_uuid, (dev_type, dev_info) in self['devices'].items():
646 if dev_type == 'vif':
647 self['vif_refs'].append(dev_uuid)
648 elif dev_type in ('vbd','tap'):
649 self['vbd_refs'].append(dev_uuid)
650 elif dev_type in ('vtpm',):
651 self['vtpm_refs'].append(dev_uuid)
654 def _sxp_to_xapi_unsupported(self, sxp_cfg):
655 """Read in an SXP configuration object and populate
656 values are that not related directly supported in
657 the Xen API.
658 """
660 log.debug('_sxp_to_xapi_unsupported(%s)' % scrub_password(sxp_cfg))
662 # Parse and convert parameters used to configure
663 # the image (as well as HVM images)
664 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
665 if image_sxp:
666 image = {}
667 image['type'] = sxp.name(image_sxp)
668 for arg, conv in LEGACY_IMAGE_CFG:
669 val = sxp.child_value(image_sxp, arg, None)
670 if val != None:
671 image[arg] = conv(val)
673 image_hvm = {}
674 for arg, conv in LEGACY_IMAGE_HVM_CFG:
675 val = sxp.child_value(image_sxp, arg, None)
676 if val != None:
677 image_hvm[arg] = conv(val)
679 image_hvm_devices = {}
680 for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
681 val = sxp.child_value(image_sxp, arg, None)
682 if val != None:
683 image_hvm_devices[arg] = conv(val)
685 if image_hvm or image_hvm_devices:
686 image['hvm'] = image_hvm
687 image['hvm']['devices'] = image_hvm_devices
689 self['image'] = image
691 for apikey, imgkey in XENAPI_HVM_CFG.items():
692 val = sxp.child_value(image_sxp, imgkey, None)
693 if val != None:
694 self[apikey] = val
696 # extract backend value
698 backend = []
699 for c in sxp.children(sxp_cfg, 'backend'):
700 backend.append(sxp.name(sxp.child0(c)))
701 if backend:
702 self['backend'] = backend
704 # Parse and convert other Non Xen API parameters.
705 def _set_cfg_if_exists(sxp_arg):
706 val = sxp.child_value(sxp_cfg, sxp_arg)
707 if val != None:
708 if LEGACY_CFG_TYPES.get(sxp_arg):
709 self[sxp_arg] = LEGACY_CFG_TYPES[sxp_arg](val)
710 else:
711 self[sxp_arg] = val
713 _set_cfg_if_exists('shadow_memory')
714 _set_cfg_if_exists('security')
715 _set_cfg_if_exists('features')
716 _set_cfg_if_exists('on_xend_stop')
717 _set_cfg_if_exists('on_xend_start')
718 _set_cfg_if_exists('vcpu_avail')
719 _set_cfg_if_exists('max_vcpu_id') # needed for vcpuDomDetails
720 _set_cfg_if_exists('cpu_weight')
721 _set_cfg_if_exists('cpu_cap')
723 # Parse and store runtime configuration
724 _set_cfg_if_exists('start_time')
725 _set_cfg_if_exists('online_vcpus')
726 _set_cfg_if_exists('cpu_time')
727 _set_cfg_if_exists('shutdown_reason')
728 _set_cfg_if_exists('up_time')
729 _set_cfg_if_exists('status') # TODO, deprecated
731 def _add_xapi_unsupported(self, xapi_dict):
732 """Updates the configuration object with entries that are not
733 officially supported by the Xen API but is required for
734 the rest of Xend to function.
735 """
737 # populate image
738 if 'image' in xapi_dict:
739 self['image'].update(xapi_dict['image'])
740 else:
741 hvm = self['HVM_boot'] != ''
742 self['image']['type'] = hvm and 'hvm' or 'linux'
743 if hvm:
744 self['image']['hvm'] = {}
745 for xapi, cfgapi in XENAPI_HVM_CFG.items():
746 self['image']['hvm'][cfgapi] = self[xapi]
749 def _get_old_state_string(self):
750 """Returns the old xm state string.
751 @rtype: string
752 @return: old state string
753 """
754 state_string = ''
755 for state_name in CONFIG_OLD_DOM_STATES:
756 on_off = self.get(state_name, 0)
757 if on_off:
758 state_string += state_name[0]
759 else:
760 state_string += '-'
762 return state_string
765 def update_config(self, dominfo):
766 """Update configuration with the output from xc.domain_getinfo().
768 @param dominfo: Domain information via xc.domain_getinfo()
769 @type dominfo: dict
770 """
771 self._dominfo_to_xapi(dominfo)
772 self.validate()
774 def update_with_xenapi_config(self, xapi):
775 """Update configuration with a Xen API VM struct
777 @param xapi: Xen API VM Struct
778 @type xapi: dict
779 """
781 log.debug('update_with_xenapi_config: %s' % scrub_password(xapi))
783 for key, val in xapi.items():
784 type_conv = XENAPI_CFG_TYPES.get(key)
785 if type_conv is None:
786 key = key.lower()
787 type_conv = XENAPI_CFG_TYPES.get(key)
788 if callable(type_conv):
789 self[key] = type_conv(val)
790 else:
791 self[key] = val
793 self.validate()
795 def to_sxp(self, domain = None, ignore_devices = False, ignore = [],
796 legacy_only = True):
797 """ Get SXP representation of this config object.
799 Incompat: removed store_mfn, console_mfn
801 @keyword domain: (optional) XendDomainInfo to get extra information
802 from such as domid and running devices.
803 @type domain: XendDomainInfo
804 @keyword ignore: (optional) list of 'keys' that we do not want
805 to export.
806 @type ignore: list of strings
807 @rtype: list of list (SXP representation)
808 """
809 sxpr = ['domain']
811 # TODO: domid/dom is the same thing but called differently
812 # depending if it is from xenstore or sxpr.
814 if domain.getDomid() is not None:
815 sxpr.append(['domid', domain.getDomid()])
817 if not legacy_only:
818 for name in XENAPI_CFG_TYPES.keys():
819 if name in self and self[name] not in (None, []):
820 sxpr.append([name, str(self[name])])
822 for xenapi, legacy in XENAPI_CFG_TO_LEGACY_CFG.items():
823 if self.has_key(xenapi) and self[xenapi] not in (None, []):
824 if type(self[xenapi]) == bool:
825 # convert booleans to ints before making an sxp item
826 sxpr.append([legacy, int(self[xenapi])])
827 else:
828 sxpr.append([legacy, self[xenapi]])
830 for legacy in LEGACY_UNSUPPORTED_BY_XENAPI_CFG:
831 if legacy in ('domid', 'uuid'): # skip these
832 continue
833 if self.has_key(legacy) and self[legacy] not in (None, []):
834 sxpr.append([legacy, self[legacy]])
836 if 'image' in self and self['image']:
837 sxpr.append(['image', self.image_sxpr()])
839 sxpr.append(['status', domain.state])
840 sxpr.append(['memory_dynamic_min', self.get('memory_dynamic_min')])
841 sxpr.append(['memory_dynamic_max', self.get('memory_dynamic_max')])
843 if domain.getDomid() is not None:
844 sxpr.append(['state', self._get_old_state_string()])
846 if domain:
847 if domain.store_mfn:
848 sxpr.append(['store_mfn', domain.store_mfn])
849 if domain.console_mfn:
850 sxpr.append(['console_mfn', domain.console_mfn])
853 # Marshall devices (running or from configuration)
854 if not ignore_devices:
855 for cls in XendDevices.valid_devices():
856 found = False
858 # figure if there is a device that is running
859 if domain:
860 try:
861 controller = domain.getDeviceController(cls)
862 configs = controller.configurations()
863 for config in configs:
864 sxpr.append(['device', config])
865 found = True
866 except:
867 log.exception("dumping sxp from device controllers")
868 pass
870 # if we didn't find that device, check the existing config
871 # for a device in the same class
872 if not found:
873 for dev_type, dev_info in self.all_devices_sxpr():
874 if dev_type == cls:
875 sxpr.append(['device', dev_info])
877 return sxpr
879 def device_add(self, dev_type, cfg_sxp = None, cfg_xenapi = None,
880 target = None):
881 """Add a device configuration in SXP format or XenAPI struct format.
883 For SXP, it could be either:
885 [device, [vbd, [uname ...]]
887 or:
889 [vbd, [uname ..]]
891 @type cfg_sxp: list of lists (parsed sxp object)
892 @param cfg_sxp: SXP configuration object
893 @type cfg_xenapi: dict
894 @param cfg_xenapi: A device configuration from Xen API (eg. vbd,vif)
895 @param target: write device information to
896 @type target: None or a dictionary
897 @rtype: string
898 @return: Assigned UUID of the device.
899 """
900 if target == None:
901 target = self
903 if dev_type not in XendDevices.valid_devices() and \
904 dev_type not in XendDevices.pseudo_devices():
905 raise XendConfigError("XendConfig: %s not a valid device type" %
906 dev_type)
908 if cfg_sxp == None and cfg_xenapi == None:
909 raise XendConfigError("XendConfig: device_add requires some "
910 "config.")
912 if cfg_sxp:
913 log.debug("XendConfig.device_add: %s" % str(cfg_sxp))
914 if cfg_xenapi:
915 log.debug("XendConfig.device_add: %s" % str(cfg_xenapi))
917 if cfg_sxp:
918 if sxp.child0(cfg_sxp) == 'device':
919 config = sxp.child0(cfg_sxp)
920 else:
921 config = cfg_sxp
923 dev_type = sxp.name(config)
924 dev_info = {}
926 try:
927 for opt, val in config[1:]:
928 dev_info[opt] = val
929 except ValueError:
930 pass # SXP has no options for this device
932 if dev_type == 'vbd':
933 if dev_info.get('dev', '').startswith('ioemu:'):
934 dev_info['driver'] = 'ioemu'
935 else:
936 dev_info['driver'] = 'paravirtualised'
938 # create uuid if it doesn't exist
939 dev_uuid = dev_info.get('uuid', uuid.createString())
940 dev_info['uuid'] = dev_uuid
942 # store dev references by uuid for certain device types
943 target['devices'][dev_uuid] = (dev_type, dev_info)
944 if dev_type in ('vif', 'vbd', 'vtpm'):
945 param = '%s_refs' % dev_type
946 if param not in target:
947 target[param] = []
948 if dev_uuid not in target[param]:
949 target[param].append(dev_uuid)
950 elif dev_type in ('tap',):
951 if 'vbd_refs' not in target:
952 target['vbd_refs'] = []
953 if dev_uuid not in target['vbd_refs']:
954 target['vbd_refs'].append(dev_uuid)
956 return dev_uuid
958 if cfg_xenapi:
959 dev_info = {}
960 if dev_type == 'vif':
961 if cfg_xenapi.get('MAC'): # don't add if blank
962 dev_info['mac'] = cfg_xenapi.get('MAC')
963 # vifname is the name on the guest, not dom0
964 # TODO: we don't have the ability to find that out or
965 # change it from dom0
966 #if cfg_xenapi.get('device'): # don't add if blank
967 # dev_info['vifname'] = cfg_xenapi.get('device')
968 if cfg_xenapi.get('type'):
969 dev_info['type'] = cfg_xenapi.get('type')
971 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
972 dev_info['uuid'] = dev_uuid
973 self['devices'][dev_uuid] = (dev_type, dev_info)
974 self['vif_refs'].append(dev_uuid)
975 return dev_uuid
977 elif dev_type in ('vbd', 'tap'):
978 if dev_type == 'vbd':
979 dev_info['uname'] = cfg_xenapi.get('image', '')
980 dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
981 elif dev_type == 'tap':
982 dev_info['uname'] = 'tap:qcow:%s' % cfg_xenapi.get('image')
983 dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
985 dev_info['driver'] = cfg_xenapi.get('driver')
986 dev_info['VDI'] = cfg_xenapi.get('VDI', '')
988 if cfg_xenapi.get('mode') == 'RW':
989 dev_info['mode'] = 'w'
990 else:
991 dev_info['mode'] = 'r'
993 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
994 dev_info['uuid'] = dev_uuid
995 self['devices'][dev_uuid] = (dev_type, dev_info)
996 self['vbd_refs'].append(dev_uuid)
997 return dev_uuid
999 elif dev_type in ('vtpm'):
1000 if cfg_xenapi.get('type'):
1001 dev_info['type'] = cfg_xenapi.get('type')
1003 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
1004 dev_info['uuid'] = dev_uuid
1005 self['devices'][dev_uuid] = (dev_type, dev_info)
1006 self['vtpm_refs'].append(dev_uuid)
1007 return dev_uuid
1009 return ''
1011 def device_update(self, dev_uuid, cfg_sxp):
1012 """Update an existing device with the new configuration.
1014 @rtype: boolean
1015 @return: Returns True if succesfully found and updated a device conf
1016 """
1017 if dev_uuid in self['devices']:
1018 config = sxp.child0(cfg_sxp)
1019 dev_type = sxp.name(config)
1020 dev_info = {}
1022 try:
1023 for opt, val in config[1:]:
1024 self['devices'][opt] = val
1025 except ValueError:
1026 pass # SXP has no options for this device
1028 return True
1030 return False
1033 def device_sxpr(self, dev_uuid = None, dev_type = None, dev_info = None):
1034 """Get Device SXPR by either giving the device UUID or (type, config).
1036 @rtype: list of lists
1037 @return: device config sxpr
1038 """
1039 sxpr = []
1040 if dev_uuid != None and dev_uuid in self['devices']:
1041 dev_type, dev_info = self['devices'][dev_uuid]
1043 if dev_type == None or dev_info == None:
1044 raise XendConfigError("Required either UUID or device type and "
1045 "configuration dictionary.")
1047 sxpr.append(dev_type)
1048 config = [(opt, val) for opt, val in dev_info.items()]
1049 sxpr += config
1051 return sxpr
1053 def all_devices_sxpr(self):
1054 """Returns the SXPR for all devices in the current configuration."""
1055 sxprs = []
1056 pci_devs = []
1058 if 'devices' not in self:
1059 return sxprs
1061 for dev_type, dev_info in self['devices'].values():
1062 if dev_type == 'pci': # special case for pci devices
1063 sxpr = [['uuid', dev_info['uuid']]]
1064 for pci_dev_info in dev_info['devs']:
1065 pci_dev_sxpr = ['dev']
1066 for opt, val in pci_dev_info.items():
1067 pci_dev_sxpr.append([opt, val])
1068 sxpr.append(pci_dev_sxpr)
1069 sxprs.append((dev_type, sxpr))
1070 else:
1071 sxpr = self.device_sxpr(dev_type = dev_type,
1072 dev_info = dev_info)
1073 sxprs.append((dev_type, sxpr))
1075 return sxprs
1077 def image_sxpr(self):
1078 """Returns a backwards compatible image SXP expression that is
1079 used in xenstore's /vm/<uuid>/image value and xm list."""
1080 image = [self['image'].get('type', 'linux')]
1081 if self.has_key('PV_kernel'):
1082 image.append(['kernel', self['PV_kernel']])
1083 if self.has_key('PV_ramdisk') and self['PV_ramdisk']:
1084 image.append(['ramdisk', self['PV_ramdisk']])
1085 if self.has_key('PV_args') and self['PV_args']:
1086 image.append(['args', self['PV_args']])
1088 for arg, conv in LEGACY_IMAGE_CFG:
1089 if self['image'].has_key(arg):
1090 image.append([arg, self['image'][arg]])
1092 if 'hvm' in self['image']:
1093 for arg, conv in LEGACY_IMAGE_HVM_CFG:
1094 if self['image']['hvm'].get(arg):
1095 image.append([arg, self['image']['hvm'][arg]])
1097 if 'hvm' in self['image'] and 'devices' in self['image']['hvm']:
1098 for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
1099 if self['image']['hvm']['devices'].get(arg):
1100 image.append([arg,
1101 self['image']['hvm']['devices'][arg]])
1103 return image
1105 def update_with_image_sxp(self, image_sxp, bootloader = False):
1106 # Convert Legacy "image" config to Xen API PV_*
1107 # configuration
1108 log.debug("update_with_image_sxp(%s)" % scrub_password(image_sxp))
1110 kernel_args = sxp.child_value(image_sxp, 'args', '')
1112 # attempt to extract extra arguments from SXP config
1113 arg_ip = sxp.child_value(image_sxp, 'ip')
1114 if arg_ip and not re.search(r'ip=[^ ]+', kernel_args):
1115 kernel_args += ' ip=%s' % arg_ip
1116 arg_root = sxp.child_value(image_sxp, 'root')
1117 if arg_root and not re.search(r'root=', kernel_args):
1118 kernel_args += ' root=%s' % arg_root
1120 if bootloader:
1121 self['_temp_using_bootloader'] = '1'
1122 self['_temp_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1123 self['_temp_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1124 self['_temp_args'] = kernel_args
1125 else:
1126 self['PV_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1127 self['PV_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1128 self['PV_args'] = kernel_args
1130 # Store image SXP in python dictionary format
1131 image = {}
1132 image['type'] = sxp.name(image_sxp)
1133 for arg, conv in LEGACY_IMAGE_CFG:
1134 val = sxp.child_value(image_sxp, arg, None)
1135 if val != None:
1136 image[arg] = conv(val)
1138 image_hvm = {}
1139 for arg, conv in LEGACY_IMAGE_HVM_CFG:
1140 val = sxp.child_value(image_sxp, arg, None)
1141 if val != None:
1142 image_hvm[arg] = conv(val)
1144 image_hvm_devices = {}
1145 for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
1146 val = sxp.child_value(image_sxp, arg, None)
1147 if val != None:
1148 image_hvm_devices[arg] = conv(val)
1150 if image_hvm or image_hvm_devices:
1151 image['hvm'] = image_hvm
1152 image['hvm']['devices'] = image_hvm_devices
1154 self['image'] = image
1156 for apikey, imgkey in XENAPI_HVM_CFG.items():
1157 val = sxp.child_value(image_sxp, imgkey, None)
1158 if val != None:
1159 type_conv = XENAPI_CFG_TYPES[apikey]
1160 if callable(conv):
1161 self[apikey] = type_conv(val)
1162 else:
1163 self[apikey] = val
1167 # debugging
1170 if __name__ == "__main__":
1171 pass