ia64/xen-unstable

view tools/python/xen/xend/XendConfig.py @ 13153:5c46e27e3915

[XEND] Add cpu_weight and cpu_cap parsing for SXP config create

Reported by Masaki Kanno on :
http://bugzilla.xensource.com/bugzilla/show_bug.cgi?id=835

Signed-off-by: Alastair Tse <atse@xensource.com>
author Alastair Tse <atse@xensource.com>
date Thu Dec 21 12:08:27 2006 +0000 (2006-12-21)
parents 057f7c4dbed1
children 8c6e20c60563
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 }
98 LEGACY_CFG_TO_XENAPI_CFG = reverse_dict(XENAPI_CFG_TO_LEGACY_CFG)
100 # Mapping from XendConfig configuration keys to the old
101 # legacy configuration keys that are found in the 'image'
102 # SXP object.
103 XENAPI_HVM_CFG = {
104 'platform_std_vga': 'stdvga',
105 'platform_serial' : 'serial',
106 'platform_localtime': 'localtime',
107 'platform_enable_audio': 'soundhw',
108 'platform_keymap' : 'keymap',
109 }
111 # List of XendConfig configuration keys that have no direct equivalent
112 # in the old world.
114 XENAPI_CFG_TYPES = {
115 'uuid': str,
116 'power_state': str,
117 'name_label': str,
118 'name_description': str,
119 'user_version': str,
120 'is_a_template': bool0,
121 'resident_on': str,
122 'memory_static_min': int,
123 'memory_static_max': int,
124 'memory_dynamic_min': int,
125 'memory_dynamic_max': int,
126 'memory_actual': int,
127 'vcpus_policy': str,
128 'vcpus_params': str,
129 'vcpus_number': int,
130 'vcpus_features_required': list,
131 'vcpus_features_can_use': list,
132 'vcpus_features_force_on': list,
133 'vcpus_features_force_off': list,
134 'actions_after_shutdown': str,
135 'actions_after_reboot': str,
136 'actions_after_suspend': str,
137 'actions_after_crash': str,
138 'tpm_instance': int,
139 'tpm_backend': int,
140 'PV_bootloader': str,
141 'PV_kernel': str,
142 'PV_initrd': str,
143 'PV_args': str,
144 'PV_bootloader_args': str,
145 'HVM_boot': str,
146 'platform_std_vga': bool0,
147 'platform_serial': str,
148 'platform_localtime': bool0,
149 'platform_clock_offset': bool0,
150 'platform_enable_audio': bool0,
151 'platform_keymap': str,
152 'pci_bus': str,
153 'tools_version': dict,
154 'otherconfig': dict,
155 }
157 # List of legacy configuration keys that have no equivalent in the
158 # Xen API, but are still stored in XendConfig.
160 LEGACY_UNSUPPORTED_BY_XENAPI_CFG = [
161 # roundtripped (dynamic, unmodified)
162 'shadow_memory',
163 'security',
164 'vcpu_avail',
165 'cpu_weight',
166 'cpu_cap',
167 'features',
168 # read/write
169 'on_xend_start',
170 'on_xend_stop',
171 # read-only
172 'domid',
173 'start_time',
174 'cpu_time',
175 'online_vcpus',
176 # write-once
177 'cpu',
178 'cpus',
179 ]
181 LEGACY_CFG_TYPES = {
182 'uuid': str,
183 'name': str,
184 'vcpus': int,
185 'vcpu_avail': int,
186 'memory': int,
187 'shadow_memory': int,
188 'maxmem': int,
189 'start_time': float,
190 'cpu_cap': int,
191 'cpu_weight': int,
192 'cpu_time': float,
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 'PV_bootloader': '',
335 'PV_kernel': '',
336 'PV_ramdisk': '',
337 'PV_args': '',
338 'PV_bootloader_args': '',
339 'HVM_boot': '',
340 'memory_static_min': 0,
341 'memory_dynamic_min': 0,
342 'shadow_memory': 0,
343 'memory_static_max': 0,
344 'memory_dynamic_max': 0,
345 'memory_actual': 0,
346 'devices': {},
347 'image': {},
348 'security': None,
349 'on_xend_start': 'ignore',
350 'on_xend_stop': 'ignore',
351 'cpus': [],
352 'cpu_weight': 256,
353 'cpu_cap': 0,
354 'vcpus_number': 1,
355 'online_vcpus': 1,
356 'max_vcpu_id': 0,
357 'vcpu_avail': 1,
358 'console_refs': [],
359 'vif_refs': [],
360 'vbd_refs': [],
361 'vtpm_refs': [],
362 }
364 defaults['name_label'] = 'Domain-' + defaults['uuid']
365 return defaults
367 def _memory_sanity_check(self):
368 if self['memory_static_min'] == 0:
369 self['memory_static_min'] = self['memory_dynamic_min']
371 # If the static max is not set, let's set it to dynamic max.
372 # If the static max is smaller than static min, then fix it!
373 self['memory_static_max'] = max(self['memory_static_max'],
374 self['memory_dynamic_max'],
375 self['memory_static_min'])
377 for mem_type in ('memory_static_min', 'memory_static_max'):
378 if self[mem_type] <= 0:
379 raise XendConfigError('Memory value too low for %s: %d' %
380 (mem_type, self[mem_type]))
382 def _actions_sanity_check(self):
383 for event in ['shutdown', 'reboot', 'crash']:
384 if self['actions_after_' + event] not in CONFIG_RESTART_MODES:
385 raise XendConfigError('Invalid event handling mode: ' +
386 event)
388 def _vcpus_sanity_check(self):
389 if self.get('vcpus_number') != None:
390 self['vcpu_avail'] = (1 << self['vcpus_number']) - 1
392 def _uuid_sanity_check(self):
393 """Make sure UUID is in proper string format with hyphens."""
394 self['uuid'] = uuid.toString(uuid.fromString(self['uuid']))
396 def validate(self):
397 self._memory_sanity_check()
398 self._actions_sanity_check()
399 self._vcpus_sanity_check()
400 self._uuid_sanity_check()
402 def _dominfo_to_xapi(self, dominfo):
403 self['domid'] = dominfo['domid']
404 self['online_vcpus'] = dominfo['online_vcpus']
405 self['max_vcpu_id'] = dominfo['max_vcpu_id']
406 self['memory_dynamic_min'] = (dominfo['mem_kb'] + 1023)/1024
407 self['memory_dynamic_max'] = (dominfo['maxmem_kb'] + 1023)/1024
408 self['cpu_time'] = dominfo['cpu_time']/1e9
409 # TODO: i don't know what the security stuff expects here
410 if dominfo.get('ssidref'):
411 self['security'] = [['ssidref', dominfo['ssidref']]]
412 self['shutdown_reason'] = dominfo['shutdown_reason']
414 # parse state into Xen API states
415 self['running'] = dominfo['running']
416 self['crashed'] = dominfo['crashed']
417 self['dying'] = dominfo['dying']
418 self['shutdown'] = dominfo['shutdown']
419 self['paused'] = dominfo['paused']
420 self['blocked'] = dominfo['blocked']
422 if 'name' in dominfo:
423 self['name_label'] = dominfo['name']
425 if 'handle' in dominfo:
426 self['uuid'] = uuid.toString(dominfo['handle'])
428 def _parse_sxp(self, sxp_cfg):
429 """ Populate this XendConfig using the parsed SXP.
431 @param sxp_cfg: Parsed SXP Configuration
432 @type sxp_cfg: list of lists
433 @rtype: dictionary
434 @return: A dictionary containing the parsed options of the SXP.
435 """
436 cfg = {}
438 for key, typ in XENAPI_CFG_TYPES.items():
439 val = sxp.child_value(sxp_cfg, key)
440 if val is not None:
441 cfg[key] = typ(val)
443 # Convert deprecated options to current equivalents.
445 restart = sxp.child_value(sxp_cfg, 'restart')
446 if restart:
447 if restart == 'onreboot':
448 cfg['on_poweroff'] = 'destroy'
449 cfg['on_reboot'] = 'restart'
450 cfg['on_crash'] = 'destroy'
451 elif restart == 'always':
452 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
453 cfg[opt] = 'restart'
454 elif restart == 'never':
455 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
456 cfg[opt] = 'never'
457 else:
458 log.warn('Ignoring unrecognised value for deprecated option:'
459 'restart = \'%s\'', restart)
461 # Only extract options we know about.
462 extract_keys = LEGACY_UNSUPPORTED_BY_XENAPI_CFG
463 extract_keys += XENAPI_CFG_TO_LEGACY_CFG.values()
465 for key in extract_keys:
466 val = sxp.child_value(sxp_cfg, key)
467 if val != None:
468 try:
469 cfg[key] = LEGACY_CFG_TYPES[key](val)
470 except KeyError:
471 cfg[key] = val
472 except (TypeError, ValueError), e:
473 log.warn("Unable to parse key %s: %s: %s" %
474 (key, str(val), e))
476 # Parsing the device SXP's. In most cases, the SXP looks
477 # like this:
478 #
479 # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]]
480 #
481 # However, for PCI devices it looks like this:
482 #
483 # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1]]]]
484 #
485 # It seems the reasoning for this difference is because
486 # pciif.py needs all the PCI device configurations at
487 # the same time when creating the devices.
488 #
489 # To further complicate matters, Xen 2.0 configuration format
490 # uses the following for pci device configuration:
491 #
492 # [device, [pci, [domain, 0], [bus, 0], [dev, 1], [func, 2]]]
493 #
494 # Hence we deal with pci device configurations outside of
495 # the regular device parsing.
497 cfg['devices'] = {}
498 for dev in sxp.children(sxp_cfg, 'device'):
499 config = sxp.child0(dev)
500 dev_type = sxp.name(config)
501 dev_info = {}
503 if dev_type == 'pci':
504 pci_devs_uuid = sxp.child_value(config, 'uuid',
505 uuid.createString())
506 pci_devs = []
507 for pci_dev in sxp.children(config, 'dev'):
508 pci_dev_info = {}
509 for opt, val in pci_dev[1:]:
510 pci_dev_info[opt] = val
511 pci_devs.append(pci_dev_info)
513 cfg['devices'][pci_devs_uuid] = (dev_type,
514 {'devs': pci_devs,
515 'uuid': pci_devs_uuid})
517 log.debug("XendConfig: reading device: %s" % pci_devs)
518 else:
519 self.device_add(dev_type, cfg_sxp = config, target = cfg)
520 log.debug("XendConfig: reading device: %s" % scrub_password(dev_info))
522 # Extract missing data from configuration entries
523 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
524 if image_sxp:
525 image_vcpus = sxp.child_value(image_sxp, 'vcpus')
526 if image_vcpus != None:
527 try:
528 if 'vcpus_number' not in cfg:
529 cfg['vcpus_number'] = int(image_vcpus)
530 elif cfg['vcpus_number'] != int(image_vcpus):
531 cfg['vcpus_number'] = int(image_vcpus)
532 log.warn('Overriding vcpus from %d to %d using image'
533 'vcpus value.', cfg['vcpus_number'])
534 except ValueError, e:
535 raise XendConfigError('integer expeceted: %s: %s' %
536 image_sxp, e)
538 # Deprecated cpu configuration
539 if 'cpu' in cfg:
540 if 'cpus' in cfg:
541 cfg['cpus'] = "%s,%s" % (str(cfg['cpu']), cfg['cpus'])
542 else:
543 cfg['cpus'] = str(cfg['cpu'])
545 # convert 'cpus' string to list of ints
546 # 'cpus' supports a list of ranges (0-3), seperated by
547 # commas, and negation, (^1).
548 # Precedence is settled by order of the string:
549 # "0-3,^1" -> [0,2,3]
550 # "0-3,^1,1" -> [0,1,2,3]
551 try:
552 if 'cpus' in cfg:
553 cpus = []
554 for c in cfg['cpus'].split(','):
555 if c.find('-') != -1:
556 (x, y) = c.split('-')
557 for i in range(int(x), int(y)+1):
558 cpus.append(int(i))
559 else:
560 # remove this element from the list
561 if c[0] == '^':
562 cpus = [x for x in cpus if x != int(c[1:])]
563 else:
564 cpus.append(int(c))
566 cfg['cpus'] = cpus
567 except ValueError, e:
568 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
570 if 'security' in cfg and isinstance(cfg['security'], str):
571 cfg['security'] = sxp.from_string(cfg['security'])
573 # TODO: get states
574 old_state = sxp.child_value(sxp_cfg, 'state')
575 if old_state:
576 for i in range(len(CONFIG_OLD_DOM_STATES)):
577 cfg[CONFIG_OLD_DOM_STATES[i]] = int(old_state[i] != '-')
579 return cfg
582 def _sxp_to_xapi(self, sxp_cfg):
583 """Read in an SXP Configuration object and
584 populate at much of the Xen API with valid values.
585 """
586 log.debug('_sxp_to_xapi(%s)' % scrub_password(sxp_cfg))
588 cfg = self._parse_sxp(sxp_cfg)
590 for key, typ in XENAPI_CFG_TYPES.items():
591 val = cfg.get(key)
592 if val is not None:
593 self[key] = typ(val)
595 # Convert parameters that can be directly mapped from
596 # the Legacy Config to Xen API Config
598 for apikey, cfgkey in XENAPI_CFG_TO_LEGACY_CFG.items():
599 try:
600 type_conv = XENAPI_CFG_TYPES.get(apikey)
601 if callable(type_conv):
602 self[apikey] = type_conv(cfg[cfgkey])
603 else:
604 log.warn("Unconverted key: " + apikey)
605 self[apikey] = cfg[cfgkey]
606 except KeyError:
607 pass
609 def update_with(n, o):
610 if not self.get(n):
611 self[n] = cfg.get(o, '')
613 update_with('PV_bootloader', 'bootloader')
614 update_with('PV_bootloader_args', 'bootloader_args')
616 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
617 if image_sxp:
618 self.update_with_image_sxp(image_sxp)
620 # Convert Legacy HVM parameters to Xen API configuration
621 self['platform_std_vga'] = bool0(cfg.get('stdvga', 0))
622 self['platform_serial'] = str(cfg.get('serial', ''))
623 self['platform_localtime'] = bool0(cfg.get('localtime', 0))
624 self['platform_enable_audio'] = bool0(cfg.get('soundhw', 0))
626 # make sure a sane maximum is set
627 if self['memory_static_max'] <= 0:
628 self['memory_static_max'] = self['memory_static_min']
630 self['memory_dynamic_max'] = self['memory_static_max']
631 self['memory_dynamic_min'] = self['memory_static_min']
633 # make sure max_vcpu_id is set correctly
634 self['max_vcpu_id'] = self['vcpus_number'] - 1
636 # set device references in the configuration
637 self['devices'] = cfg.get('devices', {})
639 self['console_refs'] = []
640 self['vif_refs'] = []
641 self['vbd_refs'] = []
642 self['vtpm_refs'] = []
643 for dev_uuid, (dev_type, dev_info) in self['devices'].items():
644 if dev_type == 'vif':
645 self['vif_refs'].append(dev_uuid)
646 elif dev_type in ('vbd','tap'):
647 self['vbd_refs'].append(dev_uuid)
648 elif dev_type in ('vtpm',):
649 self['vtpm_refs'].append(dev_uuid)
652 def _sxp_to_xapi_unsupported(self, sxp_cfg):
653 """Read in an SXP configuration object and populate
654 values are that not related directly supported in
655 the Xen API.
656 """
658 log.debug('_sxp_to_xapi_unsupported(%s)' % scrub_password(sxp_cfg))
660 # Parse and convert parameters used to configure
661 # the image (as well as HVM images)
662 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
663 if image_sxp:
664 image = {}
665 image['type'] = sxp.name(image_sxp)
666 for arg, conv in LEGACY_IMAGE_CFG:
667 val = sxp.child_value(image_sxp, arg, None)
668 if val != None:
669 image[arg] = conv(val)
671 image_hvm = {}
672 for arg, conv in LEGACY_IMAGE_HVM_CFG:
673 val = sxp.child_value(image_sxp, arg, None)
674 if val != None:
675 image_hvm[arg] = conv(val)
677 image_hvm_devices = {}
678 for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
679 val = sxp.child_value(image_sxp, arg, None)
680 if val != None:
681 image_hvm_devices[arg] = conv(val)
683 if image_hvm or image_hvm_devices:
684 image['hvm'] = image_hvm
685 image['hvm']['devices'] = image_hvm_devices
687 self['image'] = image
689 for apikey, imgkey in XENAPI_HVM_CFG.items():
690 val = sxp.child_value(image_sxp, imgkey, None)
691 if val != None:
692 self[apikey] = val
694 # extract backend value
696 backend = []
697 for c in sxp.children(sxp_cfg, 'backend'):
698 backend.append(sxp.name(sxp.child0(c)))
699 if backend:
700 self['backend'] = backend
702 # Parse and convert other Non Xen API parameters.
703 def _set_cfg_if_exists(sxp_arg):
704 val = sxp.child_value(sxp_cfg, sxp_arg)
705 if val != None:
706 if LEGACY_CFG_TYPES.get(sxp_arg):
707 self[sxp_arg] = LEGACY_CFG_TYPES[sxp_arg](val)
708 else:
709 self[sxp_arg] = val
711 _set_cfg_if_exists('shadow_memory')
712 _set_cfg_if_exists('security')
713 _set_cfg_if_exists('features')
714 _set_cfg_if_exists('on_xend_stop')
715 _set_cfg_if_exists('on_xend_start')
716 _set_cfg_if_exists('vcpu_avail')
717 _set_cfg_if_exists('max_vcpu_id') # needed for vcpuDomDetails
718 _set_cfg_if_exists('cpu_weight')
719 _set_cfg_if_exists('cpu_cap')
721 # Parse and store runtime configuration
722 _set_cfg_if_exists('start_time')
723 _set_cfg_if_exists('online_vcpus')
724 _set_cfg_if_exists('cpu_time')
725 _set_cfg_if_exists('shutdown_reason')
726 _set_cfg_if_exists('up_time')
727 _set_cfg_if_exists('status') # TODO, deprecated
729 def _add_xapi_unsupported(self):
730 """Updates the configuration object with entries that are not
731 officially supported by the Xen API but is required for
732 the rest of Xend to function.
733 """
735 # populate image
736 hvm = self['HVM_boot'] != ''
737 self['image']['type'] = hvm and 'hvm' or 'linux'
738 if hvm:
739 self['image']['hvm'] = {}
740 for xapi, cfgapi in XENAPI_HVM_CFG.items():
741 self['image']['hvm'][cfgapi] = self[xapi]
744 def _get_old_state_string(self):
745 """Returns the old xm state string.
746 @rtype: string
747 @return: old state string
748 """
749 state_string = ''
750 for state_name in CONFIG_OLD_DOM_STATES:
751 on_off = self.get(state_name, 0)
752 if on_off:
753 state_string += state_name[0]
754 else:
755 state_string += '-'
757 return state_string
760 def update_config(self, dominfo):
761 """Update configuration with the output from xc.domain_getinfo().
763 @param dominfo: Domain information via xc.domain_getinfo()
764 @type dominfo: dict
765 """
766 self._dominfo_to_xapi(dominfo)
767 self.validate()
769 def update_with_xenapi_config(self, xapi):
770 """Update configuration with a Xen API VM struct
772 @param xapi: Xen API VM Struct
773 @type xapi: dict
774 """
776 log.debug('update_with_xenapi_config: %s' % scrub_password(xapi))
778 for key, val in xapi.items():
779 type_conv = XENAPI_CFG_TYPES.get(key)
780 if type_conv is None:
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_sxp(self, domain = None, ignore_devices = False, ignore = [],
791 legacy_only = True):
792 """ Get SXP representation of this config object.
794 Incompat: removed store_mfn, console_mfn
796 @keyword domain: (optional) XendDomainInfo to get extra information
797 from such as domid and running devices.
798 @type domain: XendDomainInfo
799 @keyword ignore: (optional) list of 'keys' that we do not want
800 to export.
801 @type ignore: list of strings
802 @rtype: list of list (SXP representation)
803 """
804 sxpr = ['domain']
806 # TODO: domid/dom is the same thing but called differently
807 # depending if it is from xenstore or sxpr.
809 if domain.getDomid() is not None:
810 sxpr.append(['domid', domain.getDomid()])
812 if not legacy_only:
813 for name in XENAPI_CFG_TYPES.keys():
814 if name in self and self[name] not in (None, []):
815 sxpr.append([name, str(self[name])])
817 for xenapi, legacy in XENAPI_CFG_TO_LEGACY_CFG.items():
818 if self.has_key(xenapi) and self[xenapi] not in (None, []):
819 if type(self[xenapi]) == bool:
820 # convert booleans to ints before making an sxp item
821 sxpr.append([legacy, int(self[xenapi])])
822 else:
823 sxpr.append([legacy, self[xenapi]])
825 for legacy in LEGACY_UNSUPPORTED_BY_XENAPI_CFG:
826 if legacy in ('domid', 'uuid'): # skip these
827 continue
828 if self.has_key(legacy) and self[legacy] not in (None, []):
829 sxpr.append([legacy, self[legacy]])
831 if 'image' in self and self['image']:
832 sxpr.append(['image', self.image_sxpr()])
834 sxpr.append(['status', domain.state])
835 sxpr.append(['memory_dynamic_min', self.get('memory_dynamic_min')])
836 sxpr.append(['memory_dynamic_max', self.get('memory_dynamic_max')])
838 if domain.getDomid() is not None:
839 sxpr.append(['state', self._get_old_state_string()])
841 if domain:
842 if domain.store_mfn:
843 sxpr.append(['store_mfn', domain.store_mfn])
844 if domain.console_mfn:
845 sxpr.append(['console_mfn', domain.console_mfn])
848 # Marshall devices (running or from configuration)
849 if not ignore_devices:
850 for cls in XendDevices.valid_devices():
851 found = False
853 # figure if there is a device that is running
854 if domain:
855 try:
856 controller = domain.getDeviceController(cls)
857 configs = controller.configurations()
858 for config in configs:
859 sxpr.append(['device', config])
860 found = True
861 except:
862 log.exception("dumping sxp from device controllers")
863 pass
865 # if we didn't find that device, check the existing config
866 # for a device in the same class
867 if not found:
868 for dev_type, dev_info in self.all_devices_sxpr():
869 if dev_type == cls:
870 sxpr.append(['device', dev_info])
872 return sxpr
874 def device_add(self, dev_type, cfg_sxp = None, cfg_xenapi = None,
875 target = None):
876 """Add a device configuration in SXP format or XenAPI struct format.
878 For SXP, it could be either:
880 [device, [vbd, [uname ...]]
882 or:
884 [vbd, [uname ..]]
886 @type cfg_sxp: list of lists (parsed sxp object)
887 @param cfg_sxp: SXP configuration object
888 @type cfg_xenapi: dict
889 @param cfg_xenapi: A device configuration from Xen API (eg. vbd,vif)
890 @param target: write device information to
891 @type target: None or a dictionary
892 @rtype: string
893 @return: Assigned UUID of the device.
894 """
895 if target == None:
896 target = self
898 if dev_type not in XendDevices.valid_devices() and \
899 dev_type not in XendDevices.pseudo_devices():
900 raise XendConfigError("XendConfig: %s not a valid device type" %
901 dev_type)
903 if cfg_sxp == None and cfg_xenapi == None:
904 raise XendConfigError("XendConfig: device_add requires some "
905 "config.")
907 if cfg_sxp:
908 log.debug("XendConfig.device_add: %s" % str(cfg_sxp))
909 if cfg_xenapi:
910 log.debug("XendConfig.device_add: %s" % str(cfg_xenapi))
912 if cfg_sxp:
913 if sxp.child0(cfg_sxp) == 'device':
914 config = sxp.child0(cfg_sxp)
915 else:
916 config = cfg_sxp
918 dev_type = sxp.name(config)
919 dev_info = {}
921 try:
922 for opt, val in config[1:]:
923 dev_info[opt] = val
924 except ValueError:
925 pass # SXP has no options for this device
927 if dev_type == 'vbd':
928 if dev_info.get('dev', '').startswith('ioemu:'):
929 dev_info['driver'] = 'ioemu'
930 else:
931 dev_info['driver'] = 'paravirtualised'
933 # create uuid if it doesn't exist
934 dev_uuid = dev_info.get('uuid', uuid.createString())
935 dev_info['uuid'] = dev_uuid
937 # store dev references by uuid for certain device types
938 target['devices'][dev_uuid] = (dev_type, dev_info)
939 if dev_type in ('vif', 'vbd', 'vtpm'):
940 param = '%s_refs' % dev_type
941 if param not in target:
942 target[param] = []
943 if dev_uuid not in target[param]:
944 target[param].append(dev_uuid)
945 elif dev_type in ('tap',):
946 if 'vbd_refs' not in target:
947 target['vbd_refs'] = []
948 if dev_uuid not in target['vbd_refs']:
949 target['vbd_refs'].append(dev_uuid)
951 return dev_uuid
953 if cfg_xenapi:
954 dev_info = {}
955 if dev_type == 'vif':
956 if cfg_xenapi.get('MAC'): # don't add if blank
957 dev_info['mac'] = cfg_xenapi.get('MAC')
958 # vifname is the name on the guest, not dom0
959 # TODO: we don't have the ability to find that out or
960 # change it from dom0
961 #if cfg_xenapi.get('device'): # don't add if blank
962 # dev_info['vifname'] = cfg_xenapi.get('device')
963 if cfg_xenapi.get('type'):
964 dev_info['type'] = cfg_xenapi.get('type')
966 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
967 dev_info['uuid'] = dev_uuid
968 self['devices'][dev_uuid] = (dev_type, dev_info)
969 self['vif_refs'].append(dev_uuid)
970 return dev_uuid
972 elif dev_type in ('vbd', 'tap'):
973 if dev_type == 'vbd':
974 dev_info['uname'] = cfg_xenapi.get('image', '')
975 dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
976 elif dev_type == 'tap':
977 dev_info['uname'] = 'tap:qcow:%s' % cfg_xenapi.get('image')
978 dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
980 dev_info['driver'] = cfg_xenapi.get('driver')
981 dev_info['VDI'] = cfg_xenapi.get('VDI', '')
983 if cfg_xenapi.get('mode') == 'RW':
984 dev_info['mode'] = 'w'
985 else:
986 dev_info['mode'] = 'r'
988 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
989 dev_info['uuid'] = dev_uuid
990 self['devices'][dev_uuid] = (dev_type, dev_info)
991 self['vbd_refs'].append(dev_uuid)
992 return dev_uuid
994 elif dev_type in ('vtpm'):
995 if cfg_xenapi.get('type'):
996 dev_info['type'] = cfg_xenapi.get('type')
998 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
999 dev_info['uuid'] = dev_uuid
1000 self['devices'][dev_uuid] = (dev_type, dev_info)
1001 self['vtpm_refs'].append(dev_uuid)
1002 return dev_uuid
1004 return ''
1006 def device_update(self, dev_uuid, cfg_sxp):
1007 """Update an existing device with the new configuration.
1009 @rtype: boolean
1010 @return: Returns True if succesfully found and updated a device conf
1011 """
1012 if dev_uuid in self['devices']:
1013 config = sxp.child0(cfg_sxp)
1014 dev_type = sxp.name(config)
1015 dev_info = {}
1017 try:
1018 for opt, val in config[1:]:
1019 self['devices'][opt] = val
1020 except ValueError:
1021 pass # SXP has no options for this device
1023 return True
1025 return False
1028 def device_sxpr(self, dev_uuid = None, dev_type = None, dev_info = None):
1029 """Get Device SXPR by either giving the device UUID or (type, config).
1031 @rtype: list of lists
1032 @return: device config sxpr
1033 """
1034 sxpr = []
1035 if dev_uuid != None and dev_uuid in self['devices']:
1036 dev_type, dev_info = self['devices'][dev_uuid]
1038 if dev_type == None or dev_info == None:
1039 raise XendConfigError("Required either UUID or device type and "
1040 "configuration dictionary.")
1042 sxpr.append(dev_type)
1043 config = [(opt, val) for opt, val in dev_info.items()]
1044 sxpr += config
1046 return sxpr
1048 def all_devices_sxpr(self):
1049 """Returns the SXPR for all devices in the current configuration."""
1050 sxprs = []
1051 pci_devs = []
1053 if 'devices' not in self:
1054 return sxprs
1056 for dev_type, dev_info in self['devices'].values():
1057 if dev_type == 'pci': # special case for pci devices
1058 sxpr = [['uuid', dev_info['uuid']]]
1059 for pci_dev_info in dev_info['devs']:
1060 pci_dev_sxpr = ['dev']
1061 for opt, val in pci_dev_info.items():
1062 pci_dev_sxpr.append([opt, val])
1063 sxpr.append(pci_dev_sxpr)
1064 sxprs.append((dev_type, sxpr))
1065 else:
1066 sxpr = self.device_sxpr(dev_type = dev_type,
1067 dev_info = dev_info)
1068 sxprs.append((dev_type, sxpr))
1070 return sxprs
1072 def image_sxpr(self):
1073 """Returns a backwards compatible image SXP expression that is
1074 used in xenstore's /vm/<uuid>/image value and xm list."""
1075 image = [self['image'].get('type', 'linux')]
1076 if self.has_key('PV_kernel'):
1077 image.append(['kernel', self['PV_kernel']])
1078 if self.has_key('PV_ramdisk') and self['PV_ramdisk']:
1079 image.append(['ramdisk', self['PV_ramdisk']])
1080 if self.has_key('PV_args') and self['PV_args']:
1081 image.append(['args', self['PV_args']])
1083 for arg, conv in LEGACY_IMAGE_CFG:
1084 if self['image'].has_key(arg):
1085 image.append([arg, self['image'][arg]])
1087 if 'hvm' in self['image']:
1088 for arg, conv in LEGACY_IMAGE_HVM_CFG:
1089 if self['image']['hvm'].get(arg):
1090 image.append([arg, self['image']['hvm'][arg]])
1092 if 'hvm' in self['image'] and 'devices' in self['image']['hvm']:
1093 for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
1094 if self['image']['hvm']['devices'].get(arg):
1095 image.append([arg,
1096 self['image']['hvm']['devices'][arg]])
1098 return image
1100 def update_with_image_sxp(self, image_sxp):
1101 # Convert Legacy "image" config to Xen API PV_*
1102 # configuration
1103 log.debug("update_with_image_sxp(%s)" % scrub_password(image_sxp))
1105 self['PV_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1106 self['PV_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
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
1116 self['PV_args'] = kernel_args
1118 # Store image SXP in python dictionary format
1119 image = {}
1120 image['type'] = sxp.name(image_sxp)
1121 for arg, conv in LEGACY_IMAGE_CFG:
1122 val = sxp.child_value(image_sxp, arg, None)
1123 if val != None:
1124 image[arg] = conv(val)
1126 image_hvm = {}
1127 for arg, conv in LEGACY_IMAGE_HVM_CFG:
1128 val = sxp.child_value(image_sxp, arg, None)
1129 if val != None:
1130 image_hvm[arg] = conv(val)
1132 image_hvm_devices = {}
1133 for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
1134 val = sxp.child_value(image_sxp, arg, None)
1135 if val != None:
1136 image_hvm_devices[arg] = conv(val)
1138 if image_hvm or image_hvm_devices:
1139 image['hvm'] = image_hvm
1140 image['hvm']['devices'] = image_hvm_devices
1142 self['image'] = image
1144 for apikey, imgkey in XENAPI_HVM_CFG.items():
1145 val = sxp.child_value(image_sxp, imgkey, None)
1146 if val != None:
1147 type_conv = XENAPI_CFG_TYPES[apikey]
1148 if callable(conv):
1149 self[apikey] = type_conv(val)
1150 else:
1151 self[apikey] = val
1155 # debugging
1158 if __name__ == "__main__":
1159 pass