debuggers.hg

view tools/python/xen/xend/XendConfig.py @ 13654:b111908dd70b

[XEND] Preliminary console support in Xen API

Made serial/vnc consoles a 'fake' device so that we can take advantage
of storing state information in xenstore.

Signed-off-by: Alastair Tse <atse@xensource.com>
author Alastair Tse <atse@xensource.com>
date Thu Jan 25 18:54:25 2007 +0000 (2007-01-25)
parents 3bb7136c8fb4
children 1e88f0b736b5
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 sxpr.append(['device', config])
883 found = True
884 except:
885 log.exception("dumping sxp from device controllers")
886 pass
888 # if we didn't find that device, check the existing config
889 # for a device in the same class
890 if not found:
891 for dev_type, dev_info in self.all_devices_sxpr():
892 if dev_type == cls:
893 sxpr.append(['device', dev_info])
895 return sxpr
897 def device_add(self, dev_type, cfg_sxp = None, cfg_xenapi = None,
898 target = None):
899 """Add a device configuration in SXP format or XenAPI struct format.
901 For SXP, it could be either:
903 [device, [vbd, [uname ...]]
905 or:
907 [vbd, [uname ..]]
909 @type cfg_sxp: list of lists (parsed sxp object)
910 @param cfg_sxp: SXP configuration object
911 @type cfg_xenapi: dict
912 @param cfg_xenapi: A device configuration from Xen API (eg. vbd,vif)
913 @param target: write device information to
914 @type target: None or a dictionary
915 @rtype: string
916 @return: Assigned UUID of the device.
917 """
918 if target == None:
919 target = self
921 if dev_type not in XendDevices.valid_devices():
922 raise XendConfigError("XendConfig: %s not a valid device type" %
923 dev_type)
925 if cfg_sxp == None and cfg_xenapi == None:
926 raise XendConfigError("XendConfig: device_add requires some "
927 "config.")
929 #if cfg_sxp:
930 # log.debug("XendConfig.device_add: %s" % str(cfg_sxp))
931 #if cfg_xenapi:
932 # log.debug("XendConfig.device_add: %s" % str(cfg_xenapi))
934 if cfg_sxp:
935 if sxp.child0(cfg_sxp) == 'device':
936 config = sxp.child0(cfg_sxp)
937 else:
938 config = cfg_sxp
940 dev_type = sxp.name(config)
941 dev_info = {}
943 for opt_val in config[1:]:
944 try:
945 opt, val = opt_val
946 dev_info[opt] = val
947 except (TypeError, ValueError): # unpack error
948 pass
950 if dev_type == 'vbd':
951 if dev_info.get('dev', '').startswith('ioemu:'):
952 dev_info['driver'] = 'ioemu'
953 else:
954 dev_info['driver'] = 'paravirtualised'
956 # create uuid if it doesn't exist
957 dev_uuid = dev_info.get('uuid', uuid.createString())
958 dev_info['uuid'] = dev_uuid
960 # store dev references by uuid for certain device types
961 target['devices'][dev_uuid] = (dev_type, dev_info)
962 if dev_type in ('vif', 'vbd', 'vtpm'):
963 param = '%s_refs' % dev_type
964 if param not in target:
965 target[param] = []
966 if dev_uuid not in target[param]:
967 target[param].append(dev_uuid)
968 elif dev_type in ('tap',):
969 if 'vbd_refs' not in target:
970 target['vbd_refs'] = []
971 if dev_uuid not in target['vbd_refs']:
972 target['vbd_refs'].append(dev_uuid)
973 elif dev_type in ('console',):
974 if 'console_refs' not in target:
975 target['console_refs'] = []
976 if dev_uuid not in target['console_refs']:
977 target['console_refs'].append(dev_uuid)
979 return dev_uuid
981 if cfg_xenapi:
982 dev_info = {}
983 dev_uuid = ''
984 if dev_type == 'vif':
985 if cfg_xenapi.get('MAC'): # don't add if blank
986 dev_info['mac'] = cfg_xenapi.get('MAC')
987 # vifname is the name on the guest, not dom0
988 # TODO: we don't have the ability to find that out or
989 # change it from dom0
990 #if cfg_xenapi.get('device'): # don't add if blank
991 # dev_info['vifname'] = cfg_xenapi.get('device')
992 if cfg_xenapi.get('type'):
993 dev_info['type'] = cfg_xenapi.get('type')
994 if cfg_xenapi.get('name'):
995 dev_info['name'] = cfg_xenapi.get('name')
997 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
998 dev_info['uuid'] = dev_uuid
999 target['devices'][dev_uuid] = (dev_type, dev_info)
1000 target['vif_refs'].append(dev_uuid)
1002 elif dev_type in ('vbd', 'tap'):
1003 dev_info['type'] = cfg_xenapi.get('type', 'Disk')
1004 if dev_info['type'] == 'CD':
1005 old_vbd_type = 'cdrom'
1006 else:
1007 old_vbd_type = 'disk'
1009 dev_info['uname'] = cfg_xenapi.get('image', '')
1010 dev_info['dev'] = '%s:%s' % (cfg_xenapi.get('device'),
1011 old_vbd_type)
1012 dev_info['driver'] = cfg_xenapi.get('driver')
1013 dev_info['VDI'] = cfg_xenapi.get('VDI', '')
1015 if cfg_xenapi.get('mode') == 'RW':
1016 dev_info['mode'] = 'w'
1017 else:
1018 dev_info['mode'] = 'r'
1020 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
1021 dev_info['uuid'] = dev_uuid
1022 target['devices'][dev_uuid] = (dev_type, dev_info)
1023 target['vbd_refs'].append(dev_uuid)
1025 elif dev_type == 'vtpm':
1026 if cfg_xenapi.get('type'):
1027 dev_info['type'] = cfg_xenapi.get('type')
1029 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
1030 dev_info['uuid'] = dev_uuid
1031 target['devices'][dev_uuid] = (dev_type, dev_info)
1032 target['vtpm_refs'].append(dev_uuid)
1034 return dev_uuid
1036 # no valid device to add
1037 return ''
1039 def console_add(self, protocol, uri):
1040 dev_uuid = uuid.createString()
1041 dev_info = {
1042 'uuid': dev_uuid,
1043 'protocol': protocol,
1044 'uri': uri
1046 if 'devices' not in self:
1047 self['devices'] = {}
1049 self['devices'][dev_uuid] = ('console', dev_info)
1050 self['console_refs'].append(dev_uuid)
1051 return dev_info
1053 def console_get_all(self, protocol):
1054 consoles = [dinfo for dtype, dinfo in self['devices'].values()
1055 if dtype == 'console']
1056 return [c for c in consoles if c.get('protocol') == protocol]
1058 def device_update(self, dev_uuid, cfg_sxp):
1059 """Update an existing device with the new configuration.
1061 @rtype: boolean
1062 @return: Returns True if succesfully found and updated a device conf
1063 """
1064 if dev_uuid in self['devices']:
1065 if sxp.child0(cfg_sxp) == 'device':
1066 config = sxp.child0(cfg_sxp)
1067 else:
1068 config = cfg_sxp
1070 for opt_val in config[1:]:
1071 try:
1072 opt, val = opt_val
1073 self['devices'][dev_uuid][opt] = val
1074 except (TypeError, ValueError):
1075 pass # no value for this config option
1077 return True
1079 return False
1082 def device_sxpr(self, dev_uuid = None, dev_type = None, dev_info = None):
1083 """Get Device SXPR by either giving the device UUID or (type, config).
1085 @rtype: list of lists
1086 @return: device config sxpr
1087 """
1088 sxpr = []
1089 if dev_uuid != None and dev_uuid in self['devices']:
1090 dev_type, dev_info = self['devices'][dev_uuid]
1092 if dev_type == None or dev_info == None:
1093 raise XendConfigError("Required either UUID or device type and "
1094 "configuration dictionary.")
1096 sxpr.append(dev_type)
1097 config = [(opt, val) for opt, val in dev_info.items()]
1098 sxpr += config
1100 return sxpr
1102 def ordered_device_refs(self):
1103 result = (self['console_refs'] +
1104 self['vbd_refs'] +
1105 self['vif_refs'] +
1106 self['vtpm_refs'])
1107 result.extend([u for u in self['devices'].keys() if u not in result])
1108 return result
1110 def all_devices_sxpr(self):
1111 """Returns the SXPR for all devices in the current configuration."""
1112 sxprs = []
1113 pci_devs = []
1115 if 'devices' not in self:
1116 return sxprs
1118 ordered_refs = self.ordered_device_refs()
1119 for dev_uuid in ordered_refs:
1120 dev_type, dev_info = self['devices'][dev_uuid]
1121 if dev_type == 'pci': # special case for pci devices
1122 sxpr = [['uuid', dev_info['uuid']]]
1123 for pci_dev_info in dev_info['devs']:
1124 pci_dev_sxpr = ['dev']
1125 for opt, val in pci_dev_info.items():
1126 pci_dev_sxpr.append([opt, val])
1127 sxpr.append(pci_dev_sxpr)
1128 sxprs.append((dev_type, sxpr))
1129 else:
1130 sxpr = self.device_sxpr(dev_type = dev_type,
1131 dev_info = dev_info)
1132 sxprs.append((dev_type, sxpr))
1134 return sxprs
1136 def image_sxpr(self):
1137 """Returns a backwards compatible image SXP expression that is
1138 used in xenstore's /vm/<uuid>/image value and xm list."""
1139 image = [self['image'].get('type', 'linux')]
1140 if self.has_key('PV_kernel'):
1141 image.append(['kernel', self['PV_kernel']])
1142 if self.has_key('PV_ramdisk') and self['PV_ramdisk']:
1143 image.append(['ramdisk', self['PV_ramdisk']])
1144 if self.has_key('PV_args') and self['PV_args']:
1145 image.append(['args', self['PV_args']])
1147 for arg, conv in LEGACY_IMAGE_CFG:
1148 if self['image'].has_key(arg):
1149 image.append([arg, self['image'][arg]])
1151 if 'hvm' in self['image']:
1152 for arg, conv in LEGACY_IMAGE_HVM_CFG:
1153 if self['image']['hvm'].get(arg):
1154 image.append([arg, conv(self['image']['hvm'][arg])])
1156 if 'hvm' in self['image'] and 'devices' in self['image']['hvm']:
1157 for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
1158 val = self['image']['hvm']['devices'].get(arg)
1159 if val != None:
1160 try:
1161 if conv: val = conv(val)
1162 except (ValueError, TypeError):
1163 if type(val) == bool: val = int(val)
1165 image.append([arg, val])
1167 return image
1169 def update_with_image_sxp(self, image_sxp, bootloader = False):
1170 # Convert Legacy "image" config to Xen API PV_*
1171 # configuration
1172 log.debug("update_with_image_sxp(%s)" % scrub_password(image_sxp))
1174 # user-specified args must come last: previous releases did this and
1175 # some domU kernels rely upon the ordering.
1176 kernel_args = sxp.child_value(image_sxp, 'args', '')
1178 # attempt to extract extra arguments from SXP config
1179 arg_ip = sxp.child_value(image_sxp, 'ip')
1180 if arg_ip and not re.search(r'ip=[^ ]+', kernel_args):
1181 kernel_args = 'ip=%s ' % arg_ip + kernel_args
1182 arg_root = sxp.child_value(image_sxp, 'root')
1183 if arg_root and not re.search(r'root=', kernel_args):
1184 kernel_args = 'root=%s ' % arg_root + kernel_args
1186 if bootloader:
1187 self['_temp_using_bootloader'] = '1'
1188 self['_temp_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1189 self['_temp_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1190 self['_temp_args'] = kernel_args
1191 else:
1192 self['PV_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1193 self['PV_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1194 self['PV_args'] = kernel_args
1196 # Store image SXP in python dictionary format
1197 image = {}
1198 image['type'] = sxp.name(image_sxp)
1199 for arg, conv in LEGACY_IMAGE_CFG:
1200 val = sxp.child_value(image_sxp, arg, None)
1201 if val != None:
1202 image[arg] = conv(val)
1204 image_hvm = {}
1205 for arg, conv in LEGACY_IMAGE_HVM_CFG:
1206 val = sxp.child_value(image_sxp, arg, None)
1207 if val != None:
1208 image_hvm[arg] = conv(val)
1210 image_hvm_devices = {}
1211 for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
1212 val = sxp.child_value(image_sxp, arg, None)
1213 if val != None:
1214 try:
1215 image_hvm_devices[arg] = conv(val)
1216 except (ValueError, TypeError):
1217 image_hvm_devices[arg] = val
1220 if image_hvm or image_hvm_devices:
1221 image['hvm'] = image_hvm
1222 image['hvm']['devices'] = image_hvm_devices
1224 self['image'] = image
1226 for apikey, imgkey in XENAPI_HVM_CFG.items():
1227 val = sxp.child_value(image_sxp, imgkey, None)
1228 if val != None:
1229 type_conv = XENAPI_CFG_TYPES[apikey]
1230 if callable(conv):
1231 self[apikey] = type_conv(val)
1232 else:
1233 self[apikey] = val
1238 # debugging
1241 if __name__ == "__main__":
1242 pass