ia64/xen-unstable

view tools/python/xen/xend/XendConfig.py @ 12745:ef1dadde3631

[XEND] Reapply fix for ever expanding kernel_args in XendConfig

Signed-off-by: Alastair Tse <atse@xensource.com>
author Alastair Tse <atse@xensource.com>
date Fri Dec 01 13:55:28 2006 +0000 (2006-12-01)
parents d0ade847f886
children dd857b2e2bca
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': int,
79 'name_label': str,
80 'name_description': str,
81 'user_version': str,
82 'is_a_template': int,
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': int,
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 ]
201 LEGACY_IMAGE_HVM_CFG = [
202 ('device_model', str),
203 ('display', str),
204 ('xauthority', str),
205 ('vncconsole', int),
206 ('pae', int),
207 ('apic', int),
208 ]
210 LEGACY_IMAGE_HVM_DEVICES_CFG = [
211 ('acpi', int),
212 ('boot', str),
213 ('fda', str),
214 ('fdb', str),
215 ('isa', str),
216 ('keymap', str),
217 ('localtime', str),
218 ('serial', str),
219 ('stdvga', int),
220 ('soundhw', str),
221 ('usb', str),
222 ('usbdevice', str),
223 ('vcpus', int),
224 ]
226 ##
227 ## Config Choices
228 ##
230 CONFIG_RESTART_MODES = ('restart', 'destroy', 'preserve', 'rename-restart')
231 CONFIG_OLD_DOM_STATES = ('running', 'blocked', 'paused', 'shutdown',
232 'crashed', 'dying')
234 class XendConfigError(VmError):
235 def __str__(self):
236 return 'Invalid Configuration: %s' % str(self.value)
238 ##
239 ## XendConfig Class (an extended dictionary)
240 ##
242 class XendConfig(dict):
243 """ The new Xend VM Configuration.
245 Stores the configuration in xenapi compatible format but retains
246 import and export functions for SXP.
247 """
248 def __init__(self, filename = None, sxp_obj = None,
249 xapi = None, dominfo = None):
251 dict.__init__(self)
252 self.update(self._defaults())
254 if filename:
255 try:
256 sxp_obj = sxp.parse(open(filename,'r'))
257 sxp_obj = sxp_obj[0]
258 except IOError, e:
259 raise XendConfigError("Unable to read file: %s" % filename)
261 if sxp_obj:
262 self._sxp_to_xapi(sxp_obj)
263 self._sxp_to_xapi_unsupported(sxp_obj)
264 elif xapi:
265 self.update(xapi)
266 self._add_xapi_unsupported()
267 elif dominfo:
268 # output from xc.domain_getinfo
269 self._dominfo_to_xapi(dominfo)
271 log.debug('XendConfig.init: %s' % self)
273 # validators go here
274 self.validate()
276 def _defaults(self):
277 defaults = {
278 'uuid': uuid.createString(),
279 'name_label': 'Domain-Unnamed',
280 'actions_after_shutdown': 'destroy',
281 'actions_after_reboot': 'restart',
282 'actions_after_crash': 'restart',
283 'actions_after_suspend': '',
284 'features': '',
285 'builder': 'linux',
286 'memory_static_min': 0,
287 'memory_dynamic_min': 0,
288 'shadow_memory': 0,
289 'memory_static_max': 0,
290 'memory_dynamic_max': 0,
291 'memory_actual': 0,
292 'boot_method': None,
293 'bootloader': None,
294 'bootloader_args': None,
295 'devices': {},
296 'image': {},
297 'security': None,
298 'on_xend_start': 'ignore',
299 'on_xend_stop': 'ignore',
300 'cpus': [],
301 'cpu_weight': 256,
302 'cpu_cap': 0,
303 'vcpus_number': 1,
304 'online_vcpus': 1,
305 'max_vcpu_id': 0,
306 'vcpu_avail': 1,
307 'vif_refs': [],
308 'vbd_refs': [],
309 'vtpm_refs': [],
310 }
312 defaults['name_label'] = 'Domain-' + defaults['uuid']
313 return defaults
315 def _memory_sanity_check(self):
316 if self['memory_static_min'] == 0:
317 self['memory_static_min'] = self['memory_dynamic_min']
319 # If the static max is not set, let's set it to dynamic max.
320 # If the static max is smaller than static min, then fix it!
321 self['memory_static_max'] = max(self['memory_static_max'],
322 self['memory_dynamic_max'],
323 self['memory_static_min'])
325 for mem_type in ('memory_static_min', 'memory_static_max'):
326 if self[mem_type] <= 0:
327 raise XendConfigError('Memory value too low for %s: %d' %
328 (mem_type, self[mem_type]))
330 def _actions_sanity_check(self):
331 for event in ['shutdown', 'reboot', 'crash']:
332 if self['actions_after_' + event] not in CONFIG_RESTART_MODES:
333 raise XendConfigError('Invalid event handling mode: ' +
334 event)
336 def _builder_sanity_check(self):
337 if self['builder'] not in ('hvm', 'linux'):
338 raise XendConfigError('Invalid builder configuration')
340 def validate(self):
341 self._memory_sanity_check()
342 self._actions_sanity_check()
343 self._builder_sanity_check()
345 def _dominfo_to_xapi(self, dominfo):
346 self['domid'] = dominfo['domid']
347 self['online_vcpus'] = dominfo['online_vcpus']
348 self['max_vcpu_id'] = dominfo['max_vcpu_id']
349 self['memory_dynamic_min'] = (dominfo['mem_kb'] + 1023)/1024
350 self['memory_dynamic_max'] = (dominfo['maxmem_kb'] + 1023)/1024
351 self['cpu_time'] = dominfo['cpu_time']/1e9
352 # TODO: i don't know what the security stuff expects here
353 if dominfo.get('ssidref'):
354 self['security'] = [['ssidref', dominfo['ssidref']]]
355 self['shutdown_reason'] = dominfo['shutdown_reason']
357 # parse state into Xen API states
358 self['running'] = dominfo['running']
359 self['crashed'] = dominfo['crashed']
360 self['dying'] = dominfo['dying']
361 self['shutdown'] = dominfo['shutdown']
362 self['paused'] = dominfo['paused']
363 self['blocked'] = dominfo['blocked']
365 if 'name' in dominfo:
366 self['name_label'] = dominfo['name']
368 if 'handle' in dominfo:
369 self['uuid'] = uuid.toString(dominfo['handle'])
371 def _parse_sxp(self, sxp_cfg):
372 """ Populate this XendConfig using the parsed SXP.
374 @param sxp_cfg: Parsed SXP Configuration
375 @type sxp_cfg: list of lists
376 @rtype: dictionary
377 @return: A dictionary containing the parsed options of the SXP.
378 """
379 cfg = {}
381 # First step is to convert deprecated options to
382 # current equivalents.
384 restart = sxp.child_value(sxp_cfg, 'restart')
385 if restart:
386 if restart == 'onreboot':
387 cfg['on_poweroff'] = 'destroy'
388 cfg['on_reboot'] = 'restart'
389 cfg['on_crash'] = 'destroy'
390 elif restart == 'always':
391 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
392 cfg[opt] = 'restart'
393 elif restart == 'never':
394 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
395 cfg[opt] = 'never'
396 else:
397 log.warn('Ignoring unrecognised value for deprecated option:'
398 'restart = \'%s\'', restart)
400 # Only extract options we know about.
401 extract_keys = LEGACY_UNSUPPORTED_BY_XENAPI_CFG
402 extract_keys += XENAPI_CFG_TO_LEGACY_CFG.values()
404 for key in extract_keys:
405 val = sxp.child_value(sxp_cfg, key)
406 if val != None:
407 try:
408 cfg[key] = LEGACY_CFG_TYPES[key](val)
409 except KeyError:
410 cfg[key] = val
411 except (TypeError, ValueError), e:
412 log.warn("Unable to parse key %s: %s: %s" %
413 (key, str(val), e))
415 # Parsing the device SXP's. In most cases, the SXP looks
416 # like this:
417 #
418 # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]]
419 #
420 # However, for PCI devices it looks like this:
421 #
422 # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1]]]]
423 #
424 # It seems the reasoning for this difference is because
425 # pciif.py needs all the PCI device configurations at
426 # the same time when creating the devices.
427 #
428 # To further complicate matters, Xen 2.0 configuration format
429 # uses the following for pci device configuration:
430 #
431 # [device, [pci, [domain, 0], [bus, 0], [dev, 1], [func, 2]]]
432 #
433 # Hence we deal with pci device configurations outside of
434 # the regular device parsing.
436 cfg['devices'] = {}
437 for dev in sxp.children(sxp_cfg, 'device'):
438 config = sxp.child0(dev)
439 dev_type = sxp.name(config)
440 dev_info = {}
442 if dev_type == 'pci':
443 pci_devs_uuid = sxp.child_value(config, 'uuid',
444 uuid.createString())
445 pci_devs = []
446 for pci_dev in sxp.children(config, 'dev'):
447 pci_dev_info = {}
448 for opt, val in pci_dev[1:]:
449 pci_dev_info[opt] = val
450 pci_devs.append(pci_dev_info)
452 cfg['devices'][pci_devs_uuid] = (dev_type,
453 {'devs': pci_devs,
454 'uuid': pci_devs_uuid})
456 log.debug("XendConfig: reading device: %s" % pci_devs)
457 else:
458 for opt, val in config[1:]:
459 dev_info[opt] = val
460 log.debug("XendConfig: reading device: %s" % dev_info)
461 # create uuid if it doesn't
462 dev_uuid = dev_info.get('uuid', uuid.createString())
463 dev_info['uuid'] = dev_uuid
464 cfg['devices'][dev_uuid] = (dev_type, dev_info)
467 # Extract missing data from configuration entries
468 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
469 if image_sxp:
470 image_vcpus = sxp.child_value(image_sxp, 'vcpus')
471 if image_vcpus != None:
472 try:
473 if 'vcpus_number' not in cfg:
474 cfg['vcpus_number'] = int(image_vcpus)
475 elif cfg['vcpus_number'] != int(image_vcpus):
476 cfg['vcpus_number'] = int(image_vcpus)
477 log.warn('Overriding vcpus from %d to %d using image'
478 'vcpus value.', cfg['vcpus_number'])
479 except ValueError, e:
480 raise XendConfigError('integer expeceted: %s: %s' %
481 image_sxp, e)
483 # Deprecated cpu configuration
484 if 'cpu' in cfg:
485 if 'cpus' in cfg:
486 cfg['cpus'] = "%s,%s" % (str(cfg['cpu']), cfg['cpus'])
487 else:
488 cfg['cpus'] = str(cfg['cpu'])
490 # convert 'cpus' string to list of ints
491 # 'cpus' supports a list of ranges (0-3), seperated by
492 # commas, and negation, (^1).
493 # Precedence is settled by order of the string:
494 # "0-3,^1" -> [0,2,3]
495 # "0-3,^1,1" -> [0,1,2,3]
496 try:
497 if 'cpus' in cfg:
498 cpus = []
499 for c in cfg['cpus'].split(','):
500 if c.find('-') != -1:
501 (x, y) = c.split('-')
502 for i in range(int(x), int(y)+1):
503 cpus.append(int(i))
504 else:
505 # remove this element from the list
506 if c[0] == '^':
507 cpus = [x for x in cpus if x != int(c[1:])]
508 else:
509 cpus.append(int(c))
511 cfg['cpus'] = cpus
512 except ValueError, e:
513 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
515 if 'security' in cfg and isinstance(cfg['security'], str):
516 cfg['security'] = sxp.from_string(cfg['security'])
518 # TODO: get states
519 old_state = sxp.child_value(sxp_cfg, 'state')
520 if old_state:
521 for i in range(len(CONFIG_OLD_DOM_STATES)):
522 cfg[CONFIG_OLD_DOM_STATES[i]] = int(old_state[i] != '-')
524 return cfg
527 def _sxp_to_xapi(self, sxp_cfg):
528 """Read in an SXP Configuration object and
529 populate at much of the Xen API with valid values.
530 """
531 cfg = self._parse_sxp(sxp_cfg)
533 # Convert parameters that can be directly mapped from
534 # the Legacy Config to Xen API Config
536 for apikey, cfgkey in XENAPI_CFG_TO_LEGACY_CFG.items():
537 try:
538 type_conv = XENAPI_CFG_TYPES.get(apikey)
539 if callable(type_conv):
540 self[apikey] = type_conv(cfg[cfgkey])
541 else:
542 log.warn("Unconverted key: " + apikey)
543 self[apikey] = cfg[cfgkey]
544 except KeyError:
545 pass
547 # Convert Legacy "image" config to Xen API kernel_*
548 # configuration
549 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
550 if image_sxp:
551 self['kernel_kernel'] = sxp.child_value(image_sxp, 'kernel','')
552 self['kernel_initrd'] = sxp.child_value(image_sxp, 'ramdisk','')
553 kernel_args = sxp.child_value(image_sxp, 'args', '')
555 # attempt to extract extra arguments from SXP config
556 arg_ip = sxp.child_value(image_sxp, 'ip')
557 if arg_ip and not re.search(r'ip=[^ ]+', kernel_args):
558 kernel_args += ' ip=%s' % arg_ip
559 arg_root = sxp.child_value(image_sxp, 'root')
560 if arg_root and not re.search(r'root=[^ ]+', kernel_args):
561 kernel_args += ' root=%s' % arg_root
563 self['kernel_args'] = kernel_args
565 # Convert Legacy HVM parameters to Xen API configuration
566 self['platform_std_vga'] = bool0(cfg.get('stdvga', 0))
567 self['platform_serial'] = str(cfg.get('serial', ''))
568 self['platform_localtime'] = bool0(cfg.get('localtime', 0))
569 self['platform_enable_audio'] = bool0(cfg.get('soundhw', 0))
571 # Convert path to bootloader to boot_method
572 if not cfg.get('bootloader'):
573 if self.get('kernel_kernel','').endswith('hvmloader'):
574 self['boot_method'] = 'bios'
575 else:
576 self['boot_method'] = 'kernel_external'
577 else:
578 self['boot_method'] = 'grub'
580 # make sure a sane maximum is set
581 if self['memory_static_max'] <= 0:
582 self['memory_static_max'] = self['memory_static_min']
584 self['memory_dynamic_max'] = self['memory_static_max']
585 self['memory_dynamic_min'] = self['memory_static_min']
587 # set device references in the configuration
588 self['devices'] = cfg.get('devices', {})
590 self['vif_refs'] = []
591 self['vbd_refs'] = []
592 self['vtpm_refs'] = []
593 for dev_uuid, (dev_type, dev_info) in self['devices'].items():
594 if dev_type == 'vif':
595 self['vif_refs'].append(dev_uuid)
596 elif dev_type in ('vbd','tap'):
597 self['vbd_refs'].append(dev_uuid)
598 elif dev_type in ('vtpm',):
599 self['vtpm_refs'].append(dev_uuid)
602 def _sxp_to_xapi_unsupported(self, sxp_cfg):
603 """Read in an SXP configuration object and populate
604 values are that not related directly supported in
605 the Xen API.
606 """
608 # Parse and convert parameters used to configure
609 # the image (as well as HVM images)
610 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
611 if image_sxp:
612 image = {}
613 image['type'] = sxp.name(image_sxp)
614 for arg, conv in LEGACY_IMAGE_CFG:
615 val = sxp.child_value(image_sxp, arg, None)
616 if val != None:
617 image[arg] = conv(val)
619 image_hvm = {}
620 for arg, conv in LEGACY_IMAGE_HVM_CFG:
621 val = sxp.child_value(image_sxp, arg, None)
622 if val != None:
623 image_hvm[arg] = conv(val)
625 image_hvm_devices = {}
626 for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
627 val = sxp.child_value(image_sxp, arg, None)
628 if val != None:
629 image_hvm_devices[arg] = conv(val)
631 if image_hvm or image_hvm_devices:
632 image['hvm'] = image_hvm
633 image['hvm']['devices'] = image_hvm_devices
635 self['image'] = image
637 for apikey, imgkey in XENAPI_HVM_CFG.items():
638 val = sxp.child_value(image_sxp, imgkey, None)
639 if val != None:
640 self[apikey] = val
642 # extract backend value
644 backend = []
645 for c in sxp.children(sxp_cfg, 'backend'):
646 backend.append(sxp.name(sxp.child0(c)))
647 if backend:
648 self['backend'] = backend
650 if self['image'].has_key('hvm'):
651 self['builder'] = 'hvm'
653 # Parse and convert other Non Xen API parameters.
654 def _set_cfg_if_exists(sxp_arg):
655 val = sxp.child_value(sxp_cfg, sxp_arg)
656 if val != None:
657 if LEGACY_CFG_TYPES.get(sxp_arg):
658 self[sxp_arg] = LEGACY_CFG_TYPES[sxp_arg](val)
659 else:
660 self[sxp_arg] = val
662 _set_cfg_if_exists('shadow_memory')
663 _set_cfg_if_exists('security')
664 _set_cfg_if_exists('features')
665 _set_cfg_if_exists('on_xend_stop')
666 _set_cfg_if_exists('on_xend_start')
667 _set_cfg_if_exists('vcpu_avail')
668 _set_cfg_if_exists('max_vcpu_id') # TODO, deprecated?
670 # Parse and store runtime configuration
671 _set_cfg_if_exists('start_time')
672 _set_cfg_if_exists('online_vcpus')
673 _set_cfg_if_exists('cpu_time')
674 _set_cfg_if_exists('shutdown_reason')
675 _set_cfg_if_exists('up_time')
676 _set_cfg_if_exists('status') # TODO, deprecated
678 def _add_xapi_unsupported(self):
679 """Updates the configuration object with entries that are not
680 officially supported by the Xen API but is required for
681 the rest of Xend to function.
682 """
684 # populate image
685 self['image']['type'] = self['builder']
686 if self['builder'] == 'hvm':
687 self['image']['hvm'] = {}
688 for xapi, cfgapi in XENAPI_HVM_CFG.items():
689 self['image']['hvm'][cfgapi] = self[xapi]
692 def _get_old_state_string(self):
693 """Returns the old xm state string.
694 @rtype: string
695 @return: old state string
696 """
697 state_string = ''
698 for state_name in CONFIG_OLD_DOM_STATES:
699 on_off = self.get(state_name, 0)
700 if on_off:
701 state_string += state_name[0]
702 else:
703 state_string += '-'
705 return state_string
708 def update_config(self, dominfo):
709 """Update configuration with the output from xc.domain_getinfo().
711 @param dominfo: Domain information via xc.domain_getinfo()
712 @type dominfo: dict
713 """
714 self._dominfo_to_xapi(dominfo)
715 self.validate()
717 def to_xml(self):
718 """Return an XML string representing the configuration."""
719 pass
721 def to_sxp(self, domain = None, ignore_devices = False, ignore = []):
722 """ Get SXP representation of this config object.
724 Incompat: removed store_mfn, console_mfn
726 @keyword domain: (optional) XendDomainInfo to get extra information
727 from such as domid and running devices.
728 @type domain: XendDomainInfo
729 @keyword ignore: (optional) list of 'keys' that we do not want
730 to export.
731 @type ignore: list of strings
732 @rtype: list of list (SXP representation)
733 """
734 sxpr = ['domain']
736 # TODO: domid/dom is the same thing but called differently
737 # depending if it is from xenstore or sxpr.
739 if domain.getDomid() is not None:
740 sxpr.append(['domid', domain.getDomid()])
742 for xenapi, legacy in XENAPI_CFG_TO_LEGACY_CFG.items():
743 if self.has_key(xenapi) and self[xenapi] not in (None, []):
744 if type(self[xenapi]) == bool:
745 # convert booleans to ints before making an sxp item
746 sxpr.append([legacy, int(self[xenapi])])
747 else:
748 sxpr.append([legacy, self[xenapi]])
750 for legacy in LEGACY_UNSUPPORTED_BY_XENAPI_CFG:
751 if legacy in ('domid', 'uuid'): # skip these
752 continue
753 if self.has_key(legacy) and self[legacy] not in (None, []):
754 sxpr.append([legacy, self[legacy]])
756 if 'image' in self and self['image']:
757 sxpr.append(['image', self.image_sxpr()])
759 sxpr.append(['status', domain.state])
760 sxpr.append(['memory_dynamic_min', self.get('memory_dynamic_min')])
761 sxpr.append(['memory_dynamic_max', self.get('memory_dynamic_max')])
763 if domain.getDomid() is not None:
764 sxpr.append(['state', self._get_old_state_string()])
766 if domain:
767 if domain.store_mfn:
768 sxpr.append(['store_mfn', domain.store_mfn])
769 if domain.console_mfn:
770 sxpr.append(['console_mfn', domain.console_mfn])
773 # Marshall devices (running or from configuration)
774 if not ignore_devices:
775 for cls in XendDevices.valid_devices():
776 found = False
778 # figure if there is a device that is running
779 if domain:
780 try:
781 controller = domain.getDeviceController(cls)
782 configs = controller.configurations()
783 for config in configs:
784 sxpr.append(['device', config])
785 found = True
786 except:
787 log.exception("dumping sxp from device controllers")
788 pass
790 # if we didn't find that device, check the existing config
791 # for a device in the same class
792 if not found:
793 for dev_type, dev_info in self.all_devices_sxpr():
794 if dev_type == cls:
795 sxpr.append(['device', dev_info])
797 return sxpr
799 def device_add(self, dev_type, cfg_sxp = None, cfg_xenapi = None):
800 """Add a device configuration in SXP format or XenAPI struct format.
802 For SXP, it could be either:
804 [device, [vbd, [uname ...]]
806 or:
808 [vbd, [uname ..]]
810 @type cfg_sxp: list of lists (parsed sxp object)
811 @param cfg_sxp: SXP configuration object
812 @type cfg_xenapi: dict
813 @param cfg_xenapi: A device configuration from Xen API (eg. vbd,vif)
814 @rtype: string
815 @return: Assigned UUID of the device.
816 """
817 if dev_type not in XendDevices.valid_devices() and \
818 dev_type not in XendDevices.pseudo_devices():
819 raise XendConfigError("XendConfig: %s not a valid device type" %
820 dev_type)
822 if cfg_sxp == None and cfg_xenapi == None:
823 raise XendConfigError("XendConfig: device_add requires some "
824 "config.")
826 if cfg_sxp:
827 log.debug("XendConfig.device_add: %s" % str(cfg_sxp))
828 if cfg_xenapi:
829 log.debug("XendConfig.device_add: %s" % str(cfg_xenapi))
831 if cfg_sxp:
832 if sxp.child0(cfg_sxp) == 'device':
833 config = sxp.child0(cfg_sxp)
834 else:
835 config = cfg_sxp
837 dev_type = sxp.name(config)
838 dev_info = {}
840 try:
841 for opt, val in config[1:]:
842 dev_info[opt] = val
843 except ValueError:
844 pass # SXP has no options for this device
847 def _get_config_ipaddr(config):
848 val = []
849 for ipaddr in sxp.children(config, elt='ip'):
850 val.append(sxp.child0(ipaddr))
851 return val
853 if dev_type == 'vif' and 'ip' in dev_info:
854 dev_info['ip'] = _get_config_ipaddr(config)
856 if dev_type == 'vbd':
857 if dev_info.get('dev', '').startswith('ioemu:'):
858 dev_info['driver'] = 'ioemu'
859 else:
860 dev_info['driver'] = 'paravirtualised'
863 # create uuid if it doesn't exist
864 dev_uuid = dev_info.get('uuid', uuid.createString())
865 dev_info['uuid'] = dev_uuid
867 # store dev references by uuid for certain device types
868 self['devices'][dev_uuid] = (dev_type, dev_info)
869 if dev_type in ('vif', 'vbd', 'vtpm'):
870 self['%s_refs' % dev_type].append(dev_uuid)
871 elif dev_type in ('tap',):
872 self['vbd_refs'].append(dev_uuid)
874 return dev_uuid
876 if cfg_xenapi:
877 dev_info = {}
878 if dev_type == 'vif':
879 if cfg_xenapi.get('MAC'): # don't add if blank
880 dev_info['mac'] = cfg_xenapi.get('MAC')
881 # vifname is the name on the guest, not dom0
882 # TODO: we don't have the ability to find that out or
883 # change it from dom0
884 #if cfg_xenapi.get('device'): # don't add if blank
885 # dev_info['vifname'] = cfg_xenapi.get('device')
886 if cfg_xenapi.get('type'):
887 dev_info['type'] = cfg_xenapi.get('type')
889 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
890 dev_info['uuid'] = dev_uuid
891 self['devices'][dev_uuid] = (dev_type, dev_info)
892 self['vif_refs'].append(dev_uuid)
893 return dev_uuid
895 elif dev_type in ('vbd', 'tap'):
896 if dev_type == 'vbd':
897 dev_info['uname'] = cfg_xenapi.get('image', '')
898 dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
899 elif dev_type == 'tap':
900 dev_info['uname'] = 'tap:qcow:%s' % cfg_xenapi.get('image')
901 dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
903 dev_info['driver'] = cfg_xenapi.get('driver')
904 dev_info['VDI'] = cfg_xenapi.get('VDI', '')
906 if cfg_xenapi.get('mode') == 'RW':
907 dev_info['mode'] = 'w'
908 else:
909 dev_info['mode'] = 'r'
911 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
912 dev_info['uuid'] = dev_uuid
913 self['devices'][dev_uuid] = (dev_type, dev_info)
914 self['vbd_refs'].append(dev_uuid)
915 return dev_uuid
917 return ''
919 def device_update(self, dev_uuid, cfg_sxp):
920 """Update an existing device with the new configuration.
922 @rtype: boolean
923 @return: Returns True if succesfully found and updated a device conf
924 """
925 if dev_uuid in self['devices']:
926 config = sxp.child0(cfg_sxp)
927 dev_type = sxp.name(config)
928 dev_info = {}
930 try:
931 for opt, val in config[1:]:
932 self['devices'][opt] = val
933 except ValueError:
934 pass # SXP has no options for this device
936 return True
938 return False
941 def device_sxpr(self, dev_uuid = None, dev_type = None, dev_info = None):
942 """Get Device SXPR by either giving the device UUID or (type, config).
944 @rtype: list of lists
945 @return: device config sxpr
946 """
947 sxpr = []
948 if dev_uuid != None and dev_uuid in self['devices']:
949 dev_type, dev_info = self['devices'][dev_uuid]
951 if dev_type == None or dev_info == None:
952 raise XendConfigError("Required either UUID or device type and "
953 "configuration dictionary.")
955 sxpr.append(dev_type)
956 config = [(opt, val) for opt, val in dev_info.items()]
957 sxpr += config
959 return sxpr
961 def all_devices_sxpr(self):
962 """Returns the SXPR for all devices in the current configuration."""
963 sxprs = []
964 pci_devs = []
966 if 'devices' in self:
967 return sxprs
969 for dev_type, dev_info in self['devices'].values():
970 if dev_type == 'pci': # special case for pci devices
971 sxpr = [['uuid', dev_info['uuid']]]
972 for pci_dev_info in dev_info['devs']:
973 pci_dev_sxpr = ['dev']
974 for opt, val in pci_dev_info.items():
975 pci_dev_sxpr.append([opt, val])
976 sxpr.append(pci_dev_sxpr)
977 sxprs.append((dev_type, sxpr))
978 else:
979 sxpr = self.device_sxpr(dev_type = dev_type,
980 dev_info = dev_info)
981 sxprs.append((dev_type, sxpr))
983 return sxprs
985 def image_sxpr(self):
986 """Returns a backwards compatible image SXP expression that is
987 used in xenstore's /vm/<uuid>/image value and xm list."""
988 image = [self['image'].get('type', 'linux')]
989 if self.has_key('kernel_kernel'):
990 image.append(['kernel', self['kernel_kernel']])
991 if self.has_key('kernel_initrd') and self['kernel_initrd']:
992 image.append(['ramdisk', self['kernel_initrd']])
993 if self.has_key('kernel_args') and self['kernel_args']:
994 image.append(['args', self['kernel_args']])
996 for arg, conv in LEGACY_IMAGE_CFG:
997 if self['image'].has_key(arg):
998 image.append([arg, self['image'][arg]])
1000 if 'hvm' in self['image']:
1001 for arg, conv in LEGACY_IMAGE_HVM_CFG:
1002 if self['image']['hvm'].get(arg):
1003 image.append([arg, self['image']['hvm'][arg]])
1005 if 'hvm' in self['image'] and 'devices' in self['image']['hvm']:
1006 for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
1007 if self['image']['hvm']['devices'].get(arg):
1008 image.append([arg,
1009 self['image']['hvm']['devices'][arg]])
1011 return image
1013 def update_with_image_sxp(self, image_sxp):
1014 # Convert Legacy "image" config to Xen API kernel_*
1015 # configuration
1016 self['kernel_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1017 self['kernel_initrd'] = sxp.child_value(image_sxp, 'ramdisk','')
1018 kernel_args = sxp.child_value(image_sxp, 'args', '')
1020 # attempt to extract extra arguments from SXP config
1021 arg_ip = sxp.child_value(image_sxp, 'ip')
1022 if arg_ip and not re.search(r'ip=[^ ]+', kernel_args):
1023 kernel_args += ' ip=%s' % arg_ip
1024 arg_root = sxp.child_value(image_sxp, 'root')
1025 if arg_root and not re.search(r'root=', kernel_args):
1026 kernel_args += ' root=%s' % arg_root
1027 self['kernel_args'] = kernel_args
1029 # Store image SXP in python dictionary format
1030 image = {}
1031 image['type'] = sxp.name(image_sxp)
1032 for arg, conv in LEGACY_IMAGE_CFG:
1033 val = sxp.child_value(image_sxp, arg, None)
1034 if val != None:
1035 image[arg] = conv(val)
1037 image_hvm = {}
1038 for arg, conv in LEGACY_IMAGE_HVM_CFG:
1039 val = sxp.child_value(image_sxp, arg, None)
1040 if val != None:
1041 image_hvm[arg] = conv(val)
1043 image_hvm_devices = {}
1044 for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
1045 val = sxp.child_value(image_sxp, arg, None)
1046 if val != None:
1047 image_hvm_devices[arg] = conv(val)
1049 if image_hvm or image_hvm_devices:
1050 image['hvm'] = image_hvm
1051 image['hvm']['devices'] = image_hvm_devices
1053 self['image'] = image
1055 for apikey, imgkey in XENAPI_HVM_CFG.items():
1056 val = sxp.child_value(image_sxp, imgkey, None)
1057 if val != None:
1058 type_conv = XENAPI_CFG_TYPES[apikey]
1059 if callable(conv):
1060 self[apikey] = type_conv(val)
1061 else:
1062 self[apikey] = val
1066 # debugging
1069 if __name__ == "__main__":
1070 pass