ia64/xen-unstable

view tools/python/xen/xend/XendConfig.py @ 12888:a4d1f99e5a53

[XEND] Keep vif IP addresses as space separated string.

They were inconsistently represented as strings or lists making it
difficult to know the type of the IP parameter for vifs. Since both xm
and DevController represents these as space separated strings, that is
what it will be stored as.

Signed-off-by: Alastair Tse <atse@xensource.com>
author Alastair Tse <atse@xensource.com>
date Fri Dec 08 13:31:21 2006 +0000 (2006-12-08)
parents 28403de6c415
children da0849b74170
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 re
19 import time
20 import types
22 from xen.xend import sxp
23 from xen.xend import uuid
24 from xen.xend.XendError import VmError
25 from xen.xend.XendDevices import XendDevices
26 from xen.xend.XendLogging import log
27 from xen.xend.PrettyPrint import prettyprintstring
28 from xen.xend.XendConstants import DOM_STATE_HALTED
30 """
31 XendConfig API
33 XendConfig will try to mirror as closely the Xen API VM Struct
34 with extra parameters for those options that are not supported.
36 """
38 def reverse_dict(adict):
39 """Return the reverse mapping of a dictionary."""
40 return dict([(v, k) for k, v in adict.items()])
42 def bool0(v):
43 return v != '0' and bool(v)
45 # Recursively copy a data struct, scrubbing out VNC passwords.
46 # Will scrub any dict entry with a key of 'vncpasswd' or any
47 # 2-element list whose first member is 'vncpasswd'. It will
48 # also scrub a string matching '(vncpasswd XYZ)'. Everything
49 # else is no-op passthrough
50 def scrub_password(data):
51 if type(data) == dict or type(data) == XendConfig:
52 scrubbed = {}
53 for key in data.keys():
54 if key == "vncpasswd":
55 scrubbed[key] = "XXXXXXXX"
56 else:
57 scrubbed[key] = scrub_password(data[key])
58 return scrubbed
59 elif type(data) == list:
60 if len(data) == 2 and type(data[0]) == str and data[0] == 'vncpasswd':
61 return ['vncpasswd', 'XXXXXXXX']
62 else:
63 scrubbed = []
64 for entry in data:
65 scrubbed.append(scrub_password(entry))
66 return scrubbed
67 elif type(data) == tuple:
68 scrubbed = []
69 for entry in data:
70 scrubbed.append(scrub_password(entry))
71 return tuple(scrubbed)
72 elif type(data) == str:
73 return re.sub(r'\(vncpasswd\s+[^\)]+\)','(vncpasswd XXXXXX)', data)
74 else:
75 return data
78 # Mapping from XendConfig configuration keys to the old
79 # legacy configuration keys that map directly.
81 XENAPI_CFG_TO_LEGACY_CFG = {
82 'uuid': 'uuid',
83 'vcpus_number': 'vcpus',
84 'memory_static_min': 'memory',
85 'memory_static_max': 'maxmem',
86 'name_label': 'name',
87 'actions_after_shutdown': 'on_poweroff',
88 'actions_after_reboot': 'on_reboot',
89 'actions_after_crash': 'on_crash',
90 'platform_localtime': 'localtime',
91 }
93 LEGACY_CFG_TO_XENAPI_CFG = reverse_dict(XENAPI_CFG_TO_LEGACY_CFG)
95 # Mapping from XendConfig configuration keys to the old
96 # legacy configuration keys that are found in the 'image'
97 # SXP object.
98 XENAPI_HVM_CFG = {
99 'platform_std_vga': 'stdvga',
100 'platform_serial' : 'serial',
101 'platform_localtime': 'localtime',
102 'platform_enable_audio': 'soundhw',
103 'platform_keymap' : 'keymap',
104 }
106 # List of XendConfig configuration keys that have no equivalent
107 # in the old world.
109 XENAPI_CFG_TYPES = {
110 'uuid': str,
111 'power_state': str,
112 'name_label': str,
113 'name_description': str,
114 'user_version': str,
115 'is_a_template': bool0,
116 'resident_on': str,
117 'memory_static_min': int,
118 'memory_static_max': int,
119 'memory_dynamic_min': int,
120 'memory_dynamic_max': int,
121 'memory_actual': int,
122 'vcpus_policy': str,
123 'vcpus_params': str,
124 'vcpus_number': int,
125 'vcpus_features_required': list,
126 'vcpus_features_can_use': list,
127 'vcpus_features_force_on': list,
128 'vcpus_features_force_off': list,
129 'actions_after_shutdown': str,
130 'actions_after_reboot': str,
131 'actions_after_suspend': str,
132 'actions_after_crash': str,
133 'tpm_instance': int,
134 'tpm_backend': int,
135 'bios_boot': str,
136 'platform_std_vga': bool0,
137 'platform_serial': str,
138 'platform_localtime': bool0,
139 'platform_clock_offset': bool0,
140 'platform_enable_audio': bool0,
141 'platform_keymap': str,
142 'boot_method': str,
143 'builder': str,
144 'kernel_kernel': str,
145 'kernel_initrd': str,
146 'kernel_args': str,
147 'grub_cmdline': str,
148 'pci_bus': str,
149 'tools_version': dict,
150 'otherconfig': dict,
151 }
153 # List of legacy configuration keys that have no equivalent in the
154 # Xen API, but are still stored in XendConfig.
156 LEGACY_UNSUPPORTED_BY_XENAPI_CFG = [
157 # roundtripped (dynamic, unmodified)
158 'shadow_memory',
159 'security',
160 'vcpu_avail',
161 'cpu_weight',
162 'cpu_cap',
163 'bootloader',
164 'bootloader_args',
165 'features',
166 # read/write
167 'on_xend_start',
168 'on_xend_stop',
169 # read-only
170 'domid',
171 'start_time',
172 'cpu_time',
173 'online_vcpus',
174 # write-once
175 'cpu',
176 'cpus',
177 ]
179 LEGACY_CFG_TYPES = {
180 'uuid': str,
181 'name': str,
182 'vcpus': int,
183 'vcpu_avail': int,
184 'memory': int,
185 'shadow_memory': int,
186 'maxmem': int,
187 'start_time': float,
188 'cpu_cap': int,
189 'cpu_weight': int,
190 'cpu_time': float,
191 'bootloader': str,
192 'bootloader_args': str,
193 'features': str,
194 'localtime': int,
195 'name': str,
196 'on_poweroff': str,
197 'on_reboot': str,
198 'on_crash': str,
199 'on_xend_stop': str,
200 'on_xend_start': str,
201 'online_vcpus': int,
202 }
204 # Values that should be stored in xenstore's /vm/<uuid> that is used
205 # by Xend. Used in XendDomainInfo to restore running VM state from
206 # xenstore.
207 LEGACY_XENSTORE_VM_PARAMS = [
208 'uuid',
209 'name',
210 'vcpus',
211 'vcpu_avail',
212 'memory',
213 'shadow_memory',
214 'maxmem',
215 'start_time',
216 'name',
217 'on_poweroff',
218 'on_crash',
219 'on_reboot',
220 'on_xend_start',
221 'on_xend_stop',
222 ]
224 LEGACY_IMAGE_CFG = [
225 ('root', str),
226 ('ip', str),
227 ('nographic', int),
228 ('vnc', int),
229 ('sdl', int),
230 ('vncdisplay', int),
231 ('vncunused', int),
232 ('vncpasswd', str),
233 ]
235 LEGACY_IMAGE_HVM_CFG = [
236 ('device_model', str),
237 ('display', str),
238 ('xauthority', str),
239 ('vncconsole', int),
240 ('pae', int),
241 ('apic', int),
242 ]
244 LEGACY_IMAGE_HVM_DEVICES_CFG = [
245 ('acpi', int),
246 ('boot', str),
247 ('fda', str),
248 ('fdb', str),
249 ('isa', str),
250 ('keymap', str),
251 ('localtime', str),
252 ('serial', str),
253 ('stdvga', int),
254 ('soundhw', str),
255 ('usb', str),
256 ('usbdevice', str),
257 ('vcpus', int),
258 ]
260 ##
261 ## Config Choices
262 ##
264 CONFIG_RESTART_MODES = ('restart', 'destroy', 'preserve', 'rename-restart')
265 CONFIG_OLD_DOM_STATES = ('running', 'blocked', 'paused', 'shutdown',
266 'crashed', 'dying')
268 class XendConfigError(VmError):
269 def __str__(self):
270 return 'Invalid Configuration: %s' % str(self.value)
272 ##
273 ## XendConfig Class (an extended dictionary)
274 ##
276 class XendConfig(dict):
277 """ The new Xend VM Configuration.
279 Stores the configuration in xenapi compatible format but retains
280 import and export functions for SXP.
281 """
282 def __init__(self, filename = None, sxp_obj = None,
283 xapi = None, dominfo = None):
285 dict.__init__(self)
286 self.update(self._defaults())
288 if filename:
289 try:
290 sxp_obj = sxp.parse(open(filename,'r'))
291 sxp_obj = sxp_obj[0]
292 except IOError, e:
293 raise XendConfigError("Unable to read file: %s" % filename)
295 if sxp_obj:
296 self._sxp_to_xapi(sxp_obj)
297 self._sxp_to_xapi_unsupported(sxp_obj)
298 elif xapi:
299 self.update_with_xenapi_config(xapi)
300 self._add_xapi_unsupported()
301 elif dominfo:
302 # output from xc.domain_getinfo
303 self._dominfo_to_xapi(dominfo)
305 log.debug('XendConfig.init: %s' % scrub_password(self))
307 # validators go here
308 self.validate()
310 """ In time, we should enable this type checking addition. It is great
311 also for tracking bugs and unintended writes to XendDomainInfo.info
312 def __setitem__(self, key, value):
313 type_conv = XENAPI_CFG_TYPES.get(key)
314 if callable(type_conv):
315 try:
316 dict.__setitem__(self, key, type_conv(value))
317 except (ValueError, TypeError):
318 raise XendConfigError("Wrong type for configuration value " +
319 "%s. Expected %s" %
320 (key, type_conv.__name__))
321 else:
322 dict.__setitem__(self, key, value)
323 """
325 def _defaults(self):
326 defaults = {
327 'uuid': uuid.createString(),
328 'name_label': 'Domain-Unnamed',
329 'actions_after_shutdown': 'destroy',
330 'actions_after_reboot': 'restart',
331 'actions_after_crash': 'restart',
332 'actions_after_suspend': '',
333 'features': '',
334 'builder': 'linux',
335 'memory_static_min': 0,
336 'memory_dynamic_min': 0,
337 'shadow_memory': 0,
338 'memory_static_max': 0,
339 'memory_dynamic_max': 0,
340 'memory_actual': 0,
341 'boot_method': None,
342 'bootloader': None,
343 'bootloader_args': None,
344 'devices': {},
345 'image': {},
346 'security': None,
347 'on_xend_start': 'ignore',
348 'on_xend_stop': 'ignore',
349 'cpus': [],
350 'cpu_weight': 256,
351 'cpu_cap': 0,
352 'vcpus_number': 1,
353 'online_vcpus': 1,
354 'max_vcpu_id': 0,
355 'vcpu_avail': 1,
356 'vif_refs': [],
357 'vbd_refs': [],
358 'vtpm_refs': [],
359 }
361 defaults['name_label'] = 'Domain-' + defaults['uuid']
362 return defaults
364 def _memory_sanity_check(self):
365 if self['memory_static_min'] == 0:
366 self['memory_static_min'] = self['memory_dynamic_min']
368 # If the static max is not set, let's set it to dynamic max.
369 # If the static max is smaller than static min, then fix it!
370 self['memory_static_max'] = max(self['memory_static_max'],
371 self['memory_dynamic_max'],
372 self['memory_static_min'])
374 for mem_type in ('memory_static_min', 'memory_static_max'):
375 if self[mem_type] <= 0:
376 raise XendConfigError('Memory value too low for %s: %d' %
377 (mem_type, self[mem_type]))
379 def _actions_sanity_check(self):
380 for event in ['shutdown', 'reboot', 'crash']:
381 if self['actions_after_' + event] not in CONFIG_RESTART_MODES:
382 raise XendConfigError('Invalid event handling mode: ' +
383 event)
385 def _builder_sanity_check(self):
386 if self['builder'] not in ('hvm', 'linux'):
387 raise XendConfigError('Invalid builder configuration')
389 def _vcpus_sanity_check(self):
390 if self.get('vcpus_number') != None:
391 self['vcpu_avail'] = (1 << self['vcpus_number']) - 1
393 def _uuid_sanity_check(self):
394 """Make sure UUID is in proper string format with hyphens."""
395 self['uuid'] = uuid.toString(uuid.fromString(self['uuid']))
397 def validate(self):
398 self._memory_sanity_check()
399 self._actions_sanity_check()
400 self._builder_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 # First step is to convert deprecated options to
441 # current equivalents.
443 restart = sxp.child_value(sxp_cfg, 'restart')
444 if restart:
445 if restart == 'onreboot':
446 cfg['on_poweroff'] = 'destroy'
447 cfg['on_reboot'] = 'restart'
448 cfg['on_crash'] = 'destroy'
449 elif restart == 'always':
450 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
451 cfg[opt] = 'restart'
452 elif restart == 'never':
453 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
454 cfg[opt] = 'never'
455 else:
456 log.warn('Ignoring unrecognised value for deprecated option:'
457 'restart = \'%s\'', restart)
459 # Only extract options we know about.
460 extract_keys = LEGACY_UNSUPPORTED_BY_XENAPI_CFG
461 extract_keys += XENAPI_CFG_TO_LEGACY_CFG.values()
463 for key in extract_keys:
464 val = sxp.child_value(sxp_cfg, key)
465 if val != None:
466 try:
467 cfg[key] = LEGACY_CFG_TYPES[key](val)
468 except KeyError:
469 cfg[key] = val
470 except (TypeError, ValueError), e:
471 log.warn("Unable to parse key %s: %s: %s" %
472 (key, str(val), e))
474 # Parsing the device SXP's. In most cases, the SXP looks
475 # like this:
476 #
477 # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]]
478 #
479 # However, for PCI devices it looks like this:
480 #
481 # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1]]]]
482 #
483 # It seems the reasoning for this difference is because
484 # pciif.py needs all the PCI device configurations at
485 # the same time when creating the devices.
486 #
487 # To further complicate matters, Xen 2.0 configuration format
488 # uses the following for pci device configuration:
489 #
490 # [device, [pci, [domain, 0], [bus, 0], [dev, 1], [func, 2]]]
491 #
492 # Hence we deal with pci device configurations outside of
493 # the regular device parsing.
495 cfg['devices'] = {}
496 for dev in sxp.children(sxp_cfg, 'device'):
497 config = sxp.child0(dev)
498 dev_type = sxp.name(config)
499 dev_info = {}
501 if dev_type == 'pci':
502 pci_devs_uuid = sxp.child_value(config, 'uuid',
503 uuid.createString())
504 pci_devs = []
505 for pci_dev in sxp.children(config, 'dev'):
506 pci_dev_info = {}
507 for opt, val in pci_dev[1:]:
508 pci_dev_info[opt] = val
509 pci_devs.append(pci_dev_info)
511 cfg['devices'][pci_devs_uuid] = (dev_type,
512 {'devs': pci_devs,
513 'uuid': pci_devs_uuid})
515 log.debug("XendConfig: reading device: %s" % pci_devs)
516 else:
517 self.device_add(dev_type, cfg_sxp = config, target = cfg)
518 log.debug("XendConfig: reading device: %s" % scrub_password(dev_info))
520 # Extract missing data from configuration entries
521 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
522 if image_sxp:
523 image_vcpus = sxp.child_value(image_sxp, 'vcpus')
524 if image_vcpus != None:
525 try:
526 if 'vcpus_number' not in cfg:
527 cfg['vcpus_number'] = int(image_vcpus)
528 elif cfg['vcpus_number'] != int(image_vcpus):
529 cfg['vcpus_number'] = int(image_vcpus)
530 log.warn('Overriding vcpus from %d to %d using image'
531 'vcpus value.', cfg['vcpus_number'])
532 except ValueError, e:
533 raise XendConfigError('integer expeceted: %s: %s' %
534 image_sxp, e)
536 # Deprecated cpu configuration
537 if 'cpu' in cfg:
538 if 'cpus' in cfg:
539 cfg['cpus'] = "%s,%s" % (str(cfg['cpu']), cfg['cpus'])
540 else:
541 cfg['cpus'] = str(cfg['cpu'])
543 # convert 'cpus' string to list of ints
544 # 'cpus' supports a list of ranges (0-3), seperated by
545 # commas, and negation, (^1).
546 # Precedence is settled by order of the string:
547 # "0-3,^1" -> [0,2,3]
548 # "0-3,^1,1" -> [0,1,2,3]
549 try:
550 if 'cpus' in cfg:
551 cpus = []
552 for c in cfg['cpus'].split(','):
553 if c.find('-') != -1:
554 (x, y) = c.split('-')
555 for i in range(int(x), int(y)+1):
556 cpus.append(int(i))
557 else:
558 # remove this element from the list
559 if c[0] == '^':
560 cpus = [x for x in cpus if x != int(c[1:])]
561 else:
562 cpus.append(int(c))
564 cfg['cpus'] = cpus
565 except ValueError, e:
566 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
568 if 'security' in cfg and isinstance(cfg['security'], str):
569 cfg['security'] = sxp.from_string(cfg['security'])
571 # TODO: get states
572 old_state = sxp.child_value(sxp_cfg, 'state')
573 if old_state:
574 for i in range(len(CONFIG_OLD_DOM_STATES)):
575 cfg[CONFIG_OLD_DOM_STATES[i]] = int(old_state[i] != '-')
577 return cfg
580 def _sxp_to_xapi(self, sxp_cfg):
581 """Read in an SXP Configuration object and
582 populate at much of the Xen API with valid values.
583 """
584 cfg = self._parse_sxp(sxp_cfg)
586 # Convert parameters that can be directly mapped from
587 # the Legacy Config to Xen API Config
589 for apikey, cfgkey in XENAPI_CFG_TO_LEGACY_CFG.items():
590 try:
591 type_conv = XENAPI_CFG_TYPES.get(apikey)
592 if callable(type_conv):
593 self[apikey] = type_conv(cfg[cfgkey])
594 else:
595 log.warn("Unconverted key: " + apikey)
596 self[apikey] = cfg[cfgkey]
597 except KeyError:
598 pass
600 # Convert Legacy "image" config to Xen API kernel_*
601 # configuration
602 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
603 if image_sxp:
604 self['kernel_kernel'] = sxp.child_value(image_sxp, 'kernel','')
605 self['kernel_initrd'] = sxp.child_value(image_sxp, 'ramdisk','')
606 kernel_args = sxp.child_value(image_sxp, 'args', '')
608 # attempt to extract extra arguments from SXP config
609 arg_ip = sxp.child_value(image_sxp, 'ip')
610 if arg_ip and not re.search(r'ip=[^ ]+', kernel_args):
611 kernel_args += ' ip=%s' % arg_ip
612 arg_root = sxp.child_value(image_sxp, 'root')
613 if arg_root and not re.search(r'root=[^ ]+', kernel_args):
614 kernel_args += ' root=%s' % arg_root
616 self['kernel_args'] = kernel_args
618 # Convert Legacy HVM parameters to Xen API configuration
619 self['platform_std_vga'] = bool0(cfg.get('stdvga', 0))
620 self['platform_serial'] = str(cfg.get('serial', ''))
621 self['platform_localtime'] = bool0(cfg.get('localtime', 0))
622 self['platform_enable_audio'] = bool0(cfg.get('soundhw', 0))
624 # Convert path to bootloader to boot_method
625 if not cfg.get('bootloader'):
626 if self.get('kernel_kernel','').endswith('hvmloader'):
627 self['boot_method'] = 'bios'
628 else:
629 self['boot_method'] = 'kernel_external'
630 else:
631 self['boot_method'] = 'grub'
633 # make sure a sane maximum is set
634 if self['memory_static_max'] <= 0:
635 self['memory_static_max'] = self['memory_static_min']
637 self['memory_dynamic_max'] = self['memory_static_max']
638 self['memory_dynamic_min'] = self['memory_static_min']
640 # make sure max_vcpu_id is set correctly
641 self['max_vcpu_id'] = self['vcpus_number'] - 1
643 # set device references in the configuration
644 self['devices'] = cfg.get('devices', {})
646 self['vif_refs'] = []
647 self['vbd_refs'] = []
648 self['vtpm_refs'] = []
649 for dev_uuid, (dev_type, dev_info) in self['devices'].items():
650 if dev_type == 'vif':
651 self['vif_refs'].append(dev_uuid)
652 elif dev_type in ('vbd','tap'):
653 self['vbd_refs'].append(dev_uuid)
654 elif dev_type in ('vtpm',):
655 self['vtpm_refs'].append(dev_uuid)
658 def _sxp_to_xapi_unsupported(self, sxp_cfg):
659 """Read in an SXP configuration object and populate
660 values are that not related directly supported in
661 the Xen API.
662 """
664 # Parse and convert parameters used to configure
665 # the image (as well as HVM images)
666 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
667 if image_sxp:
668 image = {}
669 image['type'] = sxp.name(image_sxp)
670 for arg, conv in LEGACY_IMAGE_CFG:
671 val = sxp.child_value(image_sxp, arg, None)
672 if val != None:
673 image[arg] = conv(val)
675 image_hvm = {}
676 for arg, conv in LEGACY_IMAGE_HVM_CFG:
677 val = sxp.child_value(image_sxp, arg, None)
678 if val != None:
679 image_hvm[arg] = conv(val)
681 image_hvm_devices = {}
682 for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
683 val = sxp.child_value(image_sxp, arg, None)
684 if val != None:
685 image_hvm_devices[arg] = conv(val)
687 if image_hvm or image_hvm_devices:
688 image['hvm'] = image_hvm
689 image['hvm']['devices'] = image_hvm_devices
691 self['image'] = image
693 for apikey, imgkey in XENAPI_HVM_CFG.items():
694 val = sxp.child_value(image_sxp, imgkey, None)
695 if val != None:
696 self[apikey] = val
698 # extract backend value
700 backend = []
701 for c in sxp.children(sxp_cfg, 'backend'):
702 backend.append(sxp.name(sxp.child0(c)))
703 if backend:
704 self['backend'] = backend
706 if self['image'].has_key('hvm'):
707 self['builder'] = 'hvm'
709 # Parse and convert other Non Xen API parameters.
710 def _set_cfg_if_exists(sxp_arg):
711 val = sxp.child_value(sxp_cfg, sxp_arg)
712 if val != None:
713 if LEGACY_CFG_TYPES.get(sxp_arg):
714 self[sxp_arg] = LEGACY_CFG_TYPES[sxp_arg](val)
715 else:
716 self[sxp_arg] = val
718 _set_cfg_if_exists('bootloader')
719 _set_cfg_if_exists('shadow_memory')
720 _set_cfg_if_exists('security')
721 _set_cfg_if_exists('features')
722 _set_cfg_if_exists('on_xend_stop')
723 _set_cfg_if_exists('on_xend_start')
724 _set_cfg_if_exists('vcpu_avail')
725 _set_cfg_if_exists('max_vcpu_id') # needed for vcpuDomDetails
727 # Parse and store runtime configuration
728 _set_cfg_if_exists('start_time')
729 _set_cfg_if_exists('online_vcpus')
730 _set_cfg_if_exists('cpu_time')
731 _set_cfg_if_exists('shutdown_reason')
732 _set_cfg_if_exists('up_time')
733 _set_cfg_if_exists('status') # TODO, deprecated
735 def _add_xapi_unsupported(self):
736 """Updates the configuration object with entries that are not
737 officially supported by the Xen API but is required for
738 the rest of Xend to function.
739 """
741 # populate image
742 self['image']['type'] = self['builder']
743 if self['builder'] == '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 """
780 for key, val in xapi.items():
781 key = key.lower()
782 type_conv = XENAPI_CFG_TYPES.get(key)
783 if callable(type_conv):
784 self[key] = type_conv(val)
785 else:
786 self[key] = val
788 self.validate()
790 def to_xml(self):
791 """Return an XML string representing the configuration."""
792 pass
794 def to_sxp(self, domain = None, ignore_devices = False, ignore = []):
795 """ Get SXP representation of this config object.
797 Incompat: removed store_mfn, console_mfn
799 @keyword domain: (optional) XendDomainInfo to get extra information
800 from such as domid and running devices.
801 @type domain: XendDomainInfo
802 @keyword ignore: (optional) list of 'keys' that we do not want
803 to export.
804 @type ignore: list of strings
805 @rtype: list of list (SXP representation)
806 """
807 sxpr = ['domain']
809 # TODO: domid/dom is the same thing but called differently
810 # depending if it is from xenstore or sxpr.
812 if domain.getDomid() is not None:
813 sxpr.append(['domid', domain.getDomid()])
815 for xenapi, legacy in XENAPI_CFG_TO_LEGACY_CFG.items():
816 if self.has_key(xenapi) and self[xenapi] not in (None, []):
817 if type(self[xenapi]) == bool:
818 # convert booleans to ints before making an sxp item
819 sxpr.append([legacy, int(self[xenapi])])
820 else:
821 sxpr.append([legacy, self[xenapi]])
823 for legacy in LEGACY_UNSUPPORTED_BY_XENAPI_CFG:
824 if legacy in ('domid', 'uuid'): # skip these
825 continue
826 if self.has_key(legacy) and self[legacy] not in (None, []):
827 sxpr.append([legacy, self[legacy]])
829 if 'image' in self and self['image']:
830 sxpr.append(['image', self.image_sxpr()])
832 sxpr.append(['status', domain.state])
833 sxpr.append(['memory_dynamic_min', self.get('memory_dynamic_min')])
834 sxpr.append(['memory_dynamic_max', self.get('memory_dynamic_max')])
836 if domain.getDomid() is not None:
837 sxpr.append(['state', self._get_old_state_string()])
839 if domain:
840 if domain.store_mfn:
841 sxpr.append(['store_mfn', domain.store_mfn])
842 if domain.console_mfn:
843 sxpr.append(['console_mfn', domain.console_mfn])
846 # Marshall devices (running or from configuration)
847 if not ignore_devices:
848 for cls in XendDevices.valid_devices():
849 found = False
851 # figure if there is a device that is running
852 if domain:
853 try:
854 controller = domain.getDeviceController(cls)
855 configs = controller.configurations()
856 for config in configs:
857 sxpr.append(['device', config])
858 found = True
859 except:
860 log.exception("dumping sxp from device controllers")
861 pass
863 # if we didn't find that device, check the existing config
864 # for a device in the same class
865 if not found:
866 for dev_type, dev_info in self.all_devices_sxpr():
867 if dev_type == cls:
868 sxpr.append(['device', dev_info])
870 return sxpr
872 def device_add(self, dev_type, cfg_sxp = None, cfg_xenapi = None,
873 target = None):
874 """Add a device configuration in SXP format or XenAPI struct format.
876 For SXP, it could be either:
878 [device, [vbd, [uname ...]]
880 or:
882 [vbd, [uname ..]]
884 @type cfg_sxp: list of lists (parsed sxp object)
885 @param cfg_sxp: SXP configuration object
886 @type cfg_xenapi: dict
887 @param cfg_xenapi: A device configuration from Xen API (eg. vbd,vif)
888 @param target: write device information to
889 @type target: None or a dictionary
890 @rtype: string
891 @return: Assigned UUID of the device.
892 """
893 if target == None:
894 target = self
896 if dev_type not in XendDevices.valid_devices() and \
897 dev_type not in XendDevices.pseudo_devices():
898 raise XendConfigError("XendConfig: %s not a valid device type" %
899 dev_type)
901 if cfg_sxp == None and cfg_xenapi == None:
902 raise XendConfigError("XendConfig: device_add requires some "
903 "config.")
905 if cfg_sxp:
906 log.debug("XendConfig.device_add: %s" % str(cfg_sxp))
907 if cfg_xenapi:
908 log.debug("XendConfig.device_add: %s" % str(cfg_xenapi))
910 if cfg_sxp:
911 if sxp.child0(cfg_sxp) == 'device':
912 config = sxp.child0(cfg_sxp)
913 else:
914 config = cfg_sxp
916 dev_type = sxp.name(config)
917 dev_info = {}
919 try:
920 for opt, val in config[1:]:
921 dev_info[opt] = val
922 except ValueError:
923 pass # SXP has no options for this device
925 if dev_type == 'vbd':
926 if dev_info.get('dev', '').startswith('ioemu:'):
927 dev_info['driver'] = 'ioemu'
928 else:
929 dev_info['driver'] = 'paravirtualised'
931 # create uuid if it doesn't exist
932 dev_uuid = dev_info.get('uuid', uuid.createString())
933 dev_info['uuid'] = dev_uuid
935 # store dev references by uuid for certain device types
936 target['devices'][dev_uuid] = (dev_type, dev_info)
937 if dev_type in ('vif', 'vbd', 'vtpm'):
938 param = '%s_refs' % dev_type
939 if param not in target:
940 target[param] = []
941 if dev_uuid not in target[param]:
942 target[param].append(dev_uuid)
943 elif dev_type in ('tap',):
944 if 'vbd_refs' not in target:
945 target['vbd_refs'] = []
946 if dev_uuid not in target['vbd_refs']:
947 target['vbd_refs'].append(dev_uuid)
949 return dev_uuid
951 if cfg_xenapi:
952 dev_info = {}
953 if dev_type == 'vif':
954 if cfg_xenapi.get('MAC'): # don't add if blank
955 dev_info['mac'] = cfg_xenapi.get('MAC')
956 # vifname is the name on the guest, not dom0
957 # TODO: we don't have the ability to find that out or
958 # change it from dom0
959 #if cfg_xenapi.get('device'): # don't add if blank
960 # dev_info['vifname'] = cfg_xenapi.get('device')
961 if cfg_xenapi.get('type'):
962 dev_info['type'] = cfg_xenapi.get('type')
964 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
965 dev_info['uuid'] = dev_uuid
966 self['devices'][dev_uuid] = (dev_type, dev_info)
967 self['vif_refs'].append(dev_uuid)
968 return dev_uuid
970 elif dev_type in ('vbd', 'tap'):
971 if dev_type == 'vbd':
972 dev_info['uname'] = cfg_xenapi.get('image', '')
973 dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
974 elif dev_type == 'tap':
975 dev_info['uname'] = 'tap:qcow:%s' % cfg_xenapi.get('image')
976 dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
978 dev_info['driver'] = cfg_xenapi.get('driver')
979 dev_info['VDI'] = cfg_xenapi.get('VDI', '')
981 if cfg_xenapi.get('mode') == 'RW':
982 dev_info['mode'] = 'w'
983 else:
984 dev_info['mode'] = 'r'
986 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
987 dev_info['uuid'] = dev_uuid
988 self['devices'][dev_uuid] = (dev_type, dev_info)
989 self['vbd_refs'].append(dev_uuid)
990 return dev_uuid
992 elif dev_type in ('vtpm'):
993 if cfg_xenapi.get('type'):
994 dev_info['type'] = cfg_xenapi.get('type')
996 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
997 dev_info['uuid'] = dev_uuid
998 self['devices'][dev_uuid] = (dev_type, dev_info)
999 self['vtpm_refs'].append(dev_uuid)
1000 return dev_uuid
1002 return ''
1004 def device_update(self, dev_uuid, cfg_sxp):
1005 """Update an existing device with the new configuration.
1007 @rtype: boolean
1008 @return: Returns True if succesfully found and updated a device conf
1009 """
1010 if dev_uuid in self['devices']:
1011 config = sxp.child0(cfg_sxp)
1012 dev_type = sxp.name(config)
1013 dev_info = {}
1015 try:
1016 for opt, val in config[1:]:
1017 self['devices'][opt] = val
1018 except ValueError:
1019 pass # SXP has no options for this device
1021 return True
1023 return False
1026 def device_sxpr(self, dev_uuid = None, dev_type = None, dev_info = None):
1027 """Get Device SXPR by either giving the device UUID or (type, config).
1029 @rtype: list of lists
1030 @return: device config sxpr
1031 """
1032 sxpr = []
1033 if dev_uuid != None and dev_uuid in self['devices']:
1034 dev_type, dev_info = self['devices'][dev_uuid]
1036 if dev_type == None or dev_info == None:
1037 raise XendConfigError("Required either UUID or device type and "
1038 "configuration dictionary.")
1040 sxpr.append(dev_type)
1041 config = [(opt, val) for opt, val in dev_info.items()]
1042 sxpr += config
1044 return sxpr
1046 def all_devices_sxpr(self):
1047 """Returns the SXPR for all devices in the current configuration."""
1048 sxprs = []
1049 pci_devs = []
1051 if 'devices' not in self:
1052 return sxprs
1054 for dev_type, dev_info in self['devices'].values():
1055 if dev_type == 'pci': # special case for pci devices
1056 sxpr = [['uuid', dev_info['uuid']]]
1057 for pci_dev_info in dev_info['devs']:
1058 pci_dev_sxpr = ['dev']
1059 for opt, val in pci_dev_info.items():
1060 pci_dev_sxpr.append([opt, val])
1061 sxpr.append(pci_dev_sxpr)
1062 sxprs.append((dev_type, sxpr))
1063 else:
1064 sxpr = self.device_sxpr(dev_type = dev_type,
1065 dev_info = dev_info)
1066 sxprs.append((dev_type, sxpr))
1068 return sxprs
1070 def image_sxpr(self):
1071 """Returns a backwards compatible image SXP expression that is
1072 used in xenstore's /vm/<uuid>/image value and xm list."""
1073 image = [self['image'].get('type', 'linux')]
1074 if self.has_key('kernel_kernel'):
1075 image.append(['kernel', self['kernel_kernel']])
1076 if self.has_key('kernel_initrd') and self['kernel_initrd']:
1077 image.append(['ramdisk', self['kernel_initrd']])
1078 if self.has_key('kernel_args') and self['kernel_args']:
1079 image.append(['args', self['kernel_args']])
1081 for arg, conv in LEGACY_IMAGE_CFG:
1082 if self['image'].has_key(arg):
1083 image.append([arg, self['image'][arg]])
1085 if 'hvm' in self['image']:
1086 for arg, conv in LEGACY_IMAGE_HVM_CFG:
1087 if self['image']['hvm'].get(arg):
1088 image.append([arg, self['image']['hvm'][arg]])
1090 if 'hvm' in self['image'] and 'devices' in self['image']['hvm']:
1091 for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
1092 if self['image']['hvm']['devices'].get(arg):
1093 image.append([arg,
1094 self['image']['hvm']['devices'][arg]])
1096 return image
1098 def update_with_image_sxp(self, image_sxp):
1099 # Convert Legacy "image" config to Xen API kernel_*
1100 # configuration
1101 self['kernel_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1102 self['kernel_initrd'] = sxp.child_value(image_sxp, 'ramdisk','')
1103 kernel_args = sxp.child_value(image_sxp, 'args', '')
1105 # attempt to extract extra arguments from SXP config
1106 arg_ip = sxp.child_value(image_sxp, 'ip')
1107 if arg_ip and not re.search(r'ip=[^ ]+', kernel_args):
1108 kernel_args += ' ip=%s' % arg_ip
1109 arg_root = sxp.child_value(image_sxp, 'root')
1110 if arg_root and not re.search(r'root=', kernel_args):
1111 kernel_args += ' root=%s' % arg_root
1112 self['kernel_args'] = kernel_args
1114 # Store image SXP in python dictionary format
1115 image = {}
1116 image['type'] = sxp.name(image_sxp)
1117 for arg, conv in LEGACY_IMAGE_CFG:
1118 val = sxp.child_value(image_sxp, arg, None)
1119 if val != None:
1120 image[arg] = conv(val)
1122 image_hvm = {}
1123 for arg, conv in LEGACY_IMAGE_HVM_CFG:
1124 val = sxp.child_value(image_sxp, arg, None)
1125 if val != None:
1126 image_hvm[arg] = conv(val)
1128 image_hvm_devices = {}
1129 for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
1130 val = sxp.child_value(image_sxp, arg, None)
1131 if val != None:
1132 image_hvm_devices[arg] = conv(val)
1134 if image_hvm or image_hvm_devices:
1135 image['hvm'] = image_hvm
1136 image['hvm']['devices'] = image_hvm_devices
1138 self['image'] = image
1140 for apikey, imgkey in XENAPI_HVM_CFG.items():
1141 val = sxp.child_value(image_sxp, imgkey, None)
1142 if val != None:
1143 type_conv = XENAPI_CFG_TYPES[apikey]
1144 if callable(conv):
1145 self[apikey] = type_conv(val)
1146 else:
1147 self[apikey] = val
1151 # debugging
1154 if __name__ == "__main__":
1155 pass