ia64/xen-unstable

view tools/python/xen/xend/XendConfig.py @ 13609:959e79bfe913

Fix device reordering that occurs when the config gets read into dictionaries.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author Ewan Mellor <ewan@xensource.com>
date Wed Jan 24 16:48:41 2007 +0000 (2007-01-24)
parents 665be23d7fe9
children 82c306ad212e
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
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_enable_audio': 'soundhw',
129 'platform_keymap' : 'keymap',
130 }
132 # List of XendConfig configuration keys that have no direct equivalent
133 # in the old world.
135 XENAPI_CFG_TYPES = {
136 'uuid': str,
137 'power_state': str,
138 'name_label': str,
139 'name_description': str,
140 'user_version': str,
141 'is_a_template': bool0,
142 'resident_on': str,
143 'memory_static_min': int,
144 'memory_static_max': int,
145 'memory_dynamic_min': int,
146 'memory_dynamic_max': int,
147 'memory_actual': int,
148 'cpus': list,
149 'vcpus_policy': str,
150 'vcpus_params': str,
151 'vcpus_number': int,
152 'vcpus_features_required': list,
153 'vcpus_features_can_use': list,
154 'vcpus_features_force_on': list,
155 'vcpus_features_force_off': list,
156 'actions_after_shutdown': str,
157 'actions_after_reboot': str,
158 'actions_after_suspend': str,
159 'actions_after_crash': str,
160 'tpm_instance': int,
161 'tpm_backend': int,
162 'PV_bootloader': str,
163 'PV_kernel': str,
164 'PV_ramdisk': str,
165 'PV_args': str,
166 'PV_bootloader_args': str,
167 'HVM_boot': str,
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 ]
257 LEGACY_IMAGE_HVM_CFG = [
258 ('device_model', str),
259 ('display', str),
260 ('xauthority', str),
261 ('vncconsole', int),
262 ('pae', int),
263 ('apic', int),
264 ]
266 LEGACY_IMAGE_HVM_DEVICES_CFG = [
267 ('acpi', int),
268 ('boot', str),
269 ('fda', str),
270 ('fdb', str),
271 ('isa', str),
272 ('keymap', str),
273 ('localtime', str),
274 ('serial', str),
275 ('stdvga', int),
276 ('soundhw', str),
277 ('usb', str),
278 ('usbdevice', str),
279 ('vcpus', int),
280 ]
282 ##
283 ## Config Choices
284 ##
286 CONFIG_RESTART_MODES = ('restart', 'destroy', 'preserve', 'rename-restart')
287 CONFIG_OLD_DOM_STATES = ('running', 'blocked', 'paused', 'shutdown',
288 'crashed', 'dying')
290 class XendConfigError(VmError):
291 def __str__(self):
292 return 'Invalid Configuration: %s' % str(self.value)
294 ##
295 ## XendConfig Class (an extended dictionary)
296 ##
298 class XendConfig(dict):
299 """ The new Xend VM Configuration.
301 Stores the configuration in xenapi compatible format but retains
302 import and export functions for SXP.
303 """
304 def __init__(self, filename = None, sxp_obj = None,
305 xapi = None, dominfo = None):
307 dict.__init__(self)
308 self.update(self._defaults())
310 if filename:
311 try:
312 sxp_obj = sxp.parse(open(filename,'r'))
313 sxp_obj = sxp_obj[0]
314 except IOError, e:
315 raise XendConfigError("Unable to read file: %s" % filename)
317 if sxp_obj:
318 self._sxp_to_xapi(sxp_obj)
319 self._sxp_to_xapi_unsupported(sxp_obj)
320 elif xapi:
321 self.update_with_xenapi_config(xapi)
322 self._add_xapi_unsupported(xapi)
323 elif dominfo:
324 # output from xc.domain_getinfo
325 self._dominfo_to_xapi(dominfo)
327 log.debug('XendConfig.init: %s' % scrub_password(self))
329 # validators go here
330 self.validate()
332 """ In time, we should enable this type checking addition. It is great
333 also for tracking bugs and unintended writes to XendDomainInfo.info
334 def __setitem__(self, key, value):
335 type_conv = XENAPI_CFG_TYPES.get(key)
336 if callable(type_conv):
337 try:
338 dict.__setitem__(self, key, type_conv(value))
339 except (ValueError, TypeError):
340 raise XendConfigError("Wrong type for configuration value " +
341 "%s. Expected %s" %
342 (key, type_conv.__name__))
343 else:
344 dict.__setitem__(self, key, value)
345 """
347 def _defaults(self):
348 defaults = {
349 'uuid': uuid.createString(),
350 'name_label': 'Domain-Unnamed',
351 'actions_after_shutdown': 'destroy',
352 'actions_after_reboot': 'restart',
353 'actions_after_crash': 'restart',
354 'actions_after_suspend': '',
355 'features': '',
356 'PV_bootloader': '',
357 'PV_kernel': '',
358 'PV_ramdisk': '',
359 'PV_args': '',
360 'PV_bootloader_args': '',
361 'HVM_boot': '',
362 'memory_static_min': 0,
363 'memory_dynamic_min': 0,
364 'shadow_memory': 0,
365 'memory_static_max': 0,
366 'memory_dynamic_max': 0,
367 'memory_actual': 0,
368 'devices': {},
369 'image': {},
370 'security': None,
371 'on_xend_start': 'ignore',
372 'on_xend_stop': 'ignore',
373 'cpus': [],
374 'cpu_weight': 256,
375 'cpu_cap': 0,
376 'vcpus_number': 1,
377 'console_refs': [],
378 'vif_refs': [],
379 'vbd_refs': [],
380 'vtpm_refs': [],
381 }
383 defaults['name_label'] = 'Domain-' + defaults['uuid']
384 return defaults
386 def _memory_sanity_check(self):
387 if self['memory_static_min'] == 0:
388 self['memory_static_min'] = self['memory_dynamic_min']
390 # If the static max is not set, let's set it to dynamic max.
391 # If the static max is smaller than static min, then fix it!
392 self['memory_static_max'] = max(self['memory_static_max'],
393 self['memory_dynamic_max'],
394 self['memory_static_min'])
396 for mem_type in ('memory_static_min', 'memory_static_max'):
397 if self[mem_type] <= 0:
398 raise XendConfigError('Memory value too low for %s: %d' %
399 (mem_type, self[mem_type]))
401 def _actions_sanity_check(self):
402 for event in ['shutdown', 'reboot', 'crash']:
403 if self['actions_after_' + event] not in CONFIG_RESTART_MODES:
404 raise XendConfigError('Invalid event handling mode: ' +
405 event)
407 def _vcpus_sanity_check(self):
408 if 'vcpus_number' in self and 'vcpu_avail' not in self:
409 self['vcpu_avail'] = (1 << self['vcpus_number']) - 1
411 def _uuid_sanity_check(self):
412 """Make sure UUID is in proper string format with hyphens."""
413 self['uuid'] = uuid.toString(uuid.fromString(self['uuid']))
415 def validate(self):
416 self._memory_sanity_check()
417 self._actions_sanity_check()
418 self._vcpus_sanity_check()
419 self._uuid_sanity_check()
421 def _dominfo_to_xapi(self, dominfo):
422 self['domid'] = dominfo['domid']
423 self['online_vcpus'] = dominfo['online_vcpus']
424 self['vcpus_number'] = dominfo['max_vcpu_id'] + 1
425 self['memory_dynamic_min'] = (dominfo['mem_kb'] + 1023)/1024
426 self['memory_dynamic_max'] = (dominfo['maxmem_kb'] + 1023)/1024
427 self['cpu_time'] = dominfo['cpu_time']/1e9
428 # TODO: i don't know what the security stuff expects here
429 if dominfo.get('ssidref'):
430 self['security'] = [['ssidref', dominfo['ssidref']]]
431 self['shutdown_reason'] = dominfo['shutdown_reason']
433 # parse state into Xen API states
434 self['running'] = dominfo['running']
435 self['crashed'] = dominfo['crashed']
436 self['dying'] = dominfo['dying']
437 self['shutdown'] = dominfo['shutdown']
438 self['paused'] = dominfo['paused']
439 self['blocked'] = dominfo['blocked']
441 if 'name' in dominfo:
442 self['name_label'] = dominfo['name']
444 if 'handle' in dominfo:
445 self['uuid'] = uuid.toString(dominfo['handle'])
447 def _parse_sxp(self, sxp_cfg):
448 """ Populate this XendConfig using the parsed SXP.
450 @param sxp_cfg: Parsed SXP Configuration
451 @type sxp_cfg: list of lists
452 @rtype: dictionary
453 @return: A dictionary containing the parsed options of the SXP.
454 """
455 cfg = {}
457 for key, typ in XENAPI_CFG_TYPES.items():
458 val = sxp.child_value(sxp_cfg, key)
459 if val is not None:
460 cfg[key] = typ(val)
462 # Convert deprecated options to current equivalents.
464 restart = sxp.child_value(sxp_cfg, 'restart')
465 if restart:
466 if restart == 'onreboot':
467 cfg['on_poweroff'] = 'destroy'
468 cfg['on_reboot'] = 'restart'
469 cfg['on_crash'] = 'destroy'
470 elif restart == 'always':
471 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
472 cfg[opt] = 'restart'
473 elif restart == 'never':
474 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
475 cfg[opt] = 'never'
476 else:
477 log.warn('Ignoring unrecognised value for deprecated option:'
478 'restart = \'%s\'', restart)
480 # Only extract options we know about.
481 extract_keys = LEGACY_UNSUPPORTED_BY_XENAPI_CFG
482 extract_keys += XENAPI_CFG_TO_LEGACY_CFG.values()
484 for key in extract_keys:
485 val = sxp.child_value(sxp_cfg, key)
486 if val != None:
487 try:
488 cfg[key] = LEGACY_CFG_TYPES[key](val)
489 except KeyError:
490 cfg[key] = val
491 except (TypeError, ValueError), e:
492 log.warn("Unable to parse key %s: %s: %s" %
493 (key, str(val), e))
495 # Parsing the device SXP's. In most cases, the SXP looks
496 # like this:
497 #
498 # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]]
499 #
500 # However, for PCI devices it looks like this:
501 #
502 # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1]]]]
503 #
504 # It seems the reasoning for this difference is because
505 # pciif.py needs all the PCI device configurations at
506 # the same time when creating the devices.
507 #
508 # To further complicate matters, Xen 2.0 configuration format
509 # uses the following for pci device configuration:
510 #
511 # [device, [pci, [domain, 0], [bus, 0], [dev, 1], [func, 2]]]
512 #
513 # Hence we deal with pci device configurations outside of
514 # the regular device parsing.
516 cfg['devices'] = {}
517 for dev in sxp.children(sxp_cfg, 'device'):
518 config = sxp.child0(dev)
519 dev_type = sxp.name(config)
520 dev_info = {}
522 if dev_type == 'pci':
523 pci_devs_uuid = sxp.child_value(config, 'uuid',
524 uuid.createString())
525 pci_devs = []
526 for pci_dev in sxp.children(config, 'dev'):
527 pci_dev_info = {}
528 for opt_val in pci_dev[1:]:
529 try:
530 opt, val = opt_val
531 pci_dev_info[opt] = val
532 except TypeError:
533 pass
534 pci_devs.append(pci_dev_info)
536 cfg['devices'][pci_devs_uuid] = (dev_type,
537 {'devs': pci_devs,
538 'uuid': pci_devs_uuid})
540 log.debug("XendConfig: reading device: %s" % pci_devs)
541 else:
542 self.device_add(dev_type, cfg_sxp = config, target = cfg)
543 log.debug("XendConfig: reading device: %s" % scrub_password(dev_info))
545 # Extract missing data from configuration entries
546 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
547 if image_sxp:
548 image_vcpus = sxp.child_value(image_sxp, 'vcpus')
549 if image_vcpus != None:
550 try:
551 if 'vcpus_number' not in cfg:
552 cfg['vcpus_number'] = int(image_vcpus)
553 elif cfg['vcpus_number'] != int(image_vcpus):
554 cfg['vcpus_number'] = int(image_vcpus)
555 log.warn('Overriding vcpus from %d to %d using image'
556 'vcpus value.', cfg['vcpus_number'])
557 except ValueError, e:
558 raise XendConfigError('integer expeceted: %s: %s' %
559 image_sxp, e)
561 # Deprecated cpu configuration
562 if 'cpu' in cfg:
563 if 'cpus' in cfg:
564 cfg['cpus'] = "%s,%s" % (str(cfg['cpu']), cfg['cpus'])
565 else:
566 cfg['cpus'] = str(cfg['cpu'])
568 # convert 'cpus' string to list of ints
569 # 'cpus' supports a list of ranges (0-3), seperated by
570 # commas, and negation, (^1).
571 # Precedence is settled by order of the string:
572 # "0-3,^1" -> [0,2,3]
573 # "0-3,^1,1" -> [0,1,2,3]
574 try:
575 if 'cpus' in cfg:
576 cpus = []
577 for c in cfg['cpus'].split(','):
578 if c.find('-') != -1:
579 (x, y) = c.split('-')
580 for i in range(int(x), int(y)+1):
581 cpus.append(int(i))
582 else:
583 # remove this element from the list
584 if c[0] == '^':
585 cpus = [x for x in cpus if x != int(c[1:])]
586 else:
587 cpus.append(int(c))
589 cfg['cpus'] = cpus
590 except ValueError, e:
591 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
593 if 'security' in cfg and isinstance(cfg['security'], str):
594 cfg['security'] = sxp.from_string(cfg['security'])
596 old_state = sxp.child_value(sxp_cfg, 'state')
597 if old_state:
598 for i in range(len(CONFIG_OLD_DOM_STATES)):
599 cfg[CONFIG_OLD_DOM_STATES[i]] = int(old_state[i] != '-')
601 return cfg
604 def _sxp_to_xapi(self, sxp_cfg):
605 """Read in an SXP Configuration object and
606 populate at much of the Xen API with valid values.
607 """
608 log.debug('_sxp_to_xapi(%s)' % scrub_password(sxp_cfg))
610 cfg = self._parse_sxp(sxp_cfg)
612 for key, typ in XENAPI_CFG_TYPES.items():
613 val = cfg.get(key)
614 if val is not None:
615 self[key] = typ(val)
617 # Convert parameters that can be directly mapped from
618 # the Legacy Config to Xen API Config
620 for apikey, cfgkey in XENAPI_CFG_TO_LEGACY_CFG.items():
621 try:
622 type_conv = XENAPI_CFG_TYPES.get(apikey)
623 if callable(type_conv):
624 self[apikey] = type_conv(cfg[cfgkey])
625 else:
626 log.warn("Unconverted key: " + apikey)
627 self[apikey] = cfg[cfgkey]
628 except KeyError:
629 pass
631 def update_with(n, o):
632 if not self.get(n):
633 self[n] = cfg.get(o, '')
635 update_with('PV_bootloader', 'bootloader')
636 update_with('PV_bootloader_args', 'bootloader_args')
638 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
639 if image_sxp:
640 self.update_with_image_sxp(image_sxp)
642 # Convert Legacy HVM parameters to Xen API configuration
643 self['platform_std_vga'] = bool0(cfg.get('stdvga', 0))
644 self['platform_serial'] = str(cfg.get('serial', ''))
645 self['platform_localtime'] = bool0(cfg.get('localtime', 0))
646 self['platform_enable_audio'] = bool0(cfg.get('soundhw', 0))
648 # make sure a sane maximum is set
649 if self['memory_static_max'] <= 0:
650 self['memory_static_max'] = self['memory_static_min']
652 self['memory_dynamic_max'] = self['memory_static_max']
653 self['memory_dynamic_min'] = self['memory_static_min']
655 # set device references in the configuration
656 self['devices'] = cfg.get('devices', {})
657 self['console_refs'] = cfg.get('console_refs', [])
658 self['vif_refs'] = cfg.get('vif_refs', [])
659 self['vbd_refs'] = cfg.get('vbd_refs', [])
660 self['vtpm_refs'] = cfg.get('vtpm_refs', [])
663 def _sxp_to_xapi_unsupported(self, sxp_cfg):
664 """Read in an SXP configuration object and populate
665 values are that not related directly supported in
666 the Xen API.
667 """
669 log.debug('_sxp_to_xapi_unsupported(%s)' % scrub_password(sxp_cfg))
671 # Parse and convert parameters used to configure
672 # the image (as well as HVM images)
673 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
674 if image_sxp:
675 image = {}
676 image['type'] = sxp.name(image_sxp)
677 for arg, conv in LEGACY_IMAGE_CFG:
678 val = sxp.child_value(image_sxp, arg, None)
679 if val != None:
680 image[arg] = conv(val)
682 image_hvm = {}
683 for arg, conv in LEGACY_IMAGE_HVM_CFG:
684 val = sxp.child_value(image_sxp, arg, None)
685 if val != None:
686 image_hvm[arg] = conv(val)
688 image_hvm_devices = {}
689 for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
690 val = sxp.child_value(image_sxp, arg, None)
691 if val != None:
692 image_hvm_devices[arg] = conv(val)
694 if image_hvm or image_hvm_devices:
695 image['hvm'] = image_hvm
696 image['hvm']['devices'] = image_hvm_devices
698 self['image'] = image
700 for apikey, imgkey in XENAPI_HVM_CFG.items():
701 val = sxp.child_value(image_sxp, imgkey, None)
702 if val != None:
703 self[apikey] = val
705 # extract backend value
707 backend = []
708 for c in sxp.children(sxp_cfg, 'backend'):
709 backend.append(sxp.name(sxp.child0(c)))
710 if backend:
711 self['backend'] = backend
713 # Parse and convert other Non Xen API parameters.
714 def _set_cfg_if_exists(sxp_arg):
715 val = sxp.child_value(sxp_cfg, sxp_arg)
716 if val != None:
717 if LEGACY_CFG_TYPES.get(sxp_arg):
718 self[sxp_arg] = LEGACY_CFG_TYPES[sxp_arg](val)
719 else:
720 self[sxp_arg] = val
722 _set_cfg_if_exists('shadow_memory')
723 _set_cfg_if_exists('security')
724 _set_cfg_if_exists('features')
725 _set_cfg_if_exists('on_xend_stop')
726 _set_cfg_if_exists('on_xend_start')
727 _set_cfg_if_exists('vcpu_avail')
728 _set_cfg_if_exists('cpu_weight')
729 _set_cfg_if_exists('cpu_cap')
731 # Parse and store runtime configuration
732 _set_cfg_if_exists('start_time')
733 _set_cfg_if_exists('cpu_time')
734 _set_cfg_if_exists('shutdown_reason')
735 _set_cfg_if_exists('up_time')
736 _set_cfg_if_exists('status') # TODO, deprecated
738 def _add_xapi_unsupported(self, xapi_dict):
739 """Updates the configuration object with entries that are not
740 officially supported by the Xen API but is required for
741 the rest of Xend to function.
742 """
744 # populate image
745 if 'image' in xapi_dict:
746 self['image'].update(xapi_dict['image'])
747 else:
748 hvm = self['HVM_boot'] != ''
749 self['image']['type'] = hvm and 'hvm' or 'linux'
750 if hvm:
751 self['image']['hvm'] = {}
752 for xapi, cfgapi in XENAPI_HVM_CFG.items():
753 self['image']['hvm'][cfgapi] = self[xapi]
756 def _get_old_state_string(self):
757 """Returns the old xm state string.
758 @rtype: string
759 @return: old state string
760 """
761 state_string = ''
762 for state_name in CONFIG_OLD_DOM_STATES:
763 on_off = self.get(state_name, 0)
764 if on_off:
765 state_string += state_name[0]
766 else:
767 state_string += '-'
769 return state_string
772 def update_config(self, dominfo):
773 """Update configuration with the output from xc.domain_getinfo().
775 @param dominfo: Domain information via xc.domain_getinfo()
776 @type dominfo: dict
777 """
778 self._dominfo_to_xapi(dominfo)
779 self.validate()
781 def update_with_xenapi_config(self, xapi):
782 """Update configuration with a Xen API VM struct
784 @param xapi: Xen API VM Struct
785 @type xapi: dict
786 """
788 log.debug('update_with_xenapi_config: %s' % scrub_password(xapi))
790 for key, val in xapi.items():
791 type_conv = XENAPI_CFG_TYPES.get(key)
792 if type_conv is None:
793 key = key.lower()
794 type_conv = XENAPI_CFG_TYPES.get(key)
795 if callable(type_conv):
796 self[key] = type_conv(val)
797 else:
798 self[key] = val
800 self.validate()
802 def to_sxp(self, domain = None, ignore_devices = False, ignore = [],
803 legacy_only = True):
804 """ Get SXP representation of this config object.
806 Incompat: removed store_mfn, console_mfn
808 @keyword domain: (optional) XendDomainInfo to get extra information
809 from such as domid and running devices.
810 @type domain: XendDomainInfo
811 @keyword ignore: (optional) list of 'keys' that we do not want
812 to export.
813 @type ignore: list of strings
814 @rtype: list of list (SXP representation)
815 """
816 sxpr = ['domain']
818 # TODO: domid/dom is the same thing but called differently
819 # depending if it is from xenstore or sxpr.
821 if domain.getDomid() is not None:
822 sxpr.append(['domid', domain.getDomid()])
824 if not legacy_only:
825 for name in XENAPI_CFG_TYPES.keys():
826 if name in self and self[name] not in (None, []):
827 sxpr.append([name, str(self[name])])
829 for xenapi, legacy in XENAPI_CFG_TO_LEGACY_CFG.items():
830 if self.has_key(xenapi) and self[xenapi] not in (None, []):
831 if type(self[xenapi]) == bool:
832 # convert booleans to ints before making an sxp item
833 sxpr.append([legacy, int(self[xenapi])])
834 else:
835 sxpr.append([legacy, self[xenapi]])
837 for legacy in LEGACY_UNSUPPORTED_BY_XENAPI_CFG:
838 if legacy in ('domid', 'uuid'): # skip these
839 continue
840 if self.has_key(legacy) and self[legacy] not in (None, []):
841 sxpr.append([legacy, self[legacy]])
843 if 'image' in self and self['image']:
844 sxpr.append(['image', self.image_sxpr()])
846 sxpr.append(['status', domain.state])
847 sxpr.append(['memory_dynamic_min', self.get('memory_dynamic_min')])
848 sxpr.append(['memory_dynamic_max', self.get('memory_dynamic_max')])
850 if domain.getDomid() is not None:
851 sxpr.append(['state', self._get_old_state_string()])
853 if domain:
854 if domain.store_mfn:
855 sxpr.append(['store_mfn', domain.store_mfn])
856 if domain.console_mfn:
857 sxpr.append(['console_mfn', domain.console_mfn])
860 # Marshall devices (running or from configuration)
861 if not ignore_devices:
862 for cls in XendDevices.valid_devices():
863 found = False
865 # figure if there is a dev controller is valid and running
866 if domain and domain.getDomid() != None:
867 try:
868 controller = domain.getDeviceController(cls)
869 configs = controller.configurations()
870 for config in configs:
871 sxpr.append(['device', config])
873 found = True
874 except:
875 log.exception("dumping sxp from device controllers")
876 pass
878 # if we didn't find that device, check the existing config
879 # for a device in the same class
880 if not found:
881 for dev_type, dev_info in self.all_devices_sxpr():
882 if dev_type == cls:
883 sxpr.append(['device', dev_info])
885 return sxpr
887 def device_add(self, dev_type, cfg_sxp = None, cfg_xenapi = None,
888 target = None):
889 """Add a device configuration in SXP format or XenAPI struct format.
891 For SXP, it could be either:
893 [device, [vbd, [uname ...]]
895 or:
897 [vbd, [uname ..]]
899 @type cfg_sxp: list of lists (parsed sxp object)
900 @param cfg_sxp: SXP configuration object
901 @type cfg_xenapi: dict
902 @param cfg_xenapi: A device configuration from Xen API (eg. vbd,vif)
903 @param target: write device information to
904 @type target: None or a dictionary
905 @rtype: string
906 @return: Assigned UUID of the device.
907 """
908 if target == None:
909 target = self
911 if dev_type not in XendDevices.valid_devices() and \
912 dev_type not in XendDevices.pseudo_devices():
913 raise XendConfigError("XendConfig: %s not a valid device type" %
914 dev_type)
916 if cfg_sxp == None and cfg_xenapi == None:
917 raise XendConfigError("XendConfig: device_add requires some "
918 "config.")
920 if cfg_sxp:
921 log.debug("XendConfig.device_add: %s" % str(cfg_sxp))
922 if cfg_xenapi:
923 log.debug("XendConfig.device_add: %s" % str(cfg_xenapi))
925 if cfg_sxp:
926 if sxp.child0(cfg_sxp) == 'device':
927 config = sxp.child0(cfg_sxp)
928 else:
929 config = cfg_sxp
931 dev_type = sxp.name(config)
932 dev_info = {}
934 for opt_val in config[1:]:
935 try:
936 opt, val = opt_val
937 dev_info[opt] = val
938 except (TypeError, ValueError): # unpack error
939 pass
941 if dev_type == 'vbd':
942 if dev_info.get('dev', '').startswith('ioemu:'):
943 dev_info['driver'] = 'ioemu'
944 else:
945 dev_info['driver'] = 'paravirtualised'
947 # create uuid if it doesn't exist
948 dev_uuid = dev_info.get('uuid', uuid.createString())
949 dev_info['uuid'] = dev_uuid
951 # store dev references by uuid for certain device types
952 target['devices'][dev_uuid] = (dev_type, dev_info)
953 if dev_type in ('vif', 'vbd', 'vtpm'):
954 param = '%s_refs' % dev_type
955 if param not in target:
956 target[param] = []
957 if dev_uuid not in target[param]:
958 target[param].append(dev_uuid)
959 elif dev_type in ('tap',):
960 if 'vbd_refs' not in target:
961 target['vbd_refs'] = []
962 if dev_uuid not in target['vbd_refs']:
963 target['vbd_refs'].append(dev_uuid)
965 return dev_uuid
967 if cfg_xenapi:
968 dev_info = {}
969 if dev_type == 'vif':
970 if cfg_xenapi.get('MAC'): # don't add if blank
971 dev_info['mac'] = cfg_xenapi.get('MAC')
972 # vifname is the name on the guest, not dom0
973 # TODO: we don't have the ability to find that out or
974 # change it from dom0
975 #if cfg_xenapi.get('device'): # don't add if blank
976 # dev_info['vifname'] = cfg_xenapi.get('device')
977 if cfg_xenapi.get('type'):
978 dev_info['type'] = cfg_xenapi.get('type')
979 if cfg_xenapi.get('name'):
980 dev_info['name'] = cfg_xenapi.get('name')
982 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
983 dev_info['uuid'] = dev_uuid
984 target['devices'][dev_uuid] = (dev_type, dev_info)
985 target['vif_refs'].append(dev_uuid)
986 return dev_uuid
988 elif dev_type in ('vbd', 'tap'):
989 dev_info['type'] = cfg_xenapi.get('type', 'Disk')
990 if dev_info['type'] == 'CD':
991 old_vbd_type = 'cdrom'
992 else:
993 old_vbd_type = 'disk'
995 dev_info['uname'] = cfg_xenapi.get('image', '')
996 dev_info['dev'] = '%s:%s' % (cfg_xenapi.get('device'),
997 old_vbd_type)
998 dev_info['driver'] = cfg_xenapi.get('driver')
999 dev_info['VDI'] = cfg_xenapi.get('VDI', '')
1001 if cfg_xenapi.get('mode') == 'RW':
1002 dev_info['mode'] = 'w'
1003 else:
1004 dev_info['mode'] = 'r'
1006 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
1007 dev_info['uuid'] = dev_uuid
1008 target['devices'][dev_uuid] = (dev_type, dev_info)
1009 target['vbd_refs'].append(dev_uuid)
1010 return dev_uuid
1012 elif dev_type == 'vtpm':
1013 if cfg_xenapi.get('type'):
1014 dev_info['type'] = cfg_xenapi.get('type')
1016 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
1017 dev_info['uuid'] = dev_uuid
1018 target['devices'][dev_uuid] = (dev_type, dev_info)
1019 target['vtpm_refs'].append(dev_uuid)
1020 return dev_uuid
1022 return ''
1024 def device_update(self, dev_uuid, cfg_sxp):
1025 """Update an existing device with the new configuration.
1027 @rtype: boolean
1028 @return: Returns True if succesfully found and updated a device conf
1029 """
1030 if dev_uuid in self['devices']:
1031 if sxp.child0(cfg_sxp) == 'device':
1032 config = sxp.child0(cfg_sxp)
1033 else:
1034 config = cfg_sxp
1036 for opt_val in config[1:]:
1037 try:
1038 opt, val = opt_val
1039 self['devices'][dev_uuid][opt] = val
1040 except (TypeError, ValueError):
1041 pass # no value for this config option
1043 return True
1045 return False
1048 def device_sxpr(self, dev_uuid = None, dev_type = None, dev_info = None):
1049 """Get Device SXPR by either giving the device UUID or (type, config).
1051 @rtype: list of lists
1052 @return: device config sxpr
1053 """
1054 sxpr = []
1055 if dev_uuid != None and dev_uuid in self['devices']:
1056 dev_type, dev_info = self['devices'][dev_uuid]
1058 if dev_type == None or dev_info == None:
1059 raise XendConfigError("Required either UUID or device type and "
1060 "configuration dictionary.")
1062 sxpr.append(dev_type)
1063 config = [(opt, val) for opt, val in dev_info.items()]
1064 sxpr += config
1066 return sxpr
1068 def ordered_device_refs(self):
1069 result = (self['console_refs'] +
1070 self['vbd_refs'] +
1071 self['vif_refs'] +
1072 self['vtpm_refs'])
1073 result.extend([u for u in self['devices'].keys() if u not in result])
1074 return result
1076 def all_devices_sxpr(self):
1077 """Returns the SXPR for all devices in the current configuration."""
1078 sxprs = []
1079 pci_devs = []
1081 if 'devices' not in self:
1082 return sxprs
1084 ordered_refs = self.ordered_device_refs()
1085 for dev_uuid in ordered_refs:
1086 dev_type, dev_info = self['devices'][dev_uuid]
1087 if dev_type == 'pci': # special case for pci devices
1088 sxpr = [['uuid', dev_info['uuid']]]
1089 for pci_dev_info in dev_info['devs']:
1090 pci_dev_sxpr = ['dev']
1091 for opt, val in pci_dev_info.items():
1092 pci_dev_sxpr.append([opt, val])
1093 sxpr.append(pci_dev_sxpr)
1094 sxprs.append((dev_type, sxpr))
1095 else:
1096 sxpr = self.device_sxpr(dev_type = dev_type,
1097 dev_info = dev_info)
1098 sxprs.append((dev_type, sxpr))
1100 return sxprs
1102 def image_sxpr(self):
1103 """Returns a backwards compatible image SXP expression that is
1104 used in xenstore's /vm/<uuid>/image value and xm list."""
1105 image = [self['image'].get('type', 'linux')]
1106 if self.has_key('PV_kernel'):
1107 image.append(['kernel', self['PV_kernel']])
1108 if self.has_key('PV_ramdisk') and self['PV_ramdisk']:
1109 image.append(['ramdisk', self['PV_ramdisk']])
1110 if self.has_key('PV_args') and self['PV_args']:
1111 image.append(['args', self['PV_args']])
1113 for arg, conv in LEGACY_IMAGE_CFG:
1114 if self['image'].has_key(arg):
1115 image.append([arg, self['image'][arg]])
1117 if 'hvm' in self['image']:
1118 for arg, conv in LEGACY_IMAGE_HVM_CFG:
1119 if self['image']['hvm'].get(arg):
1120 image.append([arg, self['image']['hvm'][arg]])
1122 if 'hvm' in self['image'] and 'devices' in self['image']['hvm']:
1123 for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
1124 if self['image']['hvm']['devices'].get(arg):
1125 image.append([arg,
1126 self['image']['hvm']['devices'][arg]])
1128 return image
1130 def update_with_image_sxp(self, image_sxp, bootloader = False):
1131 # Convert Legacy "image" config to Xen API PV_*
1132 # configuration
1133 log.debug("update_with_image_sxp(%s)" % scrub_password(image_sxp))
1135 # user-specified args must come last: previous releases did this and
1136 # some domU kernels rely upon the ordering.
1137 kernel_args = sxp.child_value(image_sxp, 'args', '')
1139 # attempt to extract extra arguments from SXP config
1140 arg_ip = sxp.child_value(image_sxp, 'ip')
1141 if arg_ip and not re.search(r'ip=[^ ]+', kernel_args):
1142 kernel_args = 'ip=%s ' % arg_ip + kernel_args
1143 arg_root = sxp.child_value(image_sxp, 'root')
1144 if arg_root and not re.search(r'root=', kernel_args):
1145 kernel_args = 'root=%s ' % arg_root + kernel_args
1147 if bootloader:
1148 self['_temp_using_bootloader'] = '1'
1149 self['_temp_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1150 self['_temp_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1151 self['_temp_args'] = kernel_args
1152 else:
1153 self['PV_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1154 self['PV_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1155 self['PV_args'] = kernel_args
1157 # Store image SXP in python dictionary format
1158 image = {}
1159 image['type'] = sxp.name(image_sxp)
1160 for arg, conv in LEGACY_IMAGE_CFG:
1161 val = sxp.child_value(image_sxp, arg, None)
1162 if val != None:
1163 image[arg] = conv(val)
1165 image_hvm = {}
1166 for arg, conv in LEGACY_IMAGE_HVM_CFG:
1167 val = sxp.child_value(image_sxp, arg, None)
1168 if val != None:
1169 image_hvm[arg] = conv(val)
1171 image_hvm_devices = {}
1172 for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
1173 val = sxp.child_value(image_sxp, arg, None)
1174 if val != None:
1175 image_hvm_devices[arg] = conv(val)
1177 if image_hvm or image_hvm_devices:
1178 image['hvm'] = image_hvm
1179 image['hvm']['devices'] = image_hvm_devices
1181 self['image'] = image
1183 for apikey, imgkey in XENAPI_HVM_CFG.items():
1184 val = sxp.child_value(image_sxp, imgkey, None)
1185 if val != None:
1186 type_conv = XENAPI_CFG_TYPES[apikey]
1187 if callable(conv):
1188 self[apikey] = type_conv(val)
1189 else:
1190 self[apikey] = val
1194 # debugging
1197 if __name__ == "__main__":
1198 pass