ia64/xen-unstable

view tools/python/xen/xend/XendConfig.py @ 12827:e8a74ac80167

[XEND] Update vcpu_avail when parsing configuration.

Signed-off-by: Alastair Tse <atse@xensource.com>
author Alastair Tse <atse@xensource.com>
date Thu Dec 07 11:12:05 2006 +0000 (2006-12-07)
parents 1edaf60343a7
children f7cff5d296bf
line source
1 #===========================================================================
2 # This library is free software; you can redistribute it and/or
3 # modify it under the terms of version 2.1 of the GNU Lesser General Public
4 # License as published by the Free Software Foundation.
5 #
6 # This library is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 # Lesser General Public License for more details.
10 #
11 # You should have received a copy of the GNU Lesser General Public
12 # License along with this library; if not, write to the Free Software
13 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 #============================================================================
15 # Copyright (C) 2006 XenSource Ltd
16 #============================================================================
18 import re
19 import time
20 import types
22 from xen.xend import sxp
23 from xen.xend import uuid
24 from xen.xend.XendError import VmError
25 from xen.xend.XendDevices import XendDevices
26 from xen.xend.XendLogging import log
27 from xen.xend.PrettyPrint import prettyprintstring
28 from xen.xend.XendConstants import DOM_STATE_HALTED
30 """
31 XendConfig API
33 XendConfig will try to mirror as closely the Xen API VM Struct
34 with extra parameters for those options that are not supported.
36 """
38 def reverse_dict(adict):
39 """Return the reverse mapping of a dictionary."""
40 return dict([(v, k) for k, v in adict.items()])
42 def bool0(v):
43 return v != '0' and bool(v)
45 # Mapping from XendConfig configuration keys to the old
46 # legacy configuration keys that map directly.
48 XENAPI_CFG_TO_LEGACY_CFG = {
49 'uuid': 'uuid',
50 'vcpus_number': 'vcpus',
51 'memory_static_min': 'memory',
52 'memory_static_max': 'maxmem',
53 'name_label': 'name',
54 'actions_after_shutdown': 'on_poweroff',
55 'actions_after_reboot': 'on_reboot',
56 'actions_after_crash': 'on_crash',
57 'platform_localtime': 'localtime',
58 }
60 LEGACY_CFG_TO_XENAPI_CFG = reverse_dict(XENAPI_CFG_TO_LEGACY_CFG)
62 # Mapping from XendConfig configuration keys to the old
63 # legacy configuration keys that are found in the 'image'
64 # SXP object.
65 XENAPI_HVM_CFG = {
66 'platform_std_vga': 'stdvga',
67 'platform_serial' : 'serial',
68 'platform_localtime': 'localtime',
69 'platform_enable_audio': 'soundhw',
70 'platform_keymap' : 'keymap',
71 }
73 # List of XendConfig configuration keys that have no equivalent
74 # in the old world.
76 XENAPI_CFG_TYPES = {
77 'uuid': str,
78 'power_state': str,
79 'name_label': str,
80 'name_description': str,
81 'user_version': str,
82 'is_a_template': bool0,
83 'resident_on': str,
84 'memory_static_min': int,
85 'memory_static_max': int,
86 'memory_dynamic_min': int,
87 'memory_dynamic_max': int,
88 'memory_actual': int,
89 'vcpus_policy': str,
90 'vcpus_params': str,
91 'vcpus_number': int,
92 'vcpus_features_required': list,
93 'vcpus_features_can_use': list,
94 'vcpus_features_force_on': list,
95 'vcpus_features_force_off': list,
96 'actions_after_shutdown': str,
97 'actions_after_reboot': str,
98 'actions_after_suspend': str,
99 'actions_after_crash': str,
100 'tpm_instance': int,
101 'tpm_backend': int,
102 'bios_boot': str,
103 'platform_std_vga': bool0,
104 'platform_serial': str,
105 'platform_localtime': bool0,
106 'platform_clock_offset': bool0,
107 'platform_enable_audio': bool0,
108 'platform_keymap': str,
109 'boot_method': str,
110 'builder': str,
111 'kernel_kernel': str,
112 'kernel_initrd': str,
113 'kernel_args': str,
114 'grub_cmdline': str,
115 'pci_bus': str,
116 'tools_version': dict,
117 'otherconfig': dict,
118 }
120 # List of legacy configuration keys that have no equivalent in the
121 # Xen API, but are still stored in XendConfig.
123 LEGACY_UNSUPPORTED_BY_XENAPI_CFG = [
124 # roundtripped (dynamic, unmodified)
125 'shadow_memory',
126 'security',
127 'vcpu_avail',
128 'cpu_weight',
129 'cpu_cap',
130 'bootloader',
131 'bootloader_args',
132 'features',
133 # read/write
134 'on_xend_start',
135 'on_xend_stop',
136 # read-only
137 'domid',
138 'start_time',
139 'cpu_time',
140 'online_vcpus',
141 # write-once
142 'cpu',
143 'cpus',
144 ]
146 LEGACY_CFG_TYPES = {
147 'uuid': str,
148 'name': str,
149 'vcpus': int,
150 'vcpu_avail': int,
151 'memory': int,
152 'shadow_memory': int,
153 'maxmem': int,
154 'start_time': float,
155 'cpu_cap': int,
156 'cpu_weight': int,
157 'cpu_time': float,
158 'bootloader': str,
159 'bootloader_args': str,
160 'features': str,
161 'localtime': int,
162 'name': str,
163 'on_poweroff': str,
164 'on_reboot': str,
165 'on_crash': str,
166 'on_xend_stop': str,
167 'on_xend_start': str,
168 'online_vcpus': int,
169 }
171 # Values that should be stored in xenstore's /vm/<uuid> that is used
172 # by Xend. Used in XendDomainInfo to restore running VM state from
173 # xenstore.
174 LEGACY_XENSTORE_VM_PARAMS = [
175 'uuid',
176 'name',
177 'vcpus',
178 'vcpu_avail',
179 'memory',
180 'shadow_memory',
181 'maxmem',
182 'start_time',
183 'name',
184 'on_poweroff',
185 'on_crash',
186 'on_reboot',
187 'on_xend_start',
188 'on_xend_stop',
189 ]
191 LEGACY_IMAGE_CFG = [
192 ('root', str),
193 ('ip', str),
194 ('nographic', int),
195 ('vnc', int),
196 ('sdl', int),
197 ('vncdisplay', int),
198 ('vncunused', int),
199 ('vncpasswd', str),
200 ]
202 LEGACY_IMAGE_HVM_CFG = [
203 ('device_model', str),
204 ('display', str),
205 ('xauthority', str),
206 ('vncconsole', int),
207 ('pae', int),
208 ('apic', int),
209 ]
211 LEGACY_IMAGE_HVM_DEVICES_CFG = [
212 ('acpi', int),
213 ('boot', str),
214 ('fda', str),
215 ('fdb', str),
216 ('isa', str),
217 ('keymap', str),
218 ('localtime', str),
219 ('serial', str),
220 ('stdvga', int),
221 ('soundhw', str),
222 ('usb', str),
223 ('usbdevice', str),
224 ('vcpus', int),
225 ]
227 ##
228 ## Config Choices
229 ##
231 CONFIG_RESTART_MODES = ('restart', 'destroy', 'preserve', 'rename-restart')
232 CONFIG_OLD_DOM_STATES = ('running', 'blocked', 'paused', 'shutdown',
233 'crashed', 'dying')
235 class XendConfigError(VmError):
236 def __str__(self):
237 return 'Invalid Configuration: %s' % str(self.value)
239 ##
240 ## XendConfig Class (an extended dictionary)
241 ##
243 class XendConfig(dict):
244 """ The new Xend VM Configuration.
246 Stores the configuration in xenapi compatible format but retains
247 import and export functions for SXP.
248 """
249 def __init__(self, filename = None, sxp_obj = None,
250 xapi = None, dominfo = None):
252 dict.__init__(self)
253 self.update(self._defaults())
255 if filename:
256 try:
257 sxp_obj = sxp.parse(open(filename,'r'))
258 sxp_obj = sxp_obj[0]
259 except IOError, e:
260 raise XendConfigError("Unable to read file: %s" % filename)
262 if sxp_obj:
263 self._sxp_to_xapi(sxp_obj)
264 self._sxp_to_xapi_unsupported(sxp_obj)
265 elif xapi:
266 self.update_with_xenapi_config(xapi)
267 self._add_xapi_unsupported()
268 elif dominfo:
269 # output from xc.domain_getinfo
270 self._dominfo_to_xapi(dominfo)
272 log.debug('XendConfig.init: %s' % self)
274 # validators go here
275 self.validate()
277 """ In time, we should enable this type checking addition. It is great
278 also for tracking bugs and unintended writes to XendDomainInfo.info
279 def __setitem__(self, key, value):
280 type_conv = XENAPI_CFG_TYPES.get(key)
281 if callable(type_conv):
282 try:
283 dict.__setitem__(self, key, type_conv(value))
284 except (ValueError, TypeError):
285 raise XendConfigError("Wrong type for configuration value " +
286 "%s. Expected %s" %
287 (key, type_conv.__name__))
288 else:
289 dict.__setitem__(self, key, value)
290 """
292 def _defaults(self):
293 defaults = {
294 'uuid': uuid.createString(),
295 'name_label': 'Domain-Unnamed',
296 'actions_after_shutdown': 'destroy',
297 'actions_after_reboot': 'restart',
298 'actions_after_crash': 'restart',
299 'actions_after_suspend': '',
300 'features': '',
301 'builder': 'linux',
302 'memory_static_min': 0,
303 'memory_dynamic_min': 0,
304 'shadow_memory': 0,
305 'memory_static_max': 0,
306 'memory_dynamic_max': 0,
307 'memory_actual': 0,
308 'boot_method': None,
309 'bootloader': None,
310 'bootloader_args': None,
311 'devices': {},
312 'image': {},
313 'security': None,
314 'on_xend_start': 'ignore',
315 'on_xend_stop': 'ignore',
316 'cpus': [],
317 'cpu_weight': 256,
318 'cpu_cap': 0,
319 'vcpus_number': 1,
320 'online_vcpus': 1,
321 'max_vcpu_id': 0,
322 'vcpu_avail': 1,
323 'vif_refs': [],
324 'vbd_refs': [],
325 'vtpm_refs': [],
326 }
328 defaults['name_label'] = 'Domain-' + defaults['uuid']
329 return defaults
331 def _memory_sanity_check(self):
332 if self['memory_static_min'] == 0:
333 self['memory_static_min'] = self['memory_dynamic_min']
335 # If the static max is not set, let's set it to dynamic max.
336 # If the static max is smaller than static min, then fix it!
337 self['memory_static_max'] = max(self['memory_static_max'],
338 self['memory_dynamic_max'],
339 self['memory_static_min'])
341 for mem_type in ('memory_static_min', 'memory_static_max'):
342 if self[mem_type] <= 0:
343 raise XendConfigError('Memory value too low for %s: %d' %
344 (mem_type, self[mem_type]))
346 def _actions_sanity_check(self):
347 for event in ['shutdown', 'reboot', 'crash']:
348 if self['actions_after_' + event] not in CONFIG_RESTART_MODES:
349 raise XendConfigError('Invalid event handling mode: ' +
350 event)
352 def _builder_sanity_check(self):
353 if self['builder'] not in ('hvm', 'linux'):
354 raise XendConfigError('Invalid builder configuration')
356 def _vcpus_sanity_check(self):
357 if self.get('vcpus_number') != None:
358 self['vcpu_avail'] = (1 << self['vcpus_number']) - 1
360 def validate(self):
361 self._memory_sanity_check()
362 self._actions_sanity_check()
363 self._builder_sanity_check()
364 self._vcpus_sanity_check()
366 def _dominfo_to_xapi(self, dominfo):
367 self['domid'] = dominfo['domid']
368 self['online_vcpus'] = dominfo['online_vcpus']
369 self['max_vcpu_id'] = dominfo['max_vcpu_id']
370 self['memory_dynamic_min'] = (dominfo['mem_kb'] + 1023)/1024
371 self['memory_dynamic_max'] = (dominfo['maxmem_kb'] + 1023)/1024
372 self['cpu_time'] = dominfo['cpu_time']/1e9
373 # TODO: i don't know what the security stuff expects here
374 if dominfo.get('ssidref'):
375 self['security'] = [['ssidref', dominfo['ssidref']]]
376 self['shutdown_reason'] = dominfo['shutdown_reason']
378 # parse state into Xen API states
379 self['running'] = dominfo['running']
380 self['crashed'] = dominfo['crashed']
381 self['dying'] = dominfo['dying']
382 self['shutdown'] = dominfo['shutdown']
383 self['paused'] = dominfo['paused']
384 self['blocked'] = dominfo['blocked']
386 if 'name' in dominfo:
387 self['name_label'] = dominfo['name']
389 if 'handle' in dominfo:
390 self['uuid'] = uuid.toString(dominfo['handle'])
392 def _parse_sxp(self, sxp_cfg):
393 """ Populate this XendConfig using the parsed SXP.
395 @param sxp_cfg: Parsed SXP Configuration
396 @type sxp_cfg: list of lists
397 @rtype: dictionary
398 @return: A dictionary containing the parsed options of the SXP.
399 """
400 cfg = {}
402 # First step is to convert deprecated options to
403 # current equivalents.
405 restart = sxp.child_value(sxp_cfg, 'restart')
406 if restart:
407 if restart == 'onreboot':
408 cfg['on_poweroff'] = 'destroy'
409 cfg['on_reboot'] = 'restart'
410 cfg['on_crash'] = 'destroy'
411 elif restart == 'always':
412 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
413 cfg[opt] = 'restart'
414 elif restart == 'never':
415 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
416 cfg[opt] = 'never'
417 else:
418 log.warn('Ignoring unrecognised value for deprecated option:'
419 'restart = \'%s\'', restart)
421 # Only extract options we know about.
422 extract_keys = LEGACY_UNSUPPORTED_BY_XENAPI_CFG
423 extract_keys += XENAPI_CFG_TO_LEGACY_CFG.values()
425 for key in extract_keys:
426 val = sxp.child_value(sxp_cfg, key)
427 if val != None:
428 try:
429 cfg[key] = LEGACY_CFG_TYPES[key](val)
430 except KeyError:
431 cfg[key] = val
432 except (TypeError, ValueError), e:
433 log.warn("Unable to parse key %s: %s: %s" %
434 (key, str(val), e))
436 # Parsing the device SXP's. In most cases, the SXP looks
437 # like this:
438 #
439 # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]]
440 #
441 # However, for PCI devices it looks like this:
442 #
443 # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1]]]]
444 #
445 # It seems the reasoning for this difference is because
446 # pciif.py needs all the PCI device configurations at
447 # the same time when creating the devices.
448 #
449 # To further complicate matters, Xen 2.0 configuration format
450 # uses the following for pci device configuration:
451 #
452 # [device, [pci, [domain, 0], [bus, 0], [dev, 1], [func, 2]]]
453 #
454 # Hence we deal with pci device configurations outside of
455 # the regular device parsing.
457 cfg['devices'] = {}
458 for dev in sxp.children(sxp_cfg, 'device'):
459 config = sxp.child0(dev)
460 dev_type = sxp.name(config)
461 dev_info = {}
463 if dev_type == 'pci':
464 pci_devs_uuid = sxp.child_value(config, 'uuid',
465 uuid.createString())
466 pci_devs = []
467 for pci_dev in sxp.children(config, 'dev'):
468 pci_dev_info = {}
469 for opt, val in pci_dev[1:]:
470 pci_dev_info[opt] = val
471 pci_devs.append(pci_dev_info)
473 cfg['devices'][pci_devs_uuid] = (dev_type,
474 {'devs': pci_devs,
475 'uuid': pci_devs_uuid})
477 log.debug("XendConfig: reading device: %s" % pci_devs)
478 else:
479 for opt, val in config[1:]:
480 dev_info[opt] = val
481 log.debug("XendConfig: reading device: %s" % dev_info)
482 # create uuid if it doesn't
483 dev_uuid = dev_info.get('uuid', uuid.createString())
484 dev_info['uuid'] = dev_uuid
485 cfg['devices'][dev_uuid] = (dev_type, dev_info)
488 # Extract missing data from configuration entries
489 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
490 if image_sxp:
491 image_vcpus = sxp.child_value(image_sxp, 'vcpus')
492 if image_vcpus != None:
493 try:
494 if 'vcpus_number' not in cfg:
495 cfg['vcpus_number'] = int(image_vcpus)
496 elif cfg['vcpus_number'] != int(image_vcpus):
497 cfg['vcpus_number'] = int(image_vcpus)
498 log.warn('Overriding vcpus from %d to %d using image'
499 'vcpus value.', cfg['vcpus_number'])
500 except ValueError, e:
501 raise XendConfigError('integer expeceted: %s: %s' %
502 image_sxp, e)
504 # Deprecated cpu configuration
505 if 'cpu' in cfg:
506 if 'cpus' in cfg:
507 cfg['cpus'] = "%s,%s" % (str(cfg['cpu']), cfg['cpus'])
508 else:
509 cfg['cpus'] = str(cfg['cpu'])
511 # convert 'cpus' string to list of ints
512 # 'cpus' supports a list of ranges (0-3), seperated by
513 # commas, and negation, (^1).
514 # Precedence is settled by order of the string:
515 # "0-3,^1" -> [0,2,3]
516 # "0-3,^1,1" -> [0,1,2,3]
517 try:
518 if 'cpus' in cfg:
519 cpus = []
520 for c in cfg['cpus'].split(','):
521 if c.find('-') != -1:
522 (x, y) = c.split('-')
523 for i in range(int(x), int(y)+1):
524 cpus.append(int(i))
525 else:
526 # remove this element from the list
527 if c[0] == '^':
528 cpus = [x for x in cpus if x != int(c[1:])]
529 else:
530 cpus.append(int(c))
532 cfg['cpus'] = cpus
533 except ValueError, e:
534 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
536 if 'security' in cfg and isinstance(cfg['security'], str):
537 cfg['security'] = sxp.from_string(cfg['security'])
539 # TODO: get states
540 old_state = sxp.child_value(sxp_cfg, 'state')
541 if old_state:
542 for i in range(len(CONFIG_OLD_DOM_STATES)):
543 cfg[CONFIG_OLD_DOM_STATES[i]] = int(old_state[i] != '-')
545 return cfg
548 def _sxp_to_xapi(self, sxp_cfg):
549 """Read in an SXP Configuration object and
550 populate at much of the Xen API with valid values.
551 """
552 cfg = self._parse_sxp(sxp_cfg)
554 # Convert parameters that can be directly mapped from
555 # the Legacy Config to Xen API Config
557 for apikey, cfgkey in XENAPI_CFG_TO_LEGACY_CFG.items():
558 try:
559 type_conv = XENAPI_CFG_TYPES.get(apikey)
560 if callable(type_conv):
561 self[apikey] = type_conv(cfg[cfgkey])
562 else:
563 log.warn("Unconverted key: " + apikey)
564 self[apikey] = cfg[cfgkey]
565 except KeyError:
566 pass
568 # Convert Legacy "image" config to Xen API kernel_*
569 # configuration
570 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
571 if image_sxp:
572 self['kernel_kernel'] = sxp.child_value(image_sxp, 'kernel','')
573 self['kernel_initrd'] = sxp.child_value(image_sxp, 'ramdisk','')
574 kernel_args = sxp.child_value(image_sxp, 'args', '')
576 # attempt to extract extra arguments from SXP config
577 arg_ip = sxp.child_value(image_sxp, 'ip')
578 if arg_ip and not re.search(r'ip=[^ ]+', kernel_args):
579 kernel_args += ' ip=%s' % arg_ip
580 arg_root = sxp.child_value(image_sxp, 'root')
581 if arg_root and not re.search(r'root=[^ ]+', kernel_args):
582 kernel_args += ' root=%s' % arg_root
584 self['kernel_args'] = kernel_args
586 # Convert Legacy HVM parameters to Xen API configuration
587 self['platform_std_vga'] = bool0(cfg.get('stdvga', 0))
588 self['platform_serial'] = str(cfg.get('serial', ''))
589 self['platform_localtime'] = bool0(cfg.get('localtime', 0))
590 self['platform_enable_audio'] = bool0(cfg.get('soundhw', 0))
592 # Convert path to bootloader to boot_method
593 if not cfg.get('bootloader'):
594 if self.get('kernel_kernel','').endswith('hvmloader'):
595 self['boot_method'] = 'bios'
596 else:
597 self['boot_method'] = 'kernel_external'
598 else:
599 self['boot_method'] = 'grub'
601 # make sure a sane maximum is set
602 if self['memory_static_max'] <= 0:
603 self['memory_static_max'] = self['memory_static_min']
605 self['memory_dynamic_max'] = self['memory_static_max']
606 self['memory_dynamic_min'] = self['memory_static_min']
608 # set device references in the configuration
609 self['devices'] = cfg.get('devices', {})
611 self['vif_refs'] = []
612 self['vbd_refs'] = []
613 self['vtpm_refs'] = []
614 for dev_uuid, (dev_type, dev_info) in self['devices'].items():
615 if dev_type == 'vif':
616 self['vif_refs'].append(dev_uuid)
617 elif dev_type in ('vbd','tap'):
618 self['vbd_refs'].append(dev_uuid)
619 elif dev_type in ('vtpm',):
620 self['vtpm_refs'].append(dev_uuid)
623 def _sxp_to_xapi_unsupported(self, sxp_cfg):
624 """Read in an SXP configuration object and populate
625 values are that not related directly supported in
626 the Xen API.
627 """
629 # Parse and convert parameters used to configure
630 # the image (as well as HVM images)
631 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
632 if image_sxp:
633 image = {}
634 image['type'] = sxp.name(image_sxp)
635 for arg, conv in LEGACY_IMAGE_CFG:
636 val = sxp.child_value(image_sxp, arg, None)
637 if val != None:
638 image[arg] = conv(val)
640 image_hvm = {}
641 for arg, conv in LEGACY_IMAGE_HVM_CFG:
642 val = sxp.child_value(image_sxp, arg, None)
643 if val != None:
644 image_hvm[arg] = conv(val)
646 image_hvm_devices = {}
647 for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
648 val = sxp.child_value(image_sxp, arg, None)
649 if val != None:
650 image_hvm_devices[arg] = conv(val)
652 if image_hvm or image_hvm_devices:
653 image['hvm'] = image_hvm
654 image['hvm']['devices'] = image_hvm_devices
656 self['image'] = image
658 for apikey, imgkey in XENAPI_HVM_CFG.items():
659 val = sxp.child_value(image_sxp, imgkey, None)
660 if val != None:
661 self[apikey] = val
663 # extract backend value
665 backend = []
666 for c in sxp.children(sxp_cfg, 'backend'):
667 backend.append(sxp.name(sxp.child0(c)))
668 if backend:
669 self['backend'] = backend
671 if self['image'].has_key('hvm'):
672 self['builder'] = 'hvm'
674 # Parse and convert other Non Xen API parameters.
675 def _set_cfg_if_exists(sxp_arg):
676 val = sxp.child_value(sxp_cfg, sxp_arg)
677 if val != None:
678 if LEGACY_CFG_TYPES.get(sxp_arg):
679 self[sxp_arg] = LEGACY_CFG_TYPES[sxp_arg](val)
680 else:
681 self[sxp_arg] = val
683 _set_cfg_if_exists('shadow_memory')
684 _set_cfg_if_exists('security')
685 _set_cfg_if_exists('features')
686 _set_cfg_if_exists('on_xend_stop')
687 _set_cfg_if_exists('on_xend_start')
688 _set_cfg_if_exists('vcpu_avail')
689 _set_cfg_if_exists('max_vcpu_id') # TODO, deprecated?
691 # Parse and store runtime configuration
692 _set_cfg_if_exists('start_time')
693 _set_cfg_if_exists('online_vcpus')
694 _set_cfg_if_exists('cpu_time')
695 _set_cfg_if_exists('shutdown_reason')
696 _set_cfg_if_exists('up_time')
697 _set_cfg_if_exists('status') # TODO, deprecated
699 def _add_xapi_unsupported(self):
700 """Updates the configuration object with entries that are not
701 officially supported by the Xen API but is required for
702 the rest of Xend to function.
703 """
705 # populate image
706 self['image']['type'] = self['builder']
707 if self['builder'] == 'hvm':
708 self['image']['hvm'] = {}
709 for xapi, cfgapi in XENAPI_HVM_CFG.items():
710 self['image']['hvm'][cfgapi] = self[xapi]
713 def _get_old_state_string(self):
714 """Returns the old xm state string.
715 @rtype: string
716 @return: old state string
717 """
718 state_string = ''
719 for state_name in CONFIG_OLD_DOM_STATES:
720 on_off = self.get(state_name, 0)
721 if on_off:
722 state_string += state_name[0]
723 else:
724 state_string += '-'
726 return state_string
729 def update_config(self, dominfo):
730 """Update configuration with the output from xc.domain_getinfo().
732 @param dominfo: Domain information via xc.domain_getinfo()
733 @type dominfo: dict
734 """
735 self._dominfo_to_xapi(dominfo)
736 self.validate()
738 def update_with_xenapi_config(self, xapi):
739 """Update configuration with a Xen API VM struct
741 @param xapi: Xen API VM Struct
742 @type xapi: dict
743 """
744 for key, val in xapi.items():
745 key = key.lower()
746 type_conv = XENAPI_CFG_TYPES.get(key)
747 if callable(type_conv):
748 self[key] = type_conv(val)
749 else:
750 self[key] = val
752 self.validate()
754 def to_xml(self):
755 """Return an XML string representing the configuration."""
756 pass
758 def to_sxp(self, domain = None, ignore_devices = False, ignore = []):
759 """ Get SXP representation of this config object.
761 Incompat: removed store_mfn, console_mfn
763 @keyword domain: (optional) XendDomainInfo to get extra information
764 from such as domid and running devices.
765 @type domain: XendDomainInfo
766 @keyword ignore: (optional) list of 'keys' that we do not want
767 to export.
768 @type ignore: list of strings
769 @rtype: list of list (SXP representation)
770 """
771 sxpr = ['domain']
773 # TODO: domid/dom is the same thing but called differently
774 # depending if it is from xenstore or sxpr.
776 if domain.getDomid() is not None:
777 sxpr.append(['domid', domain.getDomid()])
779 for xenapi, legacy in XENAPI_CFG_TO_LEGACY_CFG.items():
780 if self.has_key(xenapi) and self[xenapi] not in (None, []):
781 if type(self[xenapi]) == bool:
782 # convert booleans to ints before making an sxp item
783 sxpr.append([legacy, int(self[xenapi])])
784 else:
785 sxpr.append([legacy, self[xenapi]])
787 for legacy in LEGACY_UNSUPPORTED_BY_XENAPI_CFG:
788 if legacy in ('domid', 'uuid'): # skip these
789 continue
790 if self.has_key(legacy) and self[legacy] not in (None, []):
791 sxpr.append([legacy, self[legacy]])
793 if 'image' in self and self['image']:
794 sxpr.append(['image', self.image_sxpr()])
796 sxpr.append(['status', domain.state])
797 sxpr.append(['memory_dynamic_min', self.get('memory_dynamic_min')])
798 sxpr.append(['memory_dynamic_max', self.get('memory_dynamic_max')])
800 if domain.getDomid() is not None:
801 sxpr.append(['state', self._get_old_state_string()])
803 if domain:
804 if domain.store_mfn:
805 sxpr.append(['store_mfn', domain.store_mfn])
806 if domain.console_mfn:
807 sxpr.append(['console_mfn', domain.console_mfn])
810 # Marshall devices (running or from configuration)
811 if not ignore_devices:
812 for cls in XendDevices.valid_devices():
813 found = False
815 # figure if there is a device that is running
816 if domain:
817 try:
818 controller = domain.getDeviceController(cls)
819 configs = controller.configurations()
820 for config in configs:
821 sxpr.append(['device', config])
822 found = True
823 except:
824 log.exception("dumping sxp from device controllers")
825 pass
827 # if we didn't find that device, check the existing config
828 # for a device in the same class
829 if not found:
830 for dev_type, dev_info in self.all_devices_sxpr():
831 if dev_type == cls:
832 sxpr.append(['device', dev_info])
834 return sxpr
836 def device_add(self, dev_type, cfg_sxp = None, cfg_xenapi = None):
837 """Add a device configuration in SXP format or XenAPI struct format.
839 For SXP, it could be either:
841 [device, [vbd, [uname ...]]
843 or:
845 [vbd, [uname ..]]
847 @type cfg_sxp: list of lists (parsed sxp object)
848 @param cfg_sxp: SXP configuration object
849 @type cfg_xenapi: dict
850 @param cfg_xenapi: A device configuration from Xen API (eg. vbd,vif)
851 @rtype: string
852 @return: Assigned UUID of the device.
853 """
854 if dev_type not in XendDevices.valid_devices() and \
855 dev_type not in XendDevices.pseudo_devices():
856 raise XendConfigError("XendConfig: %s not a valid device type" %
857 dev_type)
859 if cfg_sxp == None and cfg_xenapi == None:
860 raise XendConfigError("XendConfig: device_add requires some "
861 "config.")
863 if cfg_sxp:
864 log.debug("XendConfig.device_add: %s" % str(cfg_sxp))
865 if cfg_xenapi:
866 log.debug("XendConfig.device_add: %s" % str(cfg_xenapi))
868 if cfg_sxp:
869 if sxp.child0(cfg_sxp) == 'device':
870 config = sxp.child0(cfg_sxp)
871 else:
872 config = cfg_sxp
874 dev_type = sxp.name(config)
875 dev_info = {}
877 try:
878 for opt, val in config[1:]:
879 dev_info[opt] = val
880 except ValueError:
881 pass # SXP has no options for this device
884 def _get_config_ipaddr(config):
885 val = []
886 for ipaddr in sxp.children(config, elt='ip'):
887 val.append(sxp.child0(ipaddr))
888 return val
890 if dev_type == 'vif' and 'ip' in dev_info:
891 dev_info['ip'] = _get_config_ipaddr(config)
893 if dev_type == 'vbd':
894 if dev_info.get('dev', '').startswith('ioemu:'):
895 dev_info['driver'] = 'ioemu'
896 else:
897 dev_info['driver'] = 'paravirtualised'
900 # create uuid if it doesn't exist
901 dev_uuid = dev_info.get('uuid', uuid.createString())
902 dev_info['uuid'] = dev_uuid
904 # store dev references by uuid for certain device types
905 self['devices'][dev_uuid] = (dev_type, dev_info)
906 if dev_type in ('vif', 'vbd', 'vtpm'):
907 self['%s_refs' % dev_type].append(dev_uuid)
908 elif dev_type in ('tap',):
909 self['vbd_refs'].append(dev_uuid)
911 return dev_uuid
913 if cfg_xenapi:
914 dev_info = {}
915 if dev_type == 'vif':
916 if cfg_xenapi.get('MAC'): # don't add if blank
917 dev_info['mac'] = cfg_xenapi.get('MAC')
918 # vifname is the name on the guest, not dom0
919 # TODO: we don't have the ability to find that out or
920 # change it from dom0
921 #if cfg_xenapi.get('device'): # don't add if blank
922 # dev_info['vifname'] = cfg_xenapi.get('device')
923 if cfg_xenapi.get('type'):
924 dev_info['type'] = cfg_xenapi.get('type')
926 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
927 dev_info['uuid'] = dev_uuid
928 self['devices'][dev_uuid] = (dev_type, dev_info)
929 self['vif_refs'].append(dev_uuid)
930 return dev_uuid
932 elif dev_type in ('vbd', 'tap'):
933 if dev_type == 'vbd':
934 dev_info['uname'] = cfg_xenapi.get('image', '')
935 dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
936 elif dev_type == 'tap':
937 dev_info['uname'] = 'tap:qcow:%s' % cfg_xenapi.get('image')
938 dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
940 dev_info['driver'] = cfg_xenapi.get('driver')
941 dev_info['VDI'] = cfg_xenapi.get('VDI', '')
943 if cfg_xenapi.get('mode') == 'RW':
944 dev_info['mode'] = 'w'
945 else:
946 dev_info['mode'] = 'r'
948 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
949 dev_info['uuid'] = dev_uuid
950 self['devices'][dev_uuid] = (dev_type, dev_info)
951 self['vbd_refs'].append(dev_uuid)
952 return dev_uuid
954 elif dev_type in ('vtpm'):
955 if cfg_xenapi.get('type'):
956 dev_info['type'] = cfg_xenapi.get('type')
958 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
959 dev_info['uuid'] = dev_uuid
960 self['devices'][dev_uuid] = (dev_type, dev_info)
961 self['vtpm_refs'].append(dev_uuid)
962 return dev_uuid
964 return ''
966 def device_update(self, dev_uuid, cfg_sxp):
967 """Update an existing device with the new configuration.
969 @rtype: boolean
970 @return: Returns True if succesfully found and updated a device conf
971 """
972 if dev_uuid in self['devices']:
973 config = sxp.child0(cfg_sxp)
974 dev_type = sxp.name(config)
975 dev_info = {}
977 try:
978 for opt, val in config[1:]:
979 self['devices'][opt] = val
980 except ValueError:
981 pass # SXP has no options for this device
983 return True
985 return False
988 def device_sxpr(self, dev_uuid = None, dev_type = None, dev_info = None):
989 """Get Device SXPR by either giving the device UUID or (type, config).
991 @rtype: list of lists
992 @return: device config sxpr
993 """
994 sxpr = []
995 if dev_uuid != None and dev_uuid in self['devices']:
996 dev_type, dev_info = self['devices'][dev_uuid]
998 if dev_type == None or dev_info == None:
999 raise XendConfigError("Required either UUID or device type and "
1000 "configuration dictionary.")
1002 sxpr.append(dev_type)
1003 config = [(opt, val) for opt, val in dev_info.items()]
1004 sxpr += config
1006 return sxpr
1008 def all_devices_sxpr(self):
1009 """Returns the SXPR for all devices in the current configuration."""
1010 sxprs = []
1011 pci_devs = []
1013 if 'devices' not in self:
1014 return sxprs
1016 for dev_type, dev_info in self['devices'].values():
1017 if dev_type == 'pci': # special case for pci devices
1018 sxpr = [['uuid', dev_info['uuid']]]
1019 for pci_dev_info in dev_info['devs']:
1020 pci_dev_sxpr = ['dev']
1021 for opt, val in pci_dev_info.items():
1022 pci_dev_sxpr.append([opt, val])
1023 sxpr.append(pci_dev_sxpr)
1024 sxprs.append((dev_type, sxpr))
1025 else:
1026 sxpr = self.device_sxpr(dev_type = dev_type,
1027 dev_info = dev_info)
1028 sxprs.append((dev_type, sxpr))
1030 return sxprs
1032 def image_sxpr(self):
1033 """Returns a backwards compatible image SXP expression that is
1034 used in xenstore's /vm/<uuid>/image value and xm list."""
1035 image = [self['image'].get('type', 'linux')]
1036 if self.has_key('kernel_kernel'):
1037 image.append(['kernel', self['kernel_kernel']])
1038 if self.has_key('kernel_initrd') and self['kernel_initrd']:
1039 image.append(['ramdisk', self['kernel_initrd']])
1040 if self.has_key('kernel_args') and self['kernel_args']:
1041 image.append(['args', self['kernel_args']])
1043 for arg, conv in LEGACY_IMAGE_CFG:
1044 if self['image'].has_key(arg):
1045 image.append([arg, self['image'][arg]])
1047 if 'hvm' in self['image']:
1048 for arg, conv in LEGACY_IMAGE_HVM_CFG:
1049 if self['image']['hvm'].get(arg):
1050 image.append([arg, self['image']['hvm'][arg]])
1052 if 'hvm' in self['image'] and 'devices' in self['image']['hvm']:
1053 for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
1054 if self['image']['hvm']['devices'].get(arg):
1055 image.append([arg,
1056 self['image']['hvm']['devices'][arg]])
1058 return image
1060 def update_with_image_sxp(self, image_sxp):
1061 # Convert Legacy "image" config to Xen API kernel_*
1062 # configuration
1063 self['kernel_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1064 self['kernel_initrd'] = sxp.child_value(image_sxp, 'ramdisk','')
1065 kernel_args = sxp.child_value(image_sxp, 'args', '')
1067 # attempt to extract extra arguments from SXP config
1068 arg_ip = sxp.child_value(image_sxp, 'ip')
1069 if arg_ip and not re.search(r'ip=[^ ]+', kernel_args):
1070 kernel_args += ' ip=%s' % arg_ip
1071 arg_root = sxp.child_value(image_sxp, 'root')
1072 if arg_root and not re.search(r'root=', kernel_args):
1073 kernel_args += ' root=%s' % arg_root
1074 self['kernel_args'] = kernel_args
1076 # Store image SXP in python dictionary format
1077 image = {}
1078 image['type'] = sxp.name(image_sxp)
1079 for arg, conv in LEGACY_IMAGE_CFG:
1080 val = sxp.child_value(image_sxp, arg, None)
1081 if val != None:
1082 image[arg] = conv(val)
1084 image_hvm = {}
1085 for arg, conv in LEGACY_IMAGE_HVM_CFG:
1086 val = sxp.child_value(image_sxp, arg, None)
1087 if val != None:
1088 image_hvm[arg] = conv(val)
1090 image_hvm_devices = {}
1091 for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
1092 val = sxp.child_value(image_sxp, arg, None)
1093 if val != None:
1094 image_hvm_devices[arg] = conv(val)
1096 if image_hvm or image_hvm_devices:
1097 image['hvm'] = image_hvm
1098 image['hvm']['devices'] = image_hvm_devices
1100 self['image'] = image
1102 for apikey, imgkey in XENAPI_HVM_CFG.items():
1103 val = sxp.child_value(image_sxp, imgkey, None)
1104 if val != None:
1105 type_conv = XENAPI_CFG_TYPES[apikey]
1106 if callable(conv):
1107 self[apikey] = type_conv(val)
1108 else:
1109 self[apikey] = val
1113 # debugging
1116 if __name__ == "__main__":
1117 pass