ia64/xen-unstable

view tools/python/xen/xend/XendConfig.py @ 13730:fe02bdd43ac1

Set the log level back to WARN, reverting accidentally committed change.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author Ewan Mellor <ewan@xensource.com>
date Tue Jan 30 11:14:23 2007 +0000 (2007-01-30)
parents 1e88f0b736b5
children 5165b7ecbff5
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
81 #
82 # CPU fields:
83 #
84 # vcpus_number -- the maximum number of vcpus that this domain may ever have.
85 # aka XendDomainInfo.getVCpuCount().
86 # vcpus -- the legacy configuration name for above.
87 # max_vcpu_id -- vcpus_number - 1. This is given to us by Xen.
88 #
89 # cpus -- the list of pCPUs available to each vCPU.
90 #
91 # vcpu_avail: a bitmap telling the guest domain whether it may use each of
92 # its VCPUs. This is translated to
93 # <dompath>/cpu/<id>/availability = {online,offline} for use
94 # by the guest domain.
95 # online_vpcus -- the number of VCPUs currently up, as reported by Xen. This
96 # is changed by changing vcpu_avail, and waiting for the
97 # domain to respond.
98 #
101 # Mapping from XendConfig configuration keys to the old
102 # legacy configuration keys that map directly.
104 XENAPI_CFG_TO_LEGACY_CFG = {
105 'uuid': 'uuid',
106 'vcpus_number': 'vcpus',
107 'cpus': 'cpus',
108 'memory_static_min': 'memory',
109 'memory_static_max': 'maxmem',
110 'name_label': 'name',
111 'actions_after_shutdown': 'on_poweroff',
112 'actions_after_reboot': 'on_reboot',
113 'actions_after_crash': 'on_crash',
114 'platform_localtime': 'localtime',
115 'PV_bootloader': 'bootloader',
116 'PV_bootloader_args': 'bootloader_args',
117 }
119 LEGACY_CFG_TO_XENAPI_CFG = reverse_dict(XENAPI_CFG_TO_LEGACY_CFG)
121 # Mapping from XendConfig configuration keys to the old
122 # legacy configuration keys that are found in the 'image'
123 # SXP object.
124 XENAPI_HVM_CFG = {
125 'platform_std_vga': 'stdvga',
126 'platform_serial' : 'serial',
127 'platform_localtime': 'localtime',
128 'platform_keymap' : 'keymap',
129 }
131 # List of XendConfig configuration keys that have no direct equivalent
132 # in the old world.
134 XENAPI_CFG_TYPES = {
135 'uuid': str,
136 'power_state': str,
137 'name_label': str,
138 'name_description': str,
139 'user_version': str,
140 'is_a_template': bool0,
141 'resident_on': str,
142 'memory_static_min': int,
143 'memory_static_max': int,
144 'memory_dynamic_min': int,
145 'memory_dynamic_max': int,
146 'memory_actual': int,
147 'cpus': list,
148 'vcpus_policy': str,
149 'vcpus_params': str,
150 'vcpus_number': int,
151 'vcpus_features_required': list,
152 'vcpus_features_can_use': list,
153 'vcpus_features_force_on': list,
154 'vcpus_features_force_off': list,
155 'actions_after_shutdown': str,
156 'actions_after_reboot': str,
157 'actions_after_suspend': str,
158 'actions_after_crash': str,
159 'tpm_instance': int,
160 'tpm_backend': int,
161 'PV_bootloader': str,
162 'PV_kernel': str,
163 'PV_ramdisk': str,
164 'PV_args': str,
165 'PV_bootloader_args': str,
166 'HVM_boot': str,
167 'platform_std_vga': bool0,
168 'platform_serial': str,
169 'platform_localtime': bool0,
170 'platform_clock_offset': bool0,
171 'platform_enable_audio': bool0,
172 'platform_keymap': str,
173 'pci_bus': str,
174 'tools_version': dict,
175 'otherconfig': dict,
176 }
178 # List of legacy configuration keys that have no equivalent in the
179 # Xen API, but are still stored in XendConfig.
181 LEGACY_UNSUPPORTED_BY_XENAPI_CFG = [
182 # roundtripped (dynamic, unmodified)
183 'shadow_memory',
184 'security',
185 'vcpu_avail',
186 'cpu_weight',
187 'cpu_cap',
188 'features',
189 # read/write
190 'on_xend_start',
191 'on_xend_stop',
192 # read-only
193 'domid',
194 'start_time',
195 'cpu_time',
196 'online_vcpus',
197 # write-once
198 'cpu',
199 'cpus',
200 ]
202 LEGACY_CFG_TYPES = {
203 'uuid': str,
204 'name': str,
205 'vcpus': int,
206 'vcpu_avail': long,
207 'memory': int,
208 'shadow_memory': int,
209 'maxmem': int,
210 'start_time': float,
211 'cpu_cap': int,
212 'cpu_weight': int,
213 'cpu_time': float,
214 'features': str,
215 'localtime': int,
216 'name': str,
217 'on_poweroff': str,
218 'on_reboot': str,
219 'on_crash': str,
220 'on_xend_stop': str,
221 'on_xend_start': str,
222 'online_vcpus': int,
223 }
225 # Values that should be stored in xenstore's /vm/<uuid> that is used
226 # by Xend. Used in XendDomainInfo to restore running VM state from
227 # xenstore.
228 LEGACY_XENSTORE_VM_PARAMS = [
229 'uuid',
230 'name',
231 'vcpus',
232 'vcpu_avail',
233 'memory',
234 'shadow_memory',
235 'maxmem',
236 'start_time',
237 'name',
238 'on_poweroff',
239 'on_crash',
240 'on_reboot',
241 'on_xend_start',
242 'on_xend_stop',
243 ]
245 LEGACY_IMAGE_CFG = [
246 ('root', str),
247 ('ip', str),
248 ('nographic', int),
249 ('vnc', int),
250 ('sdl', int),
251 ('vncdisplay', int),
252 ('vncunused', int),
253 ('vncpasswd', str),
254 ]
256 LEGACY_IMAGE_HVM_CFG = [
257 ('device_model', str),
258 ('display', str),
259 ('xauthority', str),
260 ('vncconsole', int),
261 ('pae', int),
262 ('apic', int),
263 ]
265 LEGACY_IMAGE_HVM_DEVICES_CFG = [
266 ('acpi', int),
267 ('boot', str),
268 ('fda', str),
269 ('fdb', str),
270 ('isa', int),
271 ('keymap', str),
272 ('localtime', int),
273 ('serial', str),
274 ('stdvga', int),
275 ('soundhw', str),
276 ('usb', int),
277 ('usbdevice', str),
278 ('vcpus', int),
279 ]
281 LEGACY_DM = '/usr/lib/xen/bin/qemu-dm'
283 ##
284 ## Config Choices
285 ##
287 CONFIG_RESTART_MODES = ('restart', 'destroy', 'preserve', 'rename-restart')
288 CONFIG_OLD_DOM_STATES = ('running', 'blocked', 'paused', 'shutdown',
289 'crashed', 'dying')
291 class XendConfigError(VmError):
292 def __str__(self):
293 return 'Invalid Configuration: %s' % str(self.value)
295 ##
296 ## XendConfig Class (an extended dictionary)
297 ##
299 class XendConfig(dict):
300 """ The new Xend VM Configuration.
302 Stores the configuration in xenapi compatible format but retains
303 import and export functions for SXP.
304 """
305 def __init__(self, filename = None, sxp_obj = None,
306 xapi = None, dominfo = None):
308 dict.__init__(self)
309 self.update(self._defaults())
311 if filename:
312 try:
313 sxp_obj = sxp.parse(open(filename,'r'))
314 sxp_obj = sxp_obj[0]
315 except IOError, e:
316 raise XendConfigError("Unable to read file: %s" % filename)
318 if sxp_obj:
319 self._sxp_to_xapi(sxp_obj)
320 self._sxp_to_xapi_unsupported(sxp_obj)
321 elif xapi:
322 self.update_with_xenapi_config(xapi)
323 self._add_xapi_unsupported(xapi)
324 elif dominfo:
325 # output from xc.domain_getinfo
326 self._dominfo_to_xapi(dominfo)
328 log.debug('XendConfig.init: %s' % scrub_password(self))
330 # validators go here
331 self.validate()
333 """ In time, we should enable this type checking addition. It is great
334 also for tracking bugs and unintended writes to XendDomainInfo.info
335 def __setitem__(self, key, value):
336 type_conv = XENAPI_CFG_TYPES.get(key)
337 if callable(type_conv):
338 try:
339 dict.__setitem__(self, key, type_conv(value))
340 except (ValueError, TypeError):
341 raise XendConfigError("Wrong type for configuration value " +
342 "%s. Expected %s" %
343 (key, type_conv.__name__))
344 else:
345 dict.__setitem__(self, key, value)
346 """
348 def _defaults(self):
349 defaults = {
350 'uuid': uuid.createString(),
351 'name_label': 'Domain-Unnamed',
352 'actions_after_shutdown': 'destroy',
353 'actions_after_reboot': 'restart',
354 'actions_after_crash': 'restart',
355 'actions_after_suspend': '',
356 'features': '',
357 'PV_bootloader': '',
358 'PV_kernel': '',
359 'PV_ramdisk': '',
360 'PV_args': '',
361 'PV_bootloader_args': '',
362 'HVM_boot': '',
363 'memory_static_min': 0,
364 'memory_dynamic_min': 0,
365 'shadow_memory': 0,
366 'memory_static_max': 0,
367 'memory_dynamic_max': 0,
368 'memory_actual': 0,
369 'devices': {},
370 'image': {},
371 'security': None,
372 'on_xend_start': 'ignore',
373 'on_xend_stop': 'ignore',
374 'cpus': [],
375 'cpu_weight': 256,
376 'cpu_cap': 0,
377 'vcpus_number': 1,
378 'console_refs': [],
379 'vif_refs': [],
380 'vbd_refs': [],
381 'vtpm_refs': [],
382 }
384 defaults['name_label'] = 'Domain-' + defaults['uuid']
385 return defaults
387 def _memory_sanity_check(self):
388 if self['memory_static_min'] == 0:
389 self['memory_static_min'] = self['memory_dynamic_min']
391 # If the static max is not set, let's set it to dynamic max.
392 # If the static max is smaller than static min, then fix it!
393 self['memory_static_max'] = max(self['memory_static_max'],
394 self['memory_dynamic_max'],
395 self['memory_static_min'])
397 for mem_type in ('memory_static_min', 'memory_static_max'):
398 if self[mem_type] <= 0:
399 raise XendConfigError('Memory value too low for %s: %d' %
400 (mem_type, self[mem_type]))
402 def _actions_sanity_check(self):
403 for event in ['shutdown', 'reboot', 'crash']:
404 if self['actions_after_' + event] not in CONFIG_RESTART_MODES:
405 raise XendConfigError('Invalid event handling mode: ' +
406 event)
408 def _vcpus_sanity_check(self):
409 if 'vcpus_number' in self and 'vcpu_avail' not in self:
410 self['vcpu_avail'] = (1 << self['vcpus_number']) - 1
412 def _uuid_sanity_check(self):
413 """Make sure UUID is in proper string format with hyphens."""
414 self['uuid'] = uuid.toString(uuid.fromString(self['uuid']))
416 def validate(self):
417 self._memory_sanity_check()
418 self._actions_sanity_check()
419 self._vcpus_sanity_check()
420 self._uuid_sanity_check()
422 def _dominfo_to_xapi(self, dominfo):
423 self['domid'] = dominfo['domid']
424 self['online_vcpus'] = dominfo['online_vcpus']
425 self['vcpus_number'] = dominfo['max_vcpu_id'] + 1
426 self['memory_dynamic_min'] = (dominfo['mem_kb'] + 1023)/1024
427 self['memory_dynamic_max'] = (dominfo['maxmem_kb'] + 1023)/1024
428 self['cpu_time'] = dominfo['cpu_time']/1e9
429 # TODO: i don't know what the security stuff expects here
430 if dominfo.get('ssidref'):
431 self['security'] = [['ssidref', dominfo['ssidref']]]
432 self['shutdown_reason'] = dominfo['shutdown_reason']
434 # parse state into Xen API states
435 self['running'] = dominfo['running']
436 self['crashed'] = dominfo['crashed']
437 self['dying'] = dominfo['dying']
438 self['shutdown'] = dominfo['shutdown']
439 self['paused'] = dominfo['paused']
440 self['blocked'] = dominfo['blocked']
442 if 'name' in dominfo:
443 self['name_label'] = dominfo['name']
445 if 'handle' in dominfo:
446 self['uuid'] = uuid.toString(dominfo['handle'])
448 def _parse_sxp(self, sxp_cfg):
449 """ Populate this XendConfig using the parsed SXP.
451 @param sxp_cfg: Parsed SXP Configuration
452 @type sxp_cfg: list of lists
453 @rtype: dictionary
454 @return: A dictionary containing the parsed options of the SXP.
455 """
456 cfg = {}
458 for key, typ in XENAPI_CFG_TYPES.items():
459 val = sxp.child_value(sxp_cfg, key)
460 if val is not None:
461 cfg[key] = typ(val)
463 # Convert deprecated options to current equivalents.
465 restart = sxp.child_value(sxp_cfg, 'restart')
466 if restart:
467 if restart == 'onreboot':
468 cfg['on_poweroff'] = 'destroy'
469 cfg['on_reboot'] = 'restart'
470 cfg['on_crash'] = 'destroy'
471 elif restart == 'always':
472 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
473 cfg[opt] = 'restart'
474 elif restart == 'never':
475 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
476 cfg[opt] = 'never'
477 else:
478 log.warn('Ignoring unrecognised value for deprecated option:'
479 'restart = \'%s\'', restart)
481 # Only extract options we know about.
482 extract_keys = LEGACY_UNSUPPORTED_BY_XENAPI_CFG
483 extract_keys += XENAPI_CFG_TO_LEGACY_CFG.values()
485 for key in extract_keys:
486 val = sxp.child_value(sxp_cfg, key)
487 if val != None:
488 try:
489 cfg[key] = LEGACY_CFG_TYPES[key](val)
490 except KeyError:
491 cfg[key] = val
492 except (TypeError, ValueError), e:
493 log.warn("Unable to parse key %s: %s: %s" %
494 (key, str(val), e))
496 # Parsing the device SXP's. In most cases, the SXP looks
497 # like this:
498 #
499 # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]]
500 #
501 # However, for PCI devices it looks like this:
502 #
503 # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1]]]]
504 #
505 # It seems the reasoning for this difference is because
506 # pciif.py needs all the PCI device configurations at
507 # the same time when creating the devices.
508 #
509 # To further complicate matters, Xen 2.0 configuration format
510 # uses the following for pci device configuration:
511 #
512 # [device, [pci, [domain, 0], [bus, 0], [dev, 1], [func, 2]]]
513 #
514 # Hence we deal with pci device configurations outside of
515 # the regular device parsing.
517 cfg['devices'] = {}
518 for dev in sxp.children(sxp_cfg, 'device'):
519 config = sxp.child0(dev)
520 dev_type = sxp.name(config)
521 dev_info = {}
523 if dev_type == 'pci':
524 pci_devs_uuid = sxp.child_value(config, 'uuid',
525 uuid.createString())
526 pci_devs = []
527 for pci_dev in sxp.children(config, 'dev'):
528 pci_dev_info = {}
529 for opt_val in pci_dev[1:]:
530 try:
531 opt, val = opt_val
532 pci_dev_info[opt] = val
533 except TypeError:
534 pass
535 pci_devs.append(pci_dev_info)
537 cfg['devices'][pci_devs_uuid] = (dev_type,
538 {'devs': pci_devs,
539 'uuid': pci_devs_uuid})
541 log.debug("XendConfig: reading device: %s" % pci_devs)
542 else:
543 self.device_add(dev_type, cfg_sxp = config, target = cfg)
544 log.debug("XendConfig: reading device: %s" % scrub_password(dev_info))
546 # Extract missing data from configuration entries
547 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
548 if image_sxp:
549 image_vcpus = sxp.child_value(image_sxp, 'vcpus')
550 if image_vcpus != None:
551 try:
552 if 'vcpus_number' not in cfg:
553 cfg['vcpus_number'] = int(image_vcpus)
554 elif cfg['vcpus_number'] != int(image_vcpus):
555 cfg['vcpus_number'] = int(image_vcpus)
556 log.warn('Overriding vcpus from %d to %d using image'
557 'vcpus value.', cfg['vcpus_number'])
558 except ValueError, e:
559 raise XendConfigError('integer expeceted: %s: %s' %
560 image_sxp, e)
562 # Deprecated cpu configuration
563 if 'cpu' in cfg:
564 if 'cpus' in cfg:
565 cfg['cpus'] = "%s,%s" % (str(cfg['cpu']), cfg['cpus'])
566 else:
567 cfg['cpus'] = str(cfg['cpu'])
569 # convert 'cpus' string to list of ints
570 # 'cpus' supports a list of ranges (0-3), seperated by
571 # commas, and negation, (^1).
572 # Precedence is settled by order of the string:
573 # "0-3,^1" -> [0,2,3]
574 # "0-3,^1,1" -> [0,1,2,3]
575 try:
576 if 'cpus' in cfg and type(cfg['cpus']) != list:
577 cpus = []
578 for c in cfg['cpus'].split(','):
579 if c.find('-') != -1:
580 (x, y) = c.split('-')
581 for i in range(int(x), int(y)+1):
582 cpus.append(int(i))
583 else:
584 # remove this element from the list
585 if c[0] == '^':
586 cpus = [x for x in cpus if x != int(c[1:])]
587 else:
588 cpus.append(int(c))
590 cfg['cpus'] = cpus
591 except ValueError, e:
592 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
594 if 'security' in cfg and isinstance(cfg['security'], str):
595 cfg['security'] = sxp.from_string(cfg['security'])
597 old_state = sxp.child_value(sxp_cfg, 'state')
598 if old_state:
599 for i in range(len(CONFIG_OLD_DOM_STATES)):
600 cfg[CONFIG_OLD_DOM_STATES[i]] = int(old_state[i] != '-')
602 return cfg
605 def _sxp_to_xapi(self, sxp_cfg):
606 """Read in an SXP Configuration object and
607 populate at much of the Xen API with valid values.
608 """
609 log.debug('_sxp_to_xapi(%s)' % scrub_password(sxp_cfg))
611 cfg = self._parse_sxp(sxp_cfg)
613 for key, typ in XENAPI_CFG_TYPES.items():
614 val = cfg.get(key)
615 if val is not None:
616 self[key] = typ(val)
618 # Convert parameters that can be directly mapped from
619 # the Legacy Config to Xen API Config
621 for apikey, cfgkey in XENAPI_CFG_TO_LEGACY_CFG.items():
622 try:
623 type_conv = XENAPI_CFG_TYPES.get(apikey)
624 if callable(type_conv):
625 self[apikey] = type_conv(cfg[cfgkey])
626 else:
627 log.warn("Unconverted key: " + apikey)
628 self[apikey] = cfg[cfgkey]
629 except KeyError:
630 pass
632 def update_with(n, o):
633 if not self.get(n):
634 self[n] = cfg.get(o, '')
636 update_with('PV_bootloader', 'bootloader')
637 update_with('PV_bootloader_args', 'bootloader_args')
639 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
640 if image_sxp:
641 self.update_with_image_sxp(image_sxp)
643 # Convert Legacy HVM parameters to Xen API configuration
644 self['platform_std_vga'] = bool0(cfg.get('stdvga', 0))
645 self['platform_serial'] = str(cfg.get('serial', ''))
646 self['platform_localtime'] = bool0(cfg.get('localtime', 0))
647 self['platform_enable_audio'] = bool0(cfg.get('soundhw', 0))
649 # make sure a sane maximum is set
650 if self['memory_static_max'] <= 0:
651 self['memory_static_max'] = self['memory_static_min']
653 self['memory_dynamic_max'] = self['memory_static_max']
654 self['memory_dynamic_min'] = self['memory_static_min']
656 # set device references in the configuration
657 self['devices'] = cfg.get('devices', {})
658 self['console_refs'] = cfg.get('console_refs', [])
659 self['vif_refs'] = cfg.get('vif_refs', [])
660 self['vbd_refs'] = cfg.get('vbd_refs', [])
661 self['vtpm_refs'] = cfg.get('vtpm_refs', [])
664 def _sxp_to_xapi_unsupported(self, sxp_cfg):
665 """Read in an SXP configuration object and populate
666 values are that not related directly supported in
667 the Xen API.
668 """
670 log.debug('_sxp_to_xapi_unsupported(%s)' % scrub_password(sxp_cfg))
672 # Parse and convert parameters used to configure
673 # the image (as well as HVM images)
674 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
675 if image_sxp:
676 image = {}
677 image['type'] = sxp.name(image_sxp)
678 for arg, conv in LEGACY_IMAGE_CFG:
679 val = sxp.child_value(image_sxp, arg, None)
680 if val != None:
681 image[arg] = conv(val)
683 image_hvm = {}
684 for arg, conv in LEGACY_IMAGE_HVM_CFG:
685 val = sxp.child_value(image_sxp, arg, None)
686 if val != None:
687 image_hvm[arg] = conv(val)
689 image_hvm_devices = {}
690 for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
691 val = sxp.child_value(image_sxp, arg, None)
692 if val != None:
693 image_hvm_devices[arg] = conv(val)
695 if image_hvm or image_hvm_devices:
696 image['hvm'] = image_hvm
697 image['hvm']['devices'] = image_hvm_devices
699 self['image'] = image
701 for apikey, imgkey in XENAPI_HVM_CFG.items():
702 val = sxp.child_value(image_sxp, imgkey, None)
703 if val != None:
704 self[apikey] = val
706 # extract backend value
708 backend = []
709 for c in sxp.children(sxp_cfg, 'backend'):
710 backend.append(sxp.name(sxp.child0(c)))
711 if backend:
712 self['backend'] = backend
714 # Parse and convert other Non Xen API parameters.
715 def _set_cfg_if_exists(sxp_arg):
716 val = sxp.child_value(sxp_cfg, sxp_arg)
717 if val != None:
718 if LEGACY_CFG_TYPES.get(sxp_arg):
719 self[sxp_arg] = LEGACY_CFG_TYPES[sxp_arg](val)
720 else:
721 self[sxp_arg] = val
723 _set_cfg_if_exists('shadow_memory')
724 _set_cfg_if_exists('security')
725 _set_cfg_if_exists('features')
726 _set_cfg_if_exists('on_xend_stop')
727 _set_cfg_if_exists('on_xend_start')
728 _set_cfg_if_exists('vcpu_avail')
729 _set_cfg_if_exists('cpu_weight')
730 _set_cfg_if_exists('cpu_cap')
732 # Parse and store runtime configuration
733 _set_cfg_if_exists('start_time')
734 _set_cfg_if_exists('cpu_time')
735 _set_cfg_if_exists('shutdown_reason')
736 _set_cfg_if_exists('up_time')
737 _set_cfg_if_exists('status') # TODO, deprecated
739 def _add_xapi_unsupported(self, xapi_dict):
740 """Updates the configuration object with entries that are not
741 officially supported by the Xen API but is required for
742 the rest of Xend to function.
743 """
745 # populate image
746 if 'image' in xapi_dict:
747 self['image'].update(xapi_dict['image'])
748 else:
749 hvm = self['HVM_boot'] != ''
750 self['image']['type'] = hvm and 'hvm' or 'linux'
751 if hvm:
752 self['image']['hvm'] = {'devices': {}}
753 for xapi, cfgapi in XENAPI_HVM_CFG.items():
754 if xapi in self:
755 self['image']['hvm']['devices'][cfgapi] = self[xapi]
757 # currently unsupported options
758 self['image']['hvm']['device_model'] = LEGACY_DM
759 self['image']['vnc'] = 1
760 self['image']['hvm']['pae'] = 1
762 if self['platform_enable_audio']:
763 self['image']['hvm']['devices']['soundhw'] = 'sb16'
766 def _get_old_state_string(self):
767 """Returns the old xm state string.
768 @rtype: string
769 @return: old state string
770 """
771 state_string = ''
772 for state_name in CONFIG_OLD_DOM_STATES:
773 on_off = self.get(state_name, 0)
774 if on_off:
775 state_string += state_name[0]
776 else:
777 state_string += '-'
779 return state_string
782 def update_config(self, dominfo):
783 """Update configuration with the output from xc.domain_getinfo().
785 @param dominfo: Domain information via xc.domain_getinfo()
786 @type dominfo: dict
787 """
788 self._dominfo_to_xapi(dominfo)
789 self.validate()
791 def update_with_xenapi_config(self, xapi):
792 """Update configuration with a Xen API VM struct
794 @param xapi: Xen API VM Struct
795 @type xapi: dict
796 """
798 log.debug('update_with_xenapi_config: %s' % scrub_password(xapi))
800 for key, val in xapi.items():
801 type_conv = XENAPI_CFG_TYPES.get(key)
802 if type_conv is None:
803 key = key.lower()
804 type_conv = XENAPI_CFG_TYPES.get(key)
805 if callable(type_conv):
806 self[key] = type_conv(val)
807 else:
808 self[key] = val
810 self.validate()
812 def to_sxp(self, domain = None, ignore_devices = False, ignore = [],
813 legacy_only = True):
814 """ Get SXP representation of this config object.
816 Incompat: removed store_mfn, console_mfn
818 @keyword domain: (optional) XendDomainInfo to get extra information
819 from such as domid and running devices.
820 @type domain: XendDomainInfo
821 @keyword ignore: (optional) list of 'keys' that we do not want
822 to export.
823 @type ignore: list of strings
824 @rtype: list of list (SXP representation)
825 """
826 sxpr = ['domain']
828 # TODO: domid/dom is the same thing but called differently
829 # depending if it is from xenstore or sxpr.
831 if domain.getDomid() is not None:
832 sxpr.append(['domid', domain.getDomid()])
834 if not legacy_only:
835 for name in XENAPI_CFG_TYPES.keys():
836 if name in self and self[name] not in (None, []):
837 sxpr.append([name, str(self[name])])
839 for xenapi, legacy in XENAPI_CFG_TO_LEGACY_CFG.items():
840 if self.has_key(xenapi) and self[xenapi] not in (None, []):
841 if type(self[xenapi]) == bool:
842 # convert booleans to ints before making an sxp item
843 sxpr.append([legacy, int(self[xenapi])])
844 else:
845 sxpr.append([legacy, self[xenapi]])
847 for legacy in LEGACY_UNSUPPORTED_BY_XENAPI_CFG:
848 if legacy in ('domid', 'uuid'): # skip these
849 continue
850 if self.has_key(legacy) and self[legacy] not in (None, []):
851 sxpr.append([legacy, self[legacy]])
853 if 'image' in self and self['image']:
854 sxpr.append(['image', self.image_sxpr()])
856 sxpr.append(['status', domain.state])
857 sxpr.append(['memory_dynamic_min', self.get('memory_dynamic_min')])
858 sxpr.append(['memory_dynamic_max', self.get('memory_dynamic_max')])
860 if domain.getDomid() is not None:
861 sxpr.append(['state', self._get_old_state_string()])
863 if domain:
864 if domain.store_mfn:
865 sxpr.append(['store_mfn', domain.store_mfn])
866 if domain.console_mfn:
867 sxpr.append(['console_mfn', domain.console_mfn])
870 # Marshall devices (running or from configuration)
871 if not ignore_devices:
872 for cls in XendDevices.valid_devices():
873 found = False
875 # figure if there is a dev controller is valid and running
876 if domain and domain.getDomid() != None:
877 try:
878 controller = domain.getDeviceController(cls)
879 configs = controller.configurations()
880 for config in configs:
881 if sxp.name(config) in ('vbd', 'tap'):
882 # The bootable flag is never written to the
883 # store as part of the device config.
884 uuid = sxp.child_value(sxpr, 'uuid')
885 sxpr.append(
886 'bootable',
887 self['devices'][dev_uuid]['bootable'])
888 sxpr.append(['device', config])
890 found = True
891 except:
892 log.exception("dumping sxp from device controllers")
893 pass
895 # if we didn't find that device, check the existing config
896 # for a device in the same class
897 if not found:
898 for dev_type, dev_info in self.all_devices_sxpr():
899 if dev_type == cls:
900 sxpr.append(['device', dev_info])
902 return sxpr
904 def device_add(self, dev_type, cfg_sxp = None, cfg_xenapi = None,
905 target = None):
906 """Add a device configuration in SXP format or XenAPI struct format.
908 For SXP, it could be either:
910 [device, [vbd, [uname ...]]
912 or:
914 [vbd, [uname ..]]
916 @type cfg_sxp: list of lists (parsed sxp object)
917 @param cfg_sxp: SXP configuration object
918 @type cfg_xenapi: dict
919 @param cfg_xenapi: A device configuration from Xen API (eg. vbd,vif)
920 @param target: write device information to
921 @type target: None or a dictionary
922 @rtype: string
923 @return: Assigned UUID of the device.
924 """
925 if target == None:
926 target = self
928 if dev_type not in XendDevices.valid_devices():
929 raise XendConfigError("XendConfig: %s not a valid device type" %
930 dev_type)
932 if cfg_sxp == None and cfg_xenapi == None:
933 raise XendConfigError("XendConfig: device_add requires some "
934 "config.")
936 #if cfg_sxp:
937 # log.debug("XendConfig.device_add: %s" % str(cfg_sxp))
938 #if cfg_xenapi:
939 # log.debug("XendConfig.device_add: %s" % str(cfg_xenapi))
941 if cfg_sxp:
942 if sxp.child0(cfg_sxp) == 'device':
943 config = sxp.child0(cfg_sxp)
944 else:
945 config = cfg_sxp
947 dev_type = sxp.name(config)
948 dev_info = {}
950 for opt_val in config[1:]:
951 try:
952 opt, val = opt_val
953 dev_info[opt] = val
954 except (TypeError, ValueError): # unpack error
955 pass
957 if dev_type == 'vbd':
958 dev_info['bootable'] = False
959 if dev_info.get('dev', '').startswith('ioemu:'):
960 dev_info['driver'] = 'ioemu'
961 else:
962 dev_info['driver'] = 'paravirtualised'
964 # create uuid if it doesn't exist
965 dev_uuid = dev_info.get('uuid', uuid.createString())
966 dev_info['uuid'] = dev_uuid
968 # store dev references by uuid for certain device types
969 target['devices'][dev_uuid] = (dev_type, dev_info)
970 if dev_type in ('vif', 'vbd', 'vtpm'):
971 param = '%s_refs' % dev_type
972 if param not in target:
973 target[param] = []
974 if dev_uuid not in target[param]:
975 if dev_type == 'vbd' and not target[param]:
976 # Compat hack -- this is the first disk, so mark it
977 # bootable.
978 dev_info['bootable'] = True
979 target[param].append(dev_uuid)
980 elif dev_type == 'tap':
981 if 'vbd_refs' not in target:
982 target['vbd_refs'] = []
983 if dev_uuid not in target['vbd_refs']:
984 if not target['vbd_refs']:
985 # Compat hack -- this is the first disk, so mark it
986 # bootable.
987 dev_info['bootable'] = True
988 target['vbd_refs'].append(dev_uuid)
989 elif dev_type == 'console':
990 if 'console_refs' not in target:
991 target['console_refs'] = []
992 if dev_uuid not in target['console_refs']:
993 target['console_refs'].append(dev_uuid)
995 return dev_uuid
997 if cfg_xenapi:
998 dev_info = {}
999 dev_uuid = ''
1000 if dev_type == 'vif':
1001 if cfg_xenapi.get('MAC'): # don't add if blank
1002 dev_info['mac'] = cfg_xenapi.get('MAC')
1003 # vifname is the name on the guest, not dom0
1004 # TODO: we don't have the ability to find that out or
1005 # change it from dom0
1006 #if cfg_xenapi.get('device'): # don't add if blank
1007 # dev_info['vifname'] = cfg_xenapi.get('device')
1008 if cfg_xenapi.get('type'):
1009 dev_info['type'] = cfg_xenapi.get('type')
1010 if cfg_xenapi.get('name'):
1011 dev_info['name'] = cfg_xenapi.get('name')
1013 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
1014 dev_info['uuid'] = dev_uuid
1015 target['devices'][dev_uuid] = (dev_type, dev_info)
1016 target['vif_refs'].append(dev_uuid)
1018 elif dev_type in ('vbd', 'tap'):
1019 dev_info['type'] = cfg_xenapi.get('type', 'Disk')
1020 if dev_info['type'] == 'CD':
1021 old_vbd_type = 'cdrom'
1022 else:
1023 old_vbd_type = 'disk'
1025 dev_info['uname'] = cfg_xenapi.get('image', '')
1026 dev_info['dev'] = '%s:%s' % (cfg_xenapi.get('device'),
1027 old_vbd_type)
1028 dev_info['bootable'] = cfg_xenapi.get('bootable', False)
1029 dev_info['driver'] = cfg_xenapi.get('driver')
1030 dev_info['VDI'] = cfg_xenapi.get('VDI', '')
1032 if cfg_xenapi.get('mode') == 'RW':
1033 dev_info['mode'] = 'w'
1034 else:
1035 dev_info['mode'] = 'r'
1037 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
1038 dev_info['uuid'] = dev_uuid
1039 target['devices'][dev_uuid] = (dev_type, dev_info)
1040 target['vbd_refs'].append(dev_uuid)
1042 elif dev_type == 'vtpm':
1043 if cfg_xenapi.get('type'):
1044 dev_info['type'] = cfg_xenapi.get('type')
1046 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
1047 dev_info['uuid'] = dev_uuid
1048 target['devices'][dev_uuid] = (dev_type, dev_info)
1049 target['vtpm_refs'].append(dev_uuid)
1051 return dev_uuid
1053 # no valid device to add
1054 return ''
1056 def console_add(self, protocol, uri):
1057 dev_uuid = uuid.createString()
1058 dev_info = {
1059 'uuid': dev_uuid,
1060 'protocol': protocol,
1061 'uri': uri
1063 if 'devices' not in self:
1064 self['devices'] = {}
1066 self['devices'][dev_uuid] = ('console', dev_info)
1067 self['console_refs'].append(dev_uuid)
1068 return dev_info
1070 def console_get_all(self, protocol):
1071 consoles = [dinfo for dtype, dinfo in self['devices'].values()
1072 if dtype == 'console']
1073 return [c for c in consoles if c.get('protocol') == protocol]
1075 def device_update(self, dev_uuid, cfg_sxp):
1076 """Update an existing device with the new configuration.
1078 @rtype: boolean
1079 @return: Returns True if succesfully found and updated a device conf
1080 """
1081 if dev_uuid in self['devices']:
1082 if sxp.child0(cfg_sxp) == 'device':
1083 config = sxp.child0(cfg_sxp)
1084 else:
1085 config = cfg_sxp
1087 for opt_val in config[1:]:
1088 try:
1089 opt, val = opt_val
1090 self['devices'][dev_uuid][opt] = val
1091 except (TypeError, ValueError):
1092 pass # no value for this config option
1094 return True
1096 return False
1099 def device_sxpr(self, dev_uuid = None, dev_type = None, dev_info = None):
1100 """Get Device SXPR by either giving the device UUID or (type, config).
1102 @rtype: list of lists
1103 @return: device config sxpr
1104 """
1105 sxpr = []
1106 if dev_uuid != None and dev_uuid in self['devices']:
1107 dev_type, dev_info = self['devices'][dev_uuid]
1109 if dev_type == None or dev_info == None:
1110 raise XendConfigError("Required either UUID or device type and "
1111 "configuration dictionary.")
1113 sxpr.append(dev_type)
1114 config = [(opt, val) for opt, val in dev_info.items()]
1115 sxpr += config
1117 return sxpr
1119 def ordered_device_refs(self):
1120 result = (self['console_refs'] +
1121 self['vbd_refs'] +
1122 self['vif_refs'] +
1123 self['vtpm_refs'])
1124 result.extend([u for u in self['devices'].keys() if u not in result])
1125 return result
1127 def all_devices_sxpr(self):
1128 """Returns the SXPR for all devices in the current configuration."""
1129 sxprs = []
1130 pci_devs = []
1132 if 'devices' not in self:
1133 return sxprs
1135 ordered_refs = self.ordered_device_refs()
1136 for dev_uuid in ordered_refs:
1137 dev_type, dev_info = self['devices'][dev_uuid]
1138 if dev_type == 'pci': # special case for pci devices
1139 sxpr = [['uuid', dev_info['uuid']]]
1140 for pci_dev_info in dev_info['devs']:
1141 pci_dev_sxpr = ['dev']
1142 for opt, val in pci_dev_info.items():
1143 pci_dev_sxpr.append([opt, val])
1144 sxpr.append(pci_dev_sxpr)
1145 sxprs.append((dev_type, sxpr))
1146 else:
1147 sxpr = self.device_sxpr(dev_type = dev_type,
1148 dev_info = dev_info)
1149 sxprs.append((dev_type, sxpr))
1151 return sxprs
1153 def image_sxpr(self):
1154 """Returns a backwards compatible image SXP expression that is
1155 used in xenstore's /vm/<uuid>/image value and xm list."""
1156 image = [self['image'].get('type', 'linux')]
1157 if self.has_key('PV_kernel'):
1158 image.append(['kernel', self['PV_kernel']])
1159 if self.has_key('PV_ramdisk') and self['PV_ramdisk']:
1160 image.append(['ramdisk', self['PV_ramdisk']])
1161 if self.has_key('PV_args') and self['PV_args']:
1162 image.append(['args', self['PV_args']])
1164 for arg, conv in LEGACY_IMAGE_CFG:
1165 if self['image'].has_key(arg):
1166 image.append([arg, self['image'][arg]])
1168 if 'hvm' in self['image']:
1169 for arg, conv in LEGACY_IMAGE_HVM_CFG:
1170 if self['image']['hvm'].get(arg):
1171 image.append([arg, conv(self['image']['hvm'][arg])])
1173 if 'hvm' in self['image'] and 'devices' in self['image']['hvm']:
1174 for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
1175 val = self['image']['hvm']['devices'].get(arg)
1176 if val != None:
1177 try:
1178 if conv: val = conv(val)
1179 except (ValueError, TypeError):
1180 if type(val) == bool: val = int(val)
1182 image.append([arg, val])
1184 return image
1186 def update_with_image_sxp(self, image_sxp, bootloader = False):
1187 # Convert Legacy "image" config to Xen API PV_*
1188 # configuration
1189 log.debug("update_with_image_sxp(%s)" % scrub_password(image_sxp))
1191 # user-specified args must come last: previous releases did this and
1192 # some domU kernels rely upon the ordering.
1193 kernel_args = sxp.child_value(image_sxp, 'args', '')
1195 # attempt to extract extra arguments from SXP config
1196 arg_ip = sxp.child_value(image_sxp, 'ip')
1197 if arg_ip and not re.search(r'ip=[^ ]+', kernel_args):
1198 kernel_args = 'ip=%s ' % arg_ip + kernel_args
1199 arg_root = sxp.child_value(image_sxp, 'root')
1200 if arg_root and not re.search(r'root=', kernel_args):
1201 kernel_args = 'root=%s ' % arg_root + kernel_args
1203 if bootloader:
1204 self['_temp_using_bootloader'] = '1'
1205 self['_temp_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1206 self['_temp_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1207 self['_temp_args'] = kernel_args
1208 else:
1209 self['PV_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1210 self['PV_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1211 self['PV_args'] = kernel_args
1213 # Store image SXP in python dictionary format
1214 image = {}
1215 image['type'] = sxp.name(image_sxp)
1216 for arg, conv in LEGACY_IMAGE_CFG:
1217 val = sxp.child_value(image_sxp, arg, None)
1218 if val != None:
1219 image[arg] = conv(val)
1221 image_hvm = {}
1222 for arg, conv in LEGACY_IMAGE_HVM_CFG:
1223 val = sxp.child_value(image_sxp, arg, None)
1224 if val != None:
1225 image_hvm[arg] = conv(val)
1227 image_hvm_devices = {}
1228 for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
1229 val = sxp.child_value(image_sxp, arg, None)
1230 if val != None:
1231 try:
1232 image_hvm_devices[arg] = conv(val)
1233 except (ValueError, TypeError):
1234 image_hvm_devices[arg] = val
1237 if image_hvm or image_hvm_devices:
1238 image['hvm'] = image_hvm
1239 image['hvm']['devices'] = image_hvm_devices
1241 self['image'] = image
1243 for apikey, imgkey in XENAPI_HVM_CFG.items():
1244 val = sxp.child_value(image_sxp, imgkey, None)
1245 if val != None:
1246 type_conv = XENAPI_CFG_TYPES[apikey]
1247 if callable(conv):
1248 self[apikey] = type_conv(val)
1249 else:
1250 self[apikey] = val
1255 # debugging
1258 if __name__ == "__main__":
1259 pass