ia64/xen-unstable

view tools/python/xen/xend/XendConfig.py @ 13208:5c268a24e44b

Do not update the PV_ variables with the values outputted by the bootloader --
this gets us into all sorts of trouble when Xend is restarted and then the
domain is rebooted, because we expect to be able to handle the PV_kernel == ''
case by defaulting to pygrub.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author Ewan Mellor <ewan@xensource.com>
date Thu Dec 28 15:26:19 2006 +0000 (2006-12-28)
parents 90400f2c10c1
children 7cd6c032689e
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()
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):
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 hvm = self['HVM_boot'] != ''
739 self['image']['type'] = hvm and 'hvm' or 'linux'
740 if hvm:
741 self['image']['hvm'] = {}
742 for xapi, cfgapi in XENAPI_HVM_CFG.items():
743 self['image']['hvm'][cfgapi] = self[xapi]
746 def _get_old_state_string(self):
747 """Returns the old xm state string.
748 @rtype: string
749 @return: old state string
750 """
751 state_string = ''
752 for state_name in CONFIG_OLD_DOM_STATES:
753 on_off = self.get(state_name, 0)
754 if on_off:
755 state_string += state_name[0]
756 else:
757 state_string += '-'
759 return state_string
762 def update_config(self, dominfo):
763 """Update configuration with the output from xc.domain_getinfo().
765 @param dominfo: Domain information via xc.domain_getinfo()
766 @type dominfo: dict
767 """
768 self._dominfo_to_xapi(dominfo)
769 self.validate()
771 def update_with_xenapi_config(self, xapi):
772 """Update configuration with a Xen API VM struct
774 @param xapi: Xen API VM Struct
775 @type xapi: dict
776 """
778 log.debug('update_with_xenapi_config: %s' % scrub_password(xapi))
780 for key, val in xapi.items():
781 type_conv = XENAPI_CFG_TYPES.get(key)
782 if type_conv is None:
783 key = key.lower()
784 type_conv = XENAPI_CFG_TYPES.get(key)
785 if callable(type_conv):
786 self[key] = type_conv(val)
787 else:
788 self[key] = val
790 self.validate()
792 def to_sxp(self, domain = None, ignore_devices = False, ignore = [],
793 legacy_only = True):
794 """ Get SXP representation of this config object.
796 Incompat: removed store_mfn, console_mfn
798 @keyword domain: (optional) XendDomainInfo to get extra information
799 from such as domid and running devices.
800 @type domain: XendDomainInfo
801 @keyword ignore: (optional) list of 'keys' that we do not want
802 to export.
803 @type ignore: list of strings
804 @rtype: list of list (SXP representation)
805 """
806 sxpr = ['domain']
808 # TODO: domid/dom is the same thing but called differently
809 # depending if it is from xenstore or sxpr.
811 if domain.getDomid() is not None:
812 sxpr.append(['domid', domain.getDomid()])
814 if not legacy_only:
815 for name in XENAPI_CFG_TYPES.keys():
816 if name in self and self[name] not in (None, []):
817 sxpr.append([name, str(self[name])])
819 for xenapi, legacy in XENAPI_CFG_TO_LEGACY_CFG.items():
820 if self.has_key(xenapi) and self[xenapi] not in (None, []):
821 if type(self[xenapi]) == bool:
822 # convert booleans to ints before making an sxp item
823 sxpr.append([legacy, int(self[xenapi])])
824 else:
825 sxpr.append([legacy, self[xenapi]])
827 for legacy in LEGACY_UNSUPPORTED_BY_XENAPI_CFG:
828 if legacy in ('domid', 'uuid'): # skip these
829 continue
830 if self.has_key(legacy) and self[legacy] not in (None, []):
831 sxpr.append([legacy, self[legacy]])
833 if 'image' in self and self['image']:
834 sxpr.append(['image', self.image_sxpr()])
836 sxpr.append(['status', domain.state])
837 sxpr.append(['memory_dynamic_min', self.get('memory_dynamic_min')])
838 sxpr.append(['memory_dynamic_max', self.get('memory_dynamic_max')])
840 if domain.getDomid() is not None:
841 sxpr.append(['state', self._get_old_state_string()])
843 if domain:
844 if domain.store_mfn:
845 sxpr.append(['store_mfn', domain.store_mfn])
846 if domain.console_mfn:
847 sxpr.append(['console_mfn', domain.console_mfn])
850 # Marshall devices (running or from configuration)
851 if not ignore_devices:
852 for cls in XendDevices.valid_devices():
853 found = False
855 # figure if there is a device that is running
856 if domain:
857 try:
858 controller = domain.getDeviceController(cls)
859 configs = controller.configurations()
860 for config in configs:
861 sxpr.append(['device', config])
862 found = True
863 except:
864 log.exception("dumping sxp from device controllers")
865 pass
867 # if we didn't find that device, check the existing config
868 # for a device in the same class
869 if not found:
870 for dev_type, dev_info in self.all_devices_sxpr():
871 if dev_type == cls:
872 sxpr.append(['device', dev_info])
874 return sxpr
876 def device_add(self, dev_type, cfg_sxp = None, cfg_xenapi = None,
877 target = None):
878 """Add a device configuration in SXP format or XenAPI struct format.
880 For SXP, it could be either:
882 [device, [vbd, [uname ...]]
884 or:
886 [vbd, [uname ..]]
888 @type cfg_sxp: list of lists (parsed sxp object)
889 @param cfg_sxp: SXP configuration object
890 @type cfg_xenapi: dict
891 @param cfg_xenapi: A device configuration from Xen API (eg. vbd,vif)
892 @param target: write device information to
893 @type target: None or a dictionary
894 @rtype: string
895 @return: Assigned UUID of the device.
896 """
897 if target == None:
898 target = self
900 if dev_type not in XendDevices.valid_devices() and \
901 dev_type not in XendDevices.pseudo_devices():
902 raise XendConfigError("XendConfig: %s not a valid device type" %
903 dev_type)
905 if cfg_sxp == None and cfg_xenapi == None:
906 raise XendConfigError("XendConfig: device_add requires some "
907 "config.")
909 if cfg_sxp:
910 log.debug("XendConfig.device_add: %s" % str(cfg_sxp))
911 if cfg_xenapi:
912 log.debug("XendConfig.device_add: %s" % str(cfg_xenapi))
914 if cfg_sxp:
915 if sxp.child0(cfg_sxp) == 'device':
916 config = sxp.child0(cfg_sxp)
917 else:
918 config = cfg_sxp
920 dev_type = sxp.name(config)
921 dev_info = {}
923 try:
924 for opt, val in config[1:]:
925 dev_info[opt] = val
926 except ValueError:
927 pass # SXP has no options for this device
929 if dev_type == 'vbd':
930 if dev_info.get('dev', '').startswith('ioemu:'):
931 dev_info['driver'] = 'ioemu'
932 else:
933 dev_info['driver'] = 'paravirtualised'
935 # create uuid if it doesn't exist
936 dev_uuid = dev_info.get('uuid', uuid.createString())
937 dev_info['uuid'] = dev_uuid
939 # store dev references by uuid for certain device types
940 target['devices'][dev_uuid] = (dev_type, dev_info)
941 if dev_type in ('vif', 'vbd', 'vtpm'):
942 param = '%s_refs' % dev_type
943 if param not in target:
944 target[param] = []
945 if dev_uuid not in target[param]:
946 target[param].append(dev_uuid)
947 elif dev_type in ('tap',):
948 if 'vbd_refs' not in target:
949 target['vbd_refs'] = []
950 if dev_uuid not in target['vbd_refs']:
951 target['vbd_refs'].append(dev_uuid)
953 return dev_uuid
955 if cfg_xenapi:
956 dev_info = {}
957 if dev_type == 'vif':
958 if cfg_xenapi.get('MAC'): # don't add if blank
959 dev_info['mac'] = cfg_xenapi.get('MAC')
960 # vifname is the name on the guest, not dom0
961 # TODO: we don't have the ability to find that out or
962 # change it from dom0
963 #if cfg_xenapi.get('device'): # don't add if blank
964 # dev_info['vifname'] = cfg_xenapi.get('device')
965 if cfg_xenapi.get('type'):
966 dev_info['type'] = cfg_xenapi.get('type')
968 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
969 dev_info['uuid'] = dev_uuid
970 self['devices'][dev_uuid] = (dev_type, dev_info)
971 self['vif_refs'].append(dev_uuid)
972 return dev_uuid
974 elif dev_type in ('vbd', 'tap'):
975 if dev_type == 'vbd':
976 dev_info['uname'] = cfg_xenapi.get('image', '')
977 dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
978 elif dev_type == 'tap':
979 dev_info['uname'] = 'tap:qcow:%s' % cfg_xenapi.get('image')
980 dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
982 dev_info['driver'] = cfg_xenapi.get('driver')
983 dev_info['VDI'] = cfg_xenapi.get('VDI', '')
985 if cfg_xenapi.get('mode') == 'RW':
986 dev_info['mode'] = 'w'
987 else:
988 dev_info['mode'] = 'r'
990 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
991 dev_info['uuid'] = dev_uuid
992 self['devices'][dev_uuid] = (dev_type, dev_info)
993 self['vbd_refs'].append(dev_uuid)
994 return dev_uuid
996 elif dev_type in ('vtpm'):
997 if cfg_xenapi.get('type'):
998 dev_info['type'] = cfg_xenapi.get('type')
1000 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
1001 dev_info['uuid'] = dev_uuid
1002 self['devices'][dev_uuid] = (dev_type, dev_info)
1003 self['vtpm_refs'].append(dev_uuid)
1004 return dev_uuid
1006 return ''
1008 def device_update(self, dev_uuid, cfg_sxp):
1009 """Update an existing device with the new configuration.
1011 @rtype: boolean
1012 @return: Returns True if succesfully found and updated a device conf
1013 """
1014 if dev_uuid in self['devices']:
1015 config = sxp.child0(cfg_sxp)
1016 dev_type = sxp.name(config)
1017 dev_info = {}
1019 try:
1020 for opt, val in config[1:]:
1021 self['devices'][opt] = val
1022 except ValueError:
1023 pass # SXP has no options for this device
1025 return True
1027 return False
1030 def device_sxpr(self, dev_uuid = None, dev_type = None, dev_info = None):
1031 """Get Device SXPR by either giving the device UUID or (type, config).
1033 @rtype: list of lists
1034 @return: device config sxpr
1035 """
1036 sxpr = []
1037 if dev_uuid != None and dev_uuid in self['devices']:
1038 dev_type, dev_info = self['devices'][dev_uuid]
1040 if dev_type == None or dev_info == None:
1041 raise XendConfigError("Required either UUID or device type and "
1042 "configuration dictionary.")
1044 sxpr.append(dev_type)
1045 config = [(opt, val) for opt, val in dev_info.items()]
1046 sxpr += config
1048 return sxpr
1050 def all_devices_sxpr(self):
1051 """Returns the SXPR for all devices in the current configuration."""
1052 sxprs = []
1053 pci_devs = []
1055 if 'devices' not in self:
1056 return sxprs
1058 for dev_type, dev_info in self['devices'].values():
1059 if dev_type == 'pci': # special case for pci devices
1060 sxpr = [['uuid', dev_info['uuid']]]
1061 for pci_dev_info in dev_info['devs']:
1062 pci_dev_sxpr = ['dev']
1063 for opt, val in pci_dev_info.items():
1064 pci_dev_sxpr.append([opt, val])
1065 sxpr.append(pci_dev_sxpr)
1066 sxprs.append((dev_type, sxpr))
1067 else:
1068 sxpr = self.device_sxpr(dev_type = dev_type,
1069 dev_info = dev_info)
1070 sxprs.append((dev_type, sxpr))
1072 return sxprs
1074 def image_sxpr(self):
1075 """Returns a backwards compatible image SXP expression that is
1076 used in xenstore's /vm/<uuid>/image value and xm list."""
1077 image = [self['image'].get('type', 'linux')]
1078 if self.has_key('PV_kernel'):
1079 image.append(['kernel', self['PV_kernel']])
1080 if self.has_key('PV_ramdisk') and self['PV_ramdisk']:
1081 image.append(['ramdisk', self['PV_ramdisk']])
1082 if self.has_key('PV_args') and self['PV_args']:
1083 image.append(['args', self['PV_args']])
1085 for arg, conv in LEGACY_IMAGE_CFG:
1086 if self['image'].has_key(arg):
1087 image.append([arg, self['image'][arg]])
1089 if 'hvm' in self['image']:
1090 for arg, conv in LEGACY_IMAGE_HVM_CFG:
1091 if self['image']['hvm'].get(arg):
1092 image.append([arg, self['image']['hvm'][arg]])
1094 if 'hvm' in self['image'] and 'devices' in self['image']['hvm']:
1095 for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
1096 if self['image']['hvm']['devices'].get(arg):
1097 image.append([arg,
1098 self['image']['hvm']['devices'][arg]])
1100 return image
1102 def update_with_image_sxp(self, image_sxp, bootloader = False):
1103 # Convert Legacy "image" config to Xen API PV_*
1104 # configuration
1105 log.debug("update_with_image_sxp(%s)" % scrub_password(image_sxp))
1107 kernel_args = sxp.child_value(image_sxp, 'args', '')
1109 # attempt to extract extra arguments from SXP config
1110 arg_ip = sxp.child_value(image_sxp, 'ip')
1111 if arg_ip and not re.search(r'ip=[^ ]+', kernel_args):
1112 kernel_args += ' ip=%s' % arg_ip
1113 arg_root = sxp.child_value(image_sxp, 'root')
1114 if arg_root and not re.search(r'root=', kernel_args):
1115 kernel_args += ' root=%s' % arg_root
1117 if bootloader:
1118 self['_temp_using_bootloader'] = '1'
1119 self['_temp_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1120 self['_temp_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1121 self['_temp_args'] = kernel_args
1122 else:
1123 self['PV_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1124 self['PV_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1125 self['PV_args'] = kernel_args
1127 # Store image SXP in python dictionary format
1128 image = {}
1129 image['type'] = sxp.name(image_sxp)
1130 for arg, conv in LEGACY_IMAGE_CFG:
1131 val = sxp.child_value(image_sxp, arg, None)
1132 if val != None:
1133 image[arg] = conv(val)
1135 image_hvm = {}
1136 for arg, conv in LEGACY_IMAGE_HVM_CFG:
1137 val = sxp.child_value(image_sxp, arg, None)
1138 if val != None:
1139 image_hvm[arg] = conv(val)
1141 image_hvm_devices = {}
1142 for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
1143 val = sxp.child_value(image_sxp, arg, None)
1144 if val != None:
1145 image_hvm_devices[arg] = conv(val)
1147 if image_hvm or image_hvm_devices:
1148 image['hvm'] = image_hvm
1149 image['hvm']['devices'] = image_hvm_devices
1151 self['image'] = image
1153 for apikey, imgkey in XENAPI_HVM_CFG.items():
1154 val = sxp.child_value(image_sxp, imgkey, None)
1155 if val != None:
1156 type_conv = XENAPI_CFG_TYPES[apikey]
1157 if callable(conv):
1158 self[apikey] = type_conv(val)
1159 else:
1160 self[apikey] = val
1164 # debugging
1167 if __name__ == "__main__":
1168 pass