ia64/xen-unstable

view tools/python/xen/xend/XendConfig.py @ 13457:13db896ef324

[XEND] Fix warning about 'cpus' option on domain creation.
Signed-off-by: Masaki Kanno <kanno.masaki@jp.fujitsu.com>
author kfraser@localhost.localdomain
date Wed Jan 17 14:54:59 2007 +0000 (2007-01-17)
parents 66eba8d1b83a
children fd0f2b4b7071
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 'cpus': list,
130 'vcpus_policy': str,
131 'vcpus_params': str,
132 'vcpus_number': int,
133 'vcpus_features_required': list,
134 'vcpus_features_can_use': list,
135 'vcpus_features_force_on': list,
136 'vcpus_features_force_off': list,
137 'actions_after_shutdown': str,
138 'actions_after_reboot': str,
139 'actions_after_suspend': str,
140 'actions_after_crash': str,
141 'tpm_instance': int,
142 'tpm_backend': int,
143 'PV_bootloader': str,
144 'PV_kernel': str,
145 'PV_ramdisk': str,
146 'PV_args': str,
147 'PV_bootloader_args': str,
148 'HVM_boot': str,
149 'platform_std_vga': bool0,
150 'platform_serial': str,
151 'platform_localtime': bool0,
152 'platform_clock_offset': bool0,
153 'platform_enable_audio': bool0,
154 'platform_keymap': str,
155 'pci_bus': str,
156 'tools_version': dict,
157 'otherconfig': dict,
158 }
160 # List of legacy configuration keys that have no equivalent in the
161 # Xen API, but are still stored in XendConfig.
163 LEGACY_UNSUPPORTED_BY_XENAPI_CFG = [
164 # roundtripped (dynamic, unmodified)
165 'shadow_memory',
166 'security',
167 'vcpu_avail',
168 'cpu_weight',
169 'cpu_cap',
170 'features',
171 # read/write
172 'on_xend_start',
173 'on_xend_stop',
174 # read-only
175 'domid',
176 'start_time',
177 'cpu_time',
178 'online_vcpus',
179 # write-once
180 'cpu',
181 'cpus',
182 ]
184 LEGACY_CFG_TYPES = {
185 'uuid': str,
186 'name': str,
187 'vcpus': int,
188 'vcpu_avail': int,
189 'memory': int,
190 'shadow_memory': int,
191 'maxmem': int,
192 'start_time': float,
193 'cpu_cap': int,
194 'cpu_weight': int,
195 'cpu_time': float,
196 'features': str,
197 'localtime': int,
198 'name': str,
199 'on_poweroff': str,
200 'on_reboot': str,
201 'on_crash': str,
202 'on_xend_stop': str,
203 'on_xend_start': str,
204 'online_vcpus': int,
205 }
207 # Values that should be stored in xenstore's /vm/<uuid> that is used
208 # by Xend. Used in XendDomainInfo to restore running VM state from
209 # xenstore.
210 LEGACY_XENSTORE_VM_PARAMS = [
211 'uuid',
212 'name',
213 'vcpus',
214 'vcpu_avail',
215 'memory',
216 'shadow_memory',
217 'maxmem',
218 'start_time',
219 'name',
220 'on_poweroff',
221 'on_crash',
222 'on_reboot',
223 'on_xend_start',
224 'on_xend_stop',
225 ]
227 LEGACY_IMAGE_CFG = [
228 ('root', str),
229 ('ip', str),
230 ('nographic', int),
231 ('vnc', int),
232 ('sdl', int),
233 ('vncdisplay', int),
234 ('vncunused', int),
235 ('vncpasswd', str),
236 ]
238 LEGACY_IMAGE_HVM_CFG = [
239 ('device_model', str),
240 ('display', str),
241 ('xauthority', str),
242 ('vncconsole', int),
243 ('pae', int),
244 ('apic', int),
245 ]
247 LEGACY_IMAGE_HVM_DEVICES_CFG = [
248 ('acpi', int),
249 ('boot', str),
250 ('fda', str),
251 ('fdb', str),
252 ('isa', str),
253 ('keymap', str),
254 ('localtime', str),
255 ('serial', str),
256 ('stdvga', int),
257 ('soundhw', str),
258 ('usb', str),
259 ('usbdevice', str),
260 ('vcpus', int),
261 ]
263 ##
264 ## Config Choices
265 ##
267 CONFIG_RESTART_MODES = ('restart', 'destroy', 'preserve', 'rename-restart')
268 CONFIG_OLD_DOM_STATES = ('running', 'blocked', 'paused', 'shutdown',
269 'crashed', 'dying')
271 class XendConfigError(VmError):
272 def __str__(self):
273 return 'Invalid Configuration: %s' % str(self.value)
275 ##
276 ## XendConfig Class (an extended dictionary)
277 ##
279 class XendConfig(dict):
280 """ The new Xend VM Configuration.
282 Stores the configuration in xenapi compatible format but retains
283 import and export functions for SXP.
284 """
285 def __init__(self, filename = None, sxp_obj = None,
286 xapi = None, dominfo = None):
288 dict.__init__(self)
289 self.update(self._defaults())
291 if filename:
292 try:
293 sxp_obj = sxp.parse(open(filename,'r'))
294 sxp_obj = sxp_obj[0]
295 except IOError, e:
296 raise XendConfigError("Unable to read file: %s" % filename)
298 if sxp_obj:
299 self._sxp_to_xapi(sxp_obj)
300 self._sxp_to_xapi_unsupported(sxp_obj)
301 elif xapi:
302 self.update_with_xenapi_config(xapi)
303 self._add_xapi_unsupported(xapi)
304 elif dominfo:
305 # output from xc.domain_getinfo
306 self._dominfo_to_xapi(dominfo)
308 log.debug('XendConfig.init: %s' % scrub_password(self))
310 # validators go here
311 self.validate()
313 """ In time, we should enable this type checking addition. It is great
314 also for tracking bugs and unintended writes to XendDomainInfo.info
315 def __setitem__(self, key, value):
316 type_conv = XENAPI_CFG_TYPES.get(key)
317 if callable(type_conv):
318 try:
319 dict.__setitem__(self, key, type_conv(value))
320 except (ValueError, TypeError):
321 raise XendConfigError("Wrong type for configuration value " +
322 "%s. Expected %s" %
323 (key, type_conv.__name__))
324 else:
325 dict.__setitem__(self, key, value)
326 """
328 def _defaults(self):
329 defaults = {
330 'uuid': uuid.createString(),
331 'name_label': 'Domain-Unnamed',
332 'actions_after_shutdown': 'destroy',
333 'actions_after_reboot': 'restart',
334 'actions_after_crash': 'restart',
335 'actions_after_suspend': '',
336 'features': '',
337 'PV_bootloader': '',
338 'PV_kernel': '',
339 'PV_ramdisk': '',
340 'PV_args': '',
341 'PV_bootloader_args': '',
342 'HVM_boot': '',
343 'memory_static_min': 0,
344 'memory_dynamic_min': 0,
345 'shadow_memory': 0,
346 'memory_static_max': 0,
347 'memory_dynamic_max': 0,
348 'memory_actual': 0,
349 'devices': {},
350 'image': {},
351 'security': None,
352 'on_xend_start': 'ignore',
353 'on_xend_stop': 'ignore',
354 'cpus': [],
355 'cpu_weight': 256,
356 'cpu_cap': 0,
357 'vcpus_number': 1,
358 'online_vcpus': 1,
359 'max_vcpu_id': 0,
360 'vcpu_avail': 1,
361 'console_refs': [],
362 'vif_refs': [],
363 'vbd_refs': [],
364 'vtpm_refs': [],
365 }
367 defaults['name_label'] = 'Domain-' + defaults['uuid']
368 return defaults
370 def _memory_sanity_check(self):
371 if self['memory_static_min'] == 0:
372 self['memory_static_min'] = self['memory_dynamic_min']
374 # If the static max is not set, let's set it to dynamic max.
375 # If the static max is smaller than static min, then fix it!
376 self['memory_static_max'] = max(self['memory_static_max'],
377 self['memory_dynamic_max'],
378 self['memory_static_min'])
380 for mem_type in ('memory_static_min', 'memory_static_max'):
381 if self[mem_type] <= 0:
382 raise XendConfigError('Memory value too low for %s: %d' %
383 (mem_type, self[mem_type]))
385 def _actions_sanity_check(self):
386 for event in ['shutdown', 'reboot', 'crash']:
387 if self['actions_after_' + event] not in CONFIG_RESTART_MODES:
388 raise XendConfigError('Invalid event handling mode: ' +
389 event)
391 def _vcpus_sanity_check(self):
392 if self.get('vcpus_number') != None:
393 self['vcpu_avail'] = (1 << self['vcpus_number']) - 1
395 def _uuid_sanity_check(self):
396 """Make sure UUID is in proper string format with hyphens."""
397 self['uuid'] = uuid.toString(uuid.fromString(self['uuid']))
399 def validate(self):
400 self._memory_sanity_check()
401 self._actions_sanity_check()
402 self._vcpus_sanity_check()
403 self._uuid_sanity_check()
405 def _dominfo_to_xapi(self, dominfo):
406 self['domid'] = dominfo['domid']
407 self['online_vcpus'] = dominfo['online_vcpus']
408 self['max_vcpu_id'] = dominfo['max_vcpu_id']
409 self['memory_dynamic_min'] = (dominfo['mem_kb'] + 1023)/1024
410 self['memory_dynamic_max'] = (dominfo['maxmem_kb'] + 1023)/1024
411 self['cpu_time'] = dominfo['cpu_time']/1e9
412 # TODO: i don't know what the security stuff expects here
413 if dominfo.get('ssidref'):
414 self['security'] = [['ssidref', dominfo['ssidref']]]
415 self['shutdown_reason'] = dominfo['shutdown_reason']
417 # parse state into Xen API states
418 self['running'] = dominfo['running']
419 self['crashed'] = dominfo['crashed']
420 self['dying'] = dominfo['dying']
421 self['shutdown'] = dominfo['shutdown']
422 self['paused'] = dominfo['paused']
423 self['blocked'] = dominfo['blocked']
425 if 'name' in dominfo:
426 self['name_label'] = dominfo['name']
428 if 'handle' in dominfo:
429 self['uuid'] = uuid.toString(dominfo['handle'])
431 def _parse_sxp(self, sxp_cfg):
432 """ Populate this XendConfig using the parsed SXP.
434 @param sxp_cfg: Parsed SXP Configuration
435 @type sxp_cfg: list of lists
436 @rtype: dictionary
437 @return: A dictionary containing the parsed options of the SXP.
438 """
439 cfg = {}
441 for key, typ in XENAPI_CFG_TYPES.items():
442 val = sxp.child_value(sxp_cfg, key)
443 if val is not None:
444 cfg[key] = typ(val)
446 # Convert deprecated options to current equivalents.
448 restart = sxp.child_value(sxp_cfg, 'restart')
449 if restart:
450 if restart == 'onreboot':
451 cfg['on_poweroff'] = 'destroy'
452 cfg['on_reboot'] = 'restart'
453 cfg['on_crash'] = 'destroy'
454 elif restart == 'always':
455 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
456 cfg[opt] = 'restart'
457 elif restart == 'never':
458 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
459 cfg[opt] = 'never'
460 else:
461 log.warn('Ignoring unrecognised value for deprecated option:'
462 'restart = \'%s\'', restart)
464 # Only extract options we know about.
465 extract_keys = LEGACY_UNSUPPORTED_BY_XENAPI_CFG
466 extract_keys += XENAPI_CFG_TO_LEGACY_CFG.values()
468 for key in extract_keys:
469 val = sxp.child_value(sxp_cfg, key)
470 if val != None:
471 try:
472 cfg[key] = LEGACY_CFG_TYPES[key](val)
473 except KeyError:
474 cfg[key] = val
475 except (TypeError, ValueError), e:
476 log.warn("Unable to parse key %s: %s: %s" %
477 (key, str(val), e))
479 # Parsing the device SXP's. In most cases, the SXP looks
480 # like this:
481 #
482 # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]]
483 #
484 # However, for PCI devices it looks like this:
485 #
486 # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1]]]]
487 #
488 # It seems the reasoning for this difference is because
489 # pciif.py needs all the PCI device configurations at
490 # the same time when creating the devices.
491 #
492 # To further complicate matters, Xen 2.0 configuration format
493 # uses the following for pci device configuration:
494 #
495 # [device, [pci, [domain, 0], [bus, 0], [dev, 1], [func, 2]]]
496 #
497 # Hence we deal with pci device configurations outside of
498 # the regular device parsing.
500 cfg['devices'] = {}
501 for dev in sxp.children(sxp_cfg, 'device'):
502 config = sxp.child0(dev)
503 dev_type = sxp.name(config)
504 dev_info = {}
506 if dev_type == 'pci':
507 pci_devs_uuid = sxp.child_value(config, 'uuid',
508 uuid.createString())
509 pci_devs = []
510 for pci_dev in sxp.children(config, 'dev'):
511 pci_dev_info = {}
512 for opt_val in pci_dev[1:]:
513 try:
514 opt, val = opt_val
515 pci_dev_info[opt] = val
516 except TypeError:
517 pass
518 pci_devs.append(pci_dev_info)
520 cfg['devices'][pci_devs_uuid] = (dev_type,
521 {'devs': pci_devs,
522 'uuid': pci_devs_uuid})
524 log.debug("XendConfig: reading device: %s" % pci_devs)
525 else:
526 self.device_add(dev_type, cfg_sxp = config, target = cfg)
527 log.debug("XendConfig: reading device: %s" % scrub_password(dev_info))
529 # Extract missing data from configuration entries
530 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
531 if image_sxp:
532 image_vcpus = sxp.child_value(image_sxp, 'vcpus')
533 if image_vcpus != None:
534 try:
535 if 'vcpus_number' not in cfg:
536 cfg['vcpus_number'] = int(image_vcpus)
537 elif cfg['vcpus_number'] != int(image_vcpus):
538 cfg['vcpus_number'] = int(image_vcpus)
539 log.warn('Overriding vcpus from %d to %d using image'
540 'vcpus value.', cfg['vcpus_number'])
541 except ValueError, e:
542 raise XendConfigError('integer expeceted: %s: %s' %
543 image_sxp, e)
545 # Deprecated cpu configuration
546 if 'cpu' in cfg:
547 if 'cpus' in cfg:
548 cfg['cpus'] = "%s,%s" % (str(cfg['cpu']), cfg['cpus'])
549 else:
550 cfg['cpus'] = str(cfg['cpu'])
552 # convert 'cpus' string to list of ints
553 # 'cpus' supports a list of ranges (0-3), seperated by
554 # commas, and negation, (^1).
555 # Precedence is settled by order of the string:
556 # "0-3,^1" -> [0,2,3]
557 # "0-3,^1,1" -> [0,1,2,3]
558 try:
559 if 'cpus' in cfg:
560 cpus = []
561 for c in cfg['cpus'].split(','):
562 if c.find('-') != -1:
563 (x, y) = c.split('-')
564 for i in range(int(x), int(y)+1):
565 cpus.append(int(i))
566 else:
567 # remove this element from the list
568 if c[0] == '^':
569 cpus = [x for x in cpus if x != int(c[1:])]
570 else:
571 cpus.append(int(c))
573 cfg['cpus'] = cpus
574 except ValueError, e:
575 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
577 if 'security' in cfg and isinstance(cfg['security'], str):
578 cfg['security'] = sxp.from_string(cfg['security'])
580 old_state = sxp.child_value(sxp_cfg, 'state')
581 if old_state:
582 for i in range(len(CONFIG_OLD_DOM_STATES)):
583 cfg[CONFIG_OLD_DOM_STATES[i]] = int(old_state[i] != '-')
585 return cfg
588 def _sxp_to_xapi(self, sxp_cfg):
589 """Read in an SXP Configuration object and
590 populate at much of the Xen API with valid values.
591 """
592 log.debug('_sxp_to_xapi(%s)' % scrub_password(sxp_cfg))
594 cfg = self._parse_sxp(sxp_cfg)
596 for key, typ in XENAPI_CFG_TYPES.items():
597 val = cfg.get(key)
598 if val is not None:
599 self[key] = typ(val)
601 # Convert parameters that can be directly mapped from
602 # the Legacy Config to Xen API Config
604 for apikey, cfgkey in XENAPI_CFG_TO_LEGACY_CFG.items():
605 try:
606 type_conv = XENAPI_CFG_TYPES.get(apikey)
607 if callable(type_conv):
608 self[apikey] = type_conv(cfg[cfgkey])
609 else:
610 log.warn("Unconverted key: " + apikey)
611 self[apikey] = cfg[cfgkey]
612 except KeyError:
613 pass
615 def update_with(n, o):
616 if not self.get(n):
617 self[n] = cfg.get(o, '')
619 update_with('PV_bootloader', 'bootloader')
620 update_with('PV_bootloader_args', 'bootloader_args')
622 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
623 if image_sxp:
624 self.update_with_image_sxp(image_sxp)
626 # Convert Legacy HVM parameters to Xen API configuration
627 self['platform_std_vga'] = bool0(cfg.get('stdvga', 0))
628 self['platform_serial'] = str(cfg.get('serial', ''))
629 self['platform_localtime'] = bool0(cfg.get('localtime', 0))
630 self['platform_enable_audio'] = bool0(cfg.get('soundhw', 0))
632 # make sure a sane maximum is set
633 if self['memory_static_max'] <= 0:
634 self['memory_static_max'] = self['memory_static_min']
636 self['memory_dynamic_max'] = self['memory_static_max']
637 self['memory_dynamic_min'] = self['memory_static_min']
639 # make sure max_vcpu_id is set correctly
640 self['max_vcpu_id'] = self['vcpus_number'] - 1
642 # set device references in the configuration
643 self['devices'] = cfg.get('devices', {})
645 self['console_refs'] = []
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 log.debug('_sxp_to_xapi_unsupported(%s)' % scrub_password(sxp_cfg))
666 # Parse and convert parameters used to configure
667 # the image (as well as HVM images)
668 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
669 if image_sxp:
670 image = {}
671 image['type'] = sxp.name(image_sxp)
672 for arg, conv in LEGACY_IMAGE_CFG:
673 val = sxp.child_value(image_sxp, arg, None)
674 if val != None:
675 image[arg] = conv(val)
677 image_hvm = {}
678 for arg, conv in LEGACY_IMAGE_HVM_CFG:
679 val = sxp.child_value(image_sxp, arg, None)
680 if val != None:
681 image_hvm[arg] = conv(val)
683 image_hvm_devices = {}
684 for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
685 val = sxp.child_value(image_sxp, arg, None)
686 if val != None:
687 image_hvm_devices[arg] = conv(val)
689 if image_hvm or image_hvm_devices:
690 image['hvm'] = image_hvm
691 image['hvm']['devices'] = image_hvm_devices
693 self['image'] = image
695 for apikey, imgkey in XENAPI_HVM_CFG.items():
696 val = sxp.child_value(image_sxp, imgkey, None)
697 if val != None:
698 self[apikey] = val
700 # extract backend value
702 backend = []
703 for c in sxp.children(sxp_cfg, 'backend'):
704 backend.append(sxp.name(sxp.child0(c)))
705 if backend:
706 self['backend'] = backend
708 # Parse and convert other Non Xen API parameters.
709 def _set_cfg_if_exists(sxp_arg):
710 val = sxp.child_value(sxp_cfg, sxp_arg)
711 if val != None:
712 if LEGACY_CFG_TYPES.get(sxp_arg):
713 self[sxp_arg] = LEGACY_CFG_TYPES[sxp_arg](val)
714 else:
715 self[sxp_arg] = val
717 _set_cfg_if_exists('shadow_memory')
718 _set_cfg_if_exists('security')
719 _set_cfg_if_exists('features')
720 _set_cfg_if_exists('on_xend_stop')
721 _set_cfg_if_exists('on_xend_start')
722 _set_cfg_if_exists('vcpu_avail')
723 _set_cfg_if_exists('max_vcpu_id') # needed for vcpuDomDetails
724 _set_cfg_if_exists('cpu_weight')
725 _set_cfg_if_exists('cpu_cap')
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, xapi_dict):
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 if 'image' in xapi_dict:
743 self['image'].update(xapi_dict['image'])
744 else:
745 hvm = self['HVM_boot'] != ''
746 self['image']['type'] = hvm and 'hvm' or 'linux'
747 if hvm:
748 self['image']['hvm'] = {}
749 for xapi, cfgapi in XENAPI_HVM_CFG.items():
750 self['image']['hvm'][cfgapi] = self[xapi]
753 def _get_old_state_string(self):
754 """Returns the old xm state string.
755 @rtype: string
756 @return: old state string
757 """
758 state_string = ''
759 for state_name in CONFIG_OLD_DOM_STATES:
760 on_off = self.get(state_name, 0)
761 if on_off:
762 state_string += state_name[0]
763 else:
764 state_string += '-'
766 return state_string
769 def update_config(self, dominfo):
770 """Update configuration with the output from xc.domain_getinfo().
772 @param dominfo: Domain information via xc.domain_getinfo()
773 @type dominfo: dict
774 """
775 self._dominfo_to_xapi(dominfo)
776 self.validate()
778 def update_with_xenapi_config(self, xapi):
779 """Update configuration with a Xen API VM struct
781 @param xapi: Xen API VM Struct
782 @type xapi: dict
783 """
785 log.debug('update_with_xenapi_config: %s' % scrub_password(xapi))
787 for key, val in xapi.items():
788 type_conv = XENAPI_CFG_TYPES.get(key)
789 if type_conv is None:
790 key = key.lower()
791 type_conv = XENAPI_CFG_TYPES.get(key)
792 if callable(type_conv):
793 self[key] = type_conv(val)
794 else:
795 self[key] = val
797 self.validate()
799 def to_sxp(self, domain = None, ignore_devices = False, ignore = [],
800 legacy_only = True):
801 """ Get SXP representation of this config object.
803 Incompat: removed store_mfn, console_mfn
805 @keyword domain: (optional) XendDomainInfo to get extra information
806 from such as domid and running devices.
807 @type domain: XendDomainInfo
808 @keyword ignore: (optional) list of 'keys' that we do not want
809 to export.
810 @type ignore: list of strings
811 @rtype: list of list (SXP representation)
812 """
813 sxpr = ['domain']
815 # TODO: domid/dom is the same thing but called differently
816 # depending if it is from xenstore or sxpr.
818 if domain.getDomid() is not None:
819 sxpr.append(['domid', domain.getDomid()])
821 if not legacy_only:
822 for name in XENAPI_CFG_TYPES.keys():
823 if name in self and self[name] not in (None, []):
824 sxpr.append([name, str(self[name])])
826 for xenapi, legacy in XENAPI_CFG_TO_LEGACY_CFG.items():
827 if self.has_key(xenapi) and self[xenapi] not in (None, []):
828 if type(self[xenapi]) == bool:
829 # convert booleans to ints before making an sxp item
830 sxpr.append([legacy, int(self[xenapi])])
831 else:
832 sxpr.append([legacy, self[xenapi]])
834 for legacy in LEGACY_UNSUPPORTED_BY_XENAPI_CFG:
835 if legacy in ('domid', 'uuid'): # skip these
836 continue
837 if self.has_key(legacy) and self[legacy] not in (None, []):
838 sxpr.append([legacy, self[legacy]])
840 if 'image' in self and self['image']:
841 sxpr.append(['image', self.image_sxpr()])
843 sxpr.append(['status', domain.state])
844 sxpr.append(['memory_dynamic_min', self.get('memory_dynamic_min')])
845 sxpr.append(['memory_dynamic_max', self.get('memory_dynamic_max')])
847 if domain.getDomid() is not None:
848 sxpr.append(['state', self._get_old_state_string()])
850 if domain:
851 if domain.store_mfn:
852 sxpr.append(['store_mfn', domain.store_mfn])
853 if domain.console_mfn:
854 sxpr.append(['console_mfn', domain.console_mfn])
857 # Marshall devices (running or from configuration)
858 if not ignore_devices:
859 for cls in XendDevices.valid_devices():
860 found = False
862 # figure if there is a dev controller is valid and running
863 if domain and domain.getDomid() != None:
864 try:
865 controller = domain.getDeviceController(cls)
866 configs = controller.configurations()
867 for config in configs:
868 sxpr.append(['device', config])
870 found = True
871 except:
872 log.exception("dumping sxp from device controllers")
873 pass
875 # if we didn't find that device, check the existing config
876 # for a device in the same class
877 if not found:
878 for dev_type, dev_info in self.all_devices_sxpr():
879 if dev_type == cls:
880 sxpr.append(['device', dev_info])
882 return sxpr
884 def device_add(self, dev_type, cfg_sxp = None, cfg_xenapi = None,
885 target = None):
886 """Add a device configuration in SXP format or XenAPI struct format.
888 For SXP, it could be either:
890 [device, [vbd, [uname ...]]
892 or:
894 [vbd, [uname ..]]
896 @type cfg_sxp: list of lists (parsed sxp object)
897 @param cfg_sxp: SXP configuration object
898 @type cfg_xenapi: dict
899 @param cfg_xenapi: A device configuration from Xen API (eg. vbd,vif)
900 @param target: write device information to
901 @type target: None or a dictionary
902 @rtype: string
903 @return: Assigned UUID of the device.
904 """
905 if target == None:
906 target = self
908 if dev_type not in XendDevices.valid_devices() and \
909 dev_type not in XendDevices.pseudo_devices():
910 raise XendConfigError("XendConfig: %s not a valid device type" %
911 dev_type)
913 if cfg_sxp == None and cfg_xenapi == None:
914 raise XendConfigError("XendConfig: device_add requires some "
915 "config.")
917 if cfg_sxp:
918 log.debug("XendConfig.device_add: %s" % str(cfg_sxp))
919 if cfg_xenapi:
920 log.debug("XendConfig.device_add: %s" % str(cfg_xenapi))
922 if cfg_sxp:
923 if sxp.child0(cfg_sxp) == 'device':
924 config = sxp.child0(cfg_sxp)
925 else:
926 config = cfg_sxp
928 dev_type = sxp.name(config)
929 dev_info = {}
931 for opt_val in config[1:]:
932 try:
933 opt, val = opt_val
934 dev_info[opt] = val
935 except (TypeError, ValueError): # unpack error
936 pass
938 if dev_type == 'vbd':
939 if dev_info.get('dev', '').startswith('ioemu:'):
940 dev_info['driver'] = 'ioemu'
941 else:
942 dev_info['driver'] = 'paravirtualised'
944 # create uuid if it doesn't exist
945 dev_uuid = dev_info.get('uuid', uuid.createString())
946 dev_info['uuid'] = dev_uuid
948 # store dev references by uuid for certain device types
949 target['devices'][dev_uuid] = (dev_type, dev_info)
950 if dev_type in ('vif', 'vbd', 'vtpm'):
951 param = '%s_refs' % dev_type
952 if param not in target:
953 target[param] = []
954 if dev_uuid not in target[param]:
955 target[param].append(dev_uuid)
956 elif dev_type in ('tap',):
957 if 'vbd_refs' not in target:
958 target['vbd_refs'] = []
959 if dev_uuid not in target['vbd_refs']:
960 target['vbd_refs'].append(dev_uuid)
962 return dev_uuid
964 if cfg_xenapi:
965 dev_info = {}
966 if dev_type == 'vif':
967 if cfg_xenapi.get('MAC'): # don't add if blank
968 dev_info['mac'] = cfg_xenapi.get('MAC')
969 # vifname is the name on the guest, not dom0
970 # TODO: we don't have the ability to find that out or
971 # change it from dom0
972 #if cfg_xenapi.get('device'): # don't add if blank
973 # dev_info['vifname'] = cfg_xenapi.get('device')
974 if cfg_xenapi.get('type'):
975 dev_info['type'] = cfg_xenapi.get('type')
977 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
978 dev_info['uuid'] = dev_uuid
979 self['devices'][dev_uuid] = (dev_type, dev_info)
980 self['vif_refs'].append(dev_uuid)
981 return dev_uuid
983 elif dev_type in ('vbd', 'tap'):
984 if dev_type == 'vbd':
985 dev_info['uname'] = cfg_xenapi.get('image', '')
986 dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
987 elif dev_type == 'tap':
988 dev_info['uname'] = 'tap:qcow:%s' % cfg_xenapi.get('image')
989 dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
991 dev_info['driver'] = cfg_xenapi.get('driver')
992 dev_info['VDI'] = cfg_xenapi.get('VDI', '')
994 if cfg_xenapi.get('mode') == 'RW':
995 dev_info['mode'] = 'w'
996 else:
997 dev_info['mode'] = 'r'
999 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
1000 dev_info['uuid'] = dev_uuid
1001 self['devices'][dev_uuid] = (dev_type, dev_info)
1002 self['vbd_refs'].append(dev_uuid)
1003 return dev_uuid
1005 elif dev_type == 'vtpm':
1006 if cfg_xenapi.get('type'):
1007 dev_info['type'] = cfg_xenapi.get('type')
1009 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
1010 dev_info['uuid'] = dev_uuid
1011 self['devices'][dev_uuid] = (dev_type, dev_info)
1012 self['vtpm_refs'].append(dev_uuid)
1013 return dev_uuid
1015 return ''
1017 def device_update(self, dev_uuid, cfg_sxp):
1018 """Update an existing device with the new configuration.
1020 @rtype: boolean
1021 @return: Returns True if succesfully found and updated a device conf
1022 """
1023 if dev_uuid in self['devices']:
1024 if sxp.child0(cfg_sxp) == 'device':
1025 config = sxp.child0(cfg_sxp)
1026 else:
1027 config = cfg_sxp
1029 for opt_val in config[1:]:
1030 try:
1031 opt, val = opt_val
1032 self['devices'][dev_uuid][opt] = val
1033 except (TypeError, ValueError):
1034 pass # no value for this config option
1036 return True
1038 return False
1041 def device_sxpr(self, dev_uuid = None, dev_type = None, dev_info = None):
1042 """Get Device SXPR by either giving the device UUID or (type, config).
1044 @rtype: list of lists
1045 @return: device config sxpr
1046 """
1047 sxpr = []
1048 if dev_uuid != None and dev_uuid in self['devices']:
1049 dev_type, dev_info = self['devices'][dev_uuid]
1051 if dev_type == None or dev_info == None:
1052 raise XendConfigError("Required either UUID or device type and "
1053 "configuration dictionary.")
1055 sxpr.append(dev_type)
1056 config = [(opt, val) for opt, val in dev_info.items()]
1057 sxpr += config
1059 return sxpr
1061 def all_devices_sxpr(self):
1062 """Returns the SXPR for all devices in the current configuration."""
1063 sxprs = []
1064 pci_devs = []
1066 if 'devices' not in self:
1067 return sxprs
1069 for dev_type, dev_info in self['devices'].values():
1070 if dev_type == 'pci': # special case for pci devices
1071 sxpr = [['uuid', dev_info['uuid']]]
1072 for pci_dev_info in dev_info['devs']:
1073 pci_dev_sxpr = ['dev']
1074 for opt, val in pci_dev_info.items():
1075 pci_dev_sxpr.append([opt, val])
1076 sxpr.append(pci_dev_sxpr)
1077 sxprs.append((dev_type, sxpr))
1078 else:
1079 sxpr = self.device_sxpr(dev_type = dev_type,
1080 dev_info = dev_info)
1081 sxprs.append((dev_type, sxpr))
1083 return sxprs
1085 def image_sxpr(self):
1086 """Returns a backwards compatible image SXP expression that is
1087 used in xenstore's /vm/<uuid>/image value and xm list."""
1088 image = [self['image'].get('type', 'linux')]
1089 if self.has_key('PV_kernel'):
1090 image.append(['kernel', self['PV_kernel']])
1091 if self.has_key('PV_ramdisk') and self['PV_ramdisk']:
1092 image.append(['ramdisk', self['PV_ramdisk']])
1093 if self.has_key('PV_args') and self['PV_args']:
1094 image.append(['args', self['PV_args']])
1096 for arg, conv in LEGACY_IMAGE_CFG:
1097 if self['image'].has_key(arg):
1098 image.append([arg, self['image'][arg]])
1100 if 'hvm' in self['image']:
1101 for arg, conv in LEGACY_IMAGE_HVM_CFG:
1102 if self['image']['hvm'].get(arg):
1103 image.append([arg, self['image']['hvm'][arg]])
1105 if 'hvm' in self['image'] and 'devices' in self['image']['hvm']:
1106 for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
1107 if self['image']['hvm']['devices'].get(arg):
1108 image.append([arg,
1109 self['image']['hvm']['devices'][arg]])
1111 return image
1113 def update_with_image_sxp(self, image_sxp, bootloader = False):
1114 # Convert Legacy "image" config to Xen API PV_*
1115 # configuration
1116 log.debug("update_with_image_sxp(%s)" % scrub_password(image_sxp))
1118 kernel_args = ""
1120 # attempt to extract extra arguments from SXP config
1121 arg_ip = sxp.child_value(image_sxp, 'ip')
1122 if arg_ip and not re.search(r'ip=[^ ]+', kernel_args):
1123 kernel_args += 'ip=%s ' % arg_ip
1124 arg_root = sxp.child_value(image_sxp, 'root')
1125 if arg_root and not re.search(r'root=', kernel_args):
1126 kernel_args += 'root=%s ' % arg_root
1128 # user-specified args must come last: previous releases did this and
1129 # some domU kernels rely upon the ordering.
1130 kernel_args += sxp.child_value(image_sxp, 'args', '')
1132 if bootloader:
1133 self['_temp_using_bootloader'] = '1'
1134 self['_temp_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1135 self['_temp_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1136 self['_temp_args'] = kernel_args
1137 else:
1138 self['PV_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1139 self['PV_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1140 self['PV_args'] = kernel_args
1142 # Store image SXP in python dictionary format
1143 image = {}
1144 image['type'] = sxp.name(image_sxp)
1145 for arg, conv in LEGACY_IMAGE_CFG:
1146 val = sxp.child_value(image_sxp, arg, None)
1147 if val != None:
1148 image[arg] = conv(val)
1150 image_hvm = {}
1151 for arg, conv in LEGACY_IMAGE_HVM_CFG:
1152 val = sxp.child_value(image_sxp, arg, None)
1153 if val != None:
1154 image_hvm[arg] = conv(val)
1156 image_hvm_devices = {}
1157 for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
1158 val = sxp.child_value(image_sxp, arg, None)
1159 if val != None:
1160 image_hvm_devices[arg] = conv(val)
1162 if image_hvm or image_hvm_devices:
1163 image['hvm'] = image_hvm
1164 image['hvm']['devices'] = image_hvm_devices
1166 self['image'] = image
1168 for apikey, imgkey in XENAPI_HVM_CFG.items():
1169 val = sxp.child_value(image_sxp, imgkey, None)
1170 if val != None:
1171 type_conv = XENAPI_CFG_TYPES[apikey]
1172 if callable(conv):
1173 self[apikey] = type_conv(val)
1174 else:
1175 self[apikey] = val
1179 # debugging
1182 if __name__ == "__main__":
1183 pass