ia64/xen-unstable

view tools/python/xen/xend/XendConfig.py @ 12229:96ad1d72fccf

[XEND] Fix paused state being overriden by refreshShutdown

Signed-off-by: Alastair Tse <atse@xensource.com>
author Alastair Tse <atse@xensource.com>
date Thu Nov 02 15:48:32 2006 +0000 (2006-11-02)
parents 41ad2c673fdb
children 374087600412
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
21 from xen.xend import sxp
22 from xen.xend import uuid
23 from xen.xend.XendError import VmError
24 from xen.xend.XendDevices import XendDevices
25 from xen.xend.XendLogging import log
26 from xen.xend.PrettyPrint import prettyprintstring
27 from xen.xend.XendConstants import DOM_STATE_HALTED
29 """
30 XendConfig API
32 XendConfig will try to mirror as closely the Xen API VM Struct
33 providing a backwards compatibility mode for SXP dumping, loading.
35 """
38 LEGACY_CFG_TO_XENAPI_CFG = {
39 'uuid': 'uuid',
40 'vcpus': 'vcpus_number',
41 'maxmem': 'memory_static_max',
42 'memory': 'memory_static_min',
43 'name': 'name_label',
44 'on_poweroff': 'actions_after_shutdown',
45 'on_reboot': 'actions_after_reboot',
46 'on_crash': 'actions_after_crash',
47 'bootloader': 'boot_method',
48 'kernel_kernel': 'kernel_kernel',
49 'kernel_initrd': 'kernel_initrd',
50 'kernel_args': 'kernel_args',
51 }
53 XENAPI_CFG_CUSTOM_TRANSLATE = [
54 'vifs',
55 'vbds',
56 ]
58 XENAPI_HVM_CFG = {
59 'platform_std_vga': 'std-vga',
60 'platform_serial' : 'serial',
61 'platform_localtime': 'localtime',
62 'platform_enable_audio': 'soundhw',
63 }
65 XENAPI_UNSUPPORTED_IN_LEGACY_CFG = [
66 'name_description',
67 'user_version',
68 'is_a_template',
69 'memory_dynamic_min',
70 'memory_dynamic_max',
71 'memory_actual',
72 'vcpus_policy',
73 'vcpus_params',
74 'vcpus_features_required',
75 'vcpus_features_can_use',
76 'vcpus_features_force_on',
77 'vcpus_features_force_off',
78 'actions_after_suspend',
79 'bios_boot',
80 'platform_std_vga',
81 'platform_serial',
82 'platform_localtime',
83 'platform_clock_offset',
84 'platform_enable_audio',
85 'builder',
86 'grub_cmdline',
87 'pci_bus',
88 'otherconfig'
89 ]
92 # configuration params that need to be converted to ints
93 # since the XMLRPC transport for Xen API does not use
94 # 32 bit ints but string representation of 64 bit ints.
95 XENAPI_INT_CFG = [
96 'user_version',
97 'vcpus_number',
98 'memory_static_min',
99 'memory_static_max',
100 'memory_dynamic_min',
101 'memory_dynamic_max',
102 'tpm_instance',
103 'tpm_backend',
104 ]
106 ##
107 ## Xend Configuration Parameters
108 ##
111 # All parameters of VMs that may be configured on-the-fly, or at start-up.
112 VM_CONFIG_ENTRIES = [
113 ('name', str),
114 ('on_crash', str),
115 ('on_poweroff', str),
116 ('on_reboot', str),
117 ('on_xend_start', str),
118 ('on_xend_stop', str),
119 ]
121 # All entries written to the store. This is VM_CONFIG_ENTRIES, plus those
122 # entries written to the store that cannot be reconfigured on-the-fly.
123 VM_STORE_ENTRIES = [
124 ('uuid', str),
125 ('vcpus', int),
126 ('vcpu_avail', int),
127 ('memory', int),
128 ('maxmem', int),
129 ('start_time', float),
130 ]
132 VM_STORED_ENTRIES = VM_CONFIG_ENTRIES + VM_STORE_ENTRIES
134 # Configuration entries that we expect to round-trip -- be read from the
135 # config file or xc, written to save-files (i.e. through sxpr), and reused as
136 # config on restart or restore, all without munging. Some configuration
137 # entries are munged for backwards compatibility reasons, or because they
138 # don't come out of xc in the same form as they are specified in the config
139 # file, so those are handled separately.
141 ROUNDTRIPPING_CONFIG_ENTRIES = [
142 ('uuid', str),
143 ('vcpus', int),
144 ('vcpu_avail', int),
145 ('cpu_weight', float),
146 ('memory', int),
147 ('shadow_memory', int),
148 ('maxmem', int),
149 ('bootloader', str),
150 ('bootloader_args', str),
151 ('features', str),
152 ('localtime', int),
153 ]
154 ROUNDTRIPPING_CONFIG_ENTRIES += VM_CONFIG_ENTRIES
156 ## Static Configuration
158 STATIC_CONFIG_ENTRIES = [
159 ('cpu', int),
160 ('cpus', str),
161 ('image', list),
162 ('security', list), # TODO: what if null?
163 ]
165 DEPRECATED_ENTRIES = [
166 ('restart', str),
167 ]
169 ##
170 ## Config Choices
171 ##
173 CONFIG_RESTART_MODES = ('restart', 'destroy', 'preserve', 'rename-restart')
174 CONFIG_OLD_DOM_STATES = ('running', 'blocked', 'paused', 'shutdown',
175 'crashed', 'dying')
177 ##
178 ## Defaults
179 ##
181 def DEFAULT_VCPUS(info):
182 if 'max_vcpu_id' in info: return int(info['max_vcpu_id']) + 1
183 else: return 1
185 DEFAULT_CONFIGURATION = (
186 ('uuid', lambda info: uuid.createString()),
187 ('name', lambda info: 'Domain-' + info['uuid']),
189 ('on_poweroff', lambda info: 'destroy'),
190 ('on_reboot', lambda info: 'restart'),
191 ('on_crash', lambda info: 'restart'),
192 ('features', lambda info: ''),
195 ('memory', lambda info: 0),
196 ('shadow_memory',lambda info: 0),
197 ('maxmem', lambda info: 0),
198 ('bootloader', lambda info: None),
199 ('bootloader_args', lambda info: None),
200 ('backend', lambda info: []),
201 ('device', lambda info: {}),
202 ('image', lambda info: None),
203 ('security', lambda info: []),
204 ('on_xend_start', lambda info: 'ignore'),
205 ('on_xend_stop', lambda info: 'ignore'),
207 ('cpus', lambda info: []),
208 ('cpu_cap', lambda info: 0),
209 ('cpu_weight', lambda info: 256),
210 ('vcpus', lambda info: DEFAULT_VCPUS(info)),
211 ('online_vcpus', lambda info: info['vcpus']),
212 ('max_vcpu_id', lambda info: info['vcpus']-1),
213 ('vcpu_avail', lambda info: (1<<info['vcpus'])-1),
215 # New for Xen API
216 ('kernel_kernel', lambda info: ''),
217 ('kernel_initrd', lambda info: ''),
218 ('kernel_args', lambda info: ''),
220 )
222 class XendConfigError(VmError):
223 def __str__(self):
224 return 'Invalid Configuration: %s' % str(self.value)
226 ##
227 ## XendConfig SXP Config Compat
228 ##
230 class XendSXPConfig:
231 def get_domid(self):
232 pass
233 def get_handle(self):
234 return self['uuid']
237 ##
238 ## XendConfig Class (an extended dictionary)
239 ##
241 class XendConfig(dict):
242 """ Generic Configuration Parser accepting SXP, Python or XML.
243 This is a dictionary-like object that is populated.
245 @ivar legacy: dictionary holding legacy xen domain info
246 @ivar xenapi: dictionary holding xen api config info
247 """
249 def __init__(self, filename = None, fd = None,
250 sxp = None, xml = None, pycfg = None, xenapi_vm = None,
251 cfg = {}):
252 """Constructor. Provide either the filename, fd or sxp.
254 @keyword filename: filename of an SXP file
255 @keyword fd: file descriptor of an SXP file
256 @keyword sxp: a list of list of a parsed SXP
257 @keyword xml: an XML tree object
258 @keyword xenapi_vm: a struct passed from an XMLRPC call (Xen API)
259 @keyword cfg: a dictionary of configuration (eg. from xc)
260 """
261 format = 'unknown'
263 self.xenapi = {}
265 if filename and not fd:
266 fd = open(filename, 'r')
268 if fd:
269 format = self._detect_format(fd)
271 if fd:
272 if format == 'sxp':
273 sxp = self._read_sxp(fd)
274 elif format == 'python' and filename != None:
275 pycfg = self._read_python(filename)
276 elif format == 'python' and filename == None:
277 raise XendConfigError("Python files must be passed as a "
278 "filename rather than file descriptor.")
279 elif format == 'xml':
280 xml = self._read_xml(fd)
281 else:
282 raise XendConfigError("Unable to determine format of file")
284 if sxp:
285 cfg = self._populate_from_sxp(sxp)
286 if xml:
287 cfg = self._populate_from_xml(xml)
288 if pycfg:
289 cfg = self._populate_from_python_config(pycfg)
290 if xenapi_vm:
291 cfg = self._populate_from_xenapi_vm(xenapi_vm)
293 if cfg:
294 self.update(cfg)
296 if xenapi_vm:
297 self.xenapi.update(xenapi_vm)
299 log.debug('XendConfig: %s' % str(self))
300 self.validate()
302 #
303 # Xen API Attribute Access
304 #
306 def __getattr__(self, name):
307 try:
308 return dict.__getattr__(self, name)
309 except AttributeError:
310 try:
311 return self.__dict__['xenapi'][name]
312 except KeyError:
313 raise AttributeError("XendConfig Xen API has no attribute "
314 "'%s'" % name)
317 def __setattr__(self, name, value):
318 try:
319 return dict.__setattr__(self, name, value)
320 except AttributeError:
321 self.xenapi[name] = value
322 #self.set_legacy_api_with_xen_api_value(name, value)
324 def __delattr__(self, name):
325 try:
326 dict.__delattr__(self, name)
327 except AttributeError:
328 del self.xenapi[name]
329 #self.del_legacy_api_with_xen_api_key(name)
332 """
333 #
334 # Legacy API Attribute Access
335 #
337 def __getitem__(self, key):
338 try:
339 return self.legacy[key]
340 except KeyError:
341 raise AttributeError, "XendConfig Legacy has no attribute '%s'"\
342 % key
344 def __setitem__(self, key, value):
345 self.legacy[key] = value
346 self.set_xen_api_with_legacy_api_value(key, value)
348 def __delitem__(self, key):
349 del self.legacy[key]
350 self.del_xen_api_with_legacy_api_key(key)
351 """
354 def _detect_format(self, fd):
355 """Detect the format of the configuration passed.
357 @param fd: file descriptor of contents to detect
358 @rtype: string, 'sxp', 'xml', 'python' or 'unknown'
359 """
360 format = 'unknown'
362 fd.seek(0)
363 for line in fd:
364 stripped = line.strip()
365 if stripped:
366 if re.search(r'^\(', stripped):
367 format = 'sxp'
368 elif re.search(r'^\<?xml', stripped):
369 format = 'xml'
370 else:
371 format = 'python'
372 break
374 fd.seek(0)
375 return format
377 def _read_sxp(self, fd):
378 """ Read and parse SXP (from SXP to list of lists)
380 @rtype: list of lists.
381 """
382 try:
383 parsed = sxp.parse(fd)[0]
384 return parsed
385 except:
386 raise
387 return None
389 def _read_xml(self, fd):
390 """TODO: Read and parse XML (from XML to dict)
392 @rtype: dict
393 """
394 raise NotImplementedError
396 def _read_python(self, filename):
397 """Read and parse python module that represents the config.
399 @rtype: dict
400 """
401 cfg_globals = {}
402 execfile(filename, cfg_globals, {})
403 return cfg_globals
405 def _populate_from_sxp(self, parsed):
406 """ Populate this XendConfig using the parsed SXP.
408 @rtype: dictionary
409 """
410 cfg = {}
412 # First step is to convert deprecated options to
413 # current equivalents.
415 restart = sxp.child_value(parsed, 'restart')
416 if restart:
417 if restart == 'onreboot':
418 cfg['on_poweroff'] = 'destroy'
419 cfg['on_reboot'] = 'restart'
420 cfg['on_crash'] = 'destroy'
421 elif restart == 'always':
422 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
423 cfg[opt] = 'restart'
424 elif restart == 'never':
425 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
426 cfg[opt] = 'never'
427 else:
428 log.warn('Ignoring unrecognised value for deprecated option:'
429 'restart = \'%s\'', restart)
431 # Only extract options we know about.
432 all_params = VM_CONFIG_ENTRIES + ROUNDTRIPPING_CONFIG_ENTRIES + \
433 STATIC_CONFIG_ENTRIES
435 for key, typeconv in all_params:
436 val = sxp.child_value(parsed, key)
437 if val:
438 try:
439 cfg[key] = typeconv(val)
440 except ValueError:
441 pass
443 # Manually extract other complex configuration
444 # options.
446 cfg['backend'] = []
447 for c in sxp.children(parsed, 'backend'):
448 cfg['backend'].append(sxp.name(sxp.child0(c)))
450 cfg['device'] = {}
451 for dev in sxp.children(parsed, 'device'):
452 config = sxp.child0(dev)
453 dev_type = sxp.name(config)
454 dev_info = {}
455 for opt, val in config[1:]:
456 dev_info[opt] = val
457 log.debug("XendConfig: reading device: %s" % dev_info)
458 # create uuid if it doesn't
459 dev_uuid = dev_info.get('uuid', uuid.createString())
460 dev_info['uuid'] = dev_uuid
461 cfg['device'][dev_uuid] = (dev_type, dev_info)
463 #cfg['device'].append((sxp.name(config), config))
466 # Extract missing data from configuration entries
467 if 'image' in cfg:
468 image_vcpus = sxp.child_value(cfg['image'], 'vcpus')
469 if image_vcpus is not None:
470 try:
471 if 'vcpus' not in cfg:
472 cfg['vcpus'] = int(image_vcpus)
473 elif cfg['vcpus'] != int(image_vcpus):
474 cfg['vcpus'] = int(image_vcpus)
475 log.warn('Overriding vcpus from %d to %d using image'
476 'vcpus value.', cfg['vcpus'])
477 except ValueError, e:
478 raise XendConfigError('integer expeceted: %s: %s' %
479 str(cfg['image']), e)
481 # Deprecated cpu configuration
482 if 'cpu' in cfg:
483 if 'cpus' in cfg:
484 cfg['cpus'] = "%s,%s" % (str(cfg['cpu']), cfg['cpus'])
485 else:
486 cfg['cpus'] = str(cfg['cpu'])
488 # convert 'cpus' string to list of ints
489 # 'cpus' supports a list of ranges (0-3), seperated by
490 # commas, and negation, (^1).
491 # Precedence is settled by order of the string:
492 # "0-3,^1" -> [0,2,3]
493 # "0-3,^1,1" -> [0,1,2,3]
494 try:
495 if 'cpus' in cfg:
496 cpus = []
497 for c in cfg['cpus'].split(','):
498 if c.find('-') != -1:
499 (x, y) = c.split('-')
500 for i in range(int(x), int(y)+1):
501 cpus.append(int(i))
502 else:
503 # remove this element from the list
504 if c[0] == '^':
505 cpus = [x for x in cpus if x != int(c[1:])]
506 else:
507 cpus.append(int(c))
509 cfg['cpus'] = cpus
510 except ValueError, e:
511 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
513 # Parse image SXP outside of image.py
514 # - used to be only done in image.py
515 if 'image' in cfg:
516 cfg['kernel_kernel'] = sxp.child_value(cfg['image'], 'kernel','')
517 cfg['kernel_initrd'] = sxp.child_value(cfg['image'], 'ramdisk','')
518 kernel_args = sxp.child_value(cfg['image'], 'args', '')
520 # attempt to extract extra arguments from SXP config
521 arg_ip = sxp.child_value(cfg['image'], 'ip')
522 if arg_ip: kernel_args += ' ip=%s' % arg_ip
523 arg_root = sxp.child_value(cfg['image'], 'root')
524 if arg_root: kernel_args += ' root=%s' % arg_root
526 cfg['kernel_args'] = kernel_args
528 # TODO: get states
529 old_state = sxp.child_value(parsed, 'state')
530 if old_state:
531 for i in range(len(CONFIG_OLD_DOM_STATES)):
532 cfg[CONFIG_OLD_DOM_STATES[i]] = (old_state[i] != '-')
534 # Xen API extra cfgs
535 # ------------------
536 cfg['vif_refs'] = []
537 cfg['vbd_refs'] = []
538 cfg['vtpm_refs'] = []
539 for dev_uuid, (dev_type, dev_info) in cfg['device'].items():
540 if dev_type == 'vif':
541 cfg['vif_refs'].append(dev_uuid)
542 elif dev_type in ('vbd','tap'):
543 cfg['vbd_refs'].append(dev_uuid)
544 elif dev_type == 'vtpm':
545 cfg['vtpm_refs'].append(dev_uuid)
547 return cfg
550 def _populate_from_xenapi_vm(self, xenapi_vm):
551 cfg = {}
553 for cfgkey, apikey in LEGACY_CFG_TO_XENAPI_CFG.items():
554 try:
555 if apikey in XENAPI_INT_CFG:
556 cfg[cfgkey] = int(xenapi_vm[apikey])
557 else:
558 cfg[cfgkey] = xenapi_vm[apikey]
559 except KeyError:
560 pass
562 # Reconstruct image SXP
563 # TODO: get rid of SXP altogether from here
564 sxp_image = ['linux']
565 if xenapi_vm['kernel_kernel']:
566 sxp_image.append(['kernel', xenapi_vm['kernel_kernel']])
567 if xenapi_vm['kernel_initrd']:
568 sxp_image.append(['ramdisk', xenapi_vm['kernel_initrd']])
569 if xenapi_vm['kernel_args']:
570 sxp_image.append(['args', xenapi_vm['kernel_args']])
572 cfg['image'] = prettyprintstring(sxp_image)
574 # make sure device structures are there.
575 if 'device' not in cfg:
576 cfg['device'] = {}
577 if 'vif_refs' not in cfg:
578 cfg['vif_refs'] = []
579 if 'vbd_refs' not in cfg:
580 cfg['vbd_refs'] = []
581 if 'vtpm_refs' not in cfg:
582 cfg['vtpm_refs'] = []
584 return cfg
587 def _sync_xen_api_from_legacy_api(self):
588 """ Sync all the attributes that is supported by the Xen API
589 from the legacy API configuration.
590 """
591 for cfgkey, apikey in LEGACY_CFG_TO_XENAPI_CFG.items():
592 if cfgkey in self:
593 self.xenapi[apikey] = self[cfgkey]
595 def _sync_legacy_api_from_xen_api(self):
596 for cfgkey, apikey in LEGACY_CFG_TO_XENAPI_CFG.items():
597 if apikey in self.xenapi:
598 self[cfgkey] = self.xenapi[apikey]
601 def _populate_from_xml(self, parsed_xml):
602 raise NotImplementedError
604 def _populate_from_python_config(self, parsed_py):
605 raise NotImplementedError
608 def get_sxp(self, domain = None, ignore_devices = False, ignore = []):
609 """ Get SXP representation of this config object.
611 @keyword domain: (optional) XendDomainInfo to get extra information
612 from such as domid and running devices.
613 @type domain: XendDomainInfo
614 @keyword ignore: (optional) list of 'keys' that we do not want
615 to export.
616 @type ignore: list of strings
617 @rtype: list of list (SXP representation)
618 """
619 sxpr = ['domain']
621 # TODO: domid/dom is the same thing but called differently
622 # depending if it is from xenstore or sxpr.
624 if domain.getDomid() != None:
625 sxpr.append(['domid', domain.getDomid()])
627 for cfg, typefunc in ROUNDTRIPPING_CONFIG_ENTRIES:
628 if cfg in self:
629 if self[cfg] != None:
630 sxpr.append([cfg, self[cfg]])
632 if 'image' in self and self['image'] != None:
633 sxpr.append(['image', self['image']])
634 if 'security' in self and self['security']:
635 sxpr.append(['security', self['security']])
636 if 'shutdown_reason' in self:
637 sxpr.append(['shutdown_reason', self['shutdown_reason']])
638 if 'cpu_time' in self:
639 sxpr.append(['cpu_time', self['cpu_time']/1e9])
641 sxpr.append(['online_vcpus', self['online_vcpus']])
643 if 'start_time' in self:
644 uptime = time.time() - self['start_time']
645 sxpr.append(['up_time', str(uptime)])
646 sxpr.append(['start_time', str(self['start_time'])])
648 if domain:
649 sxpr.append(['status', str(domain.state)])
650 else:
651 sxpr.append(['status', str(DOM_STATE_HALTED)])
653 # For save/restore migration
654 if domain:
655 if domain.store_mfn:
656 sxpr.append(['store_mfn', domain.store_mfn])
657 if domain.console_mfn:
658 sxpr.append(['console_mfn', domain.console_mfn])
660 # Marshall devices (running or from configuration)
661 if not ignore_devices:
662 for cls in XendDevices.valid_devices():
663 found = False
665 # figure if there is a device that is running
666 if domain:
667 try:
668 controller = domain.getDeviceController(cls)
669 configs = controller.configurations()
670 for config in configs:
671 sxpr.append(['device', config])
672 found = True
673 except:
674 log.exception("dumping sxp from device controllers")
675 pass
677 # if we didn't find that device, check the existing config
678 # for a device in the same class
679 if not found:
680 for dev_type, dev_info in self.all_devices_sxpr():
681 if dev_type == cls:
682 sxpr.append(['device', dev_info])
684 return sxpr
686 def validate(self):
687 """ Validate the configuration and fill in missing configuration
688 with defaults.
689 """
691 # Fill in default values
692 for key, default_func in DEFAULT_CONFIGURATION:
693 if key not in self:
694 self[key] = default_func(self)
696 # Basic sanity checks
697 if 'image' in self and isinstance(self['image'], str):
698 self['image'] = sxp.from_string(self['image'])
699 if 'security' in self and isinstance(self['security'], str):
700 self['security'] = sxp.from_string(self['security'])
701 if self['memory'] == 0 and 'mem_kb' in self:
702 self['memory'] = (self['mem_kb'] + 1023)/1024
703 if self['memory'] <= 0:
704 raise XendConfigError('Invalid memory size: %s' %
705 str(self['memory']))
707 self['maxmem'] = max(self['memory'], self['maxmem'])
709 # Verify devices
710 for d_uuid, (d_type, d_info) in self['device'].items():
711 if d_type not in XendDevices.valid_devices():
712 raise XendConfigError('Invalid device (%s)' % d_type)
714 # Verify restart modes
715 for event in ('on_poweroff', 'on_reboot', 'on_crash'):
716 if self[event] not in CONFIG_RESTART_MODES:
717 raise XendConfigError('Invalid restart event: %s = %s' % \
718 (event, str(self[event])))
720 # Verify that {vif,vbd}_refs are here too
721 if 'vif_refs' not in self:
722 self['vif_refs'] = []
723 if 'vbd_refs' not in self:
724 self['vbd_refs'] = []
725 if 'vtpm_refs' not in self:
726 self['vtpm_refs'] = []
728 def device_add(self, dev_type, cfg_sxp = None, cfg_xenapi = None):
729 if dev_type not in XendDevices.valid_devices():
730 raise XendConfigError("XendConfig: %s not a valid device type" %
731 dev_type)
733 if cfg_sxp == None and cfg_xenapi == None:
734 raise XendConfigError("XendConfig: device_add requires some "
735 "config.")
737 if cfg_sxp:
738 log.debug("XendConfig.device_add: %s" % str(cfg_sxp))
739 if cfg_xenapi:
740 log.debug("XendConfig.device_add: %s" % str(cfg_xenapi))
742 if cfg_sxp:
743 dev_info = {}
745 try:
746 for opt, val in cfg_sxp[1:]:
747 dev_info[opt] = val
748 except ValueError:
749 pass # SXP has no options for this device
751 # create uuid if it doesn't exist
752 dev_uuid = dev_info.get('uuid', uuid.createString())
753 dev_info['uuid'] = dev_uuid
754 self['device'][dev_uuid] = (dev_type, dev_info)
755 if dev_type in ('vif', 'vbd'):
756 self['%s_refs' % dev_type].append(dev_uuid)
757 elif dev_type in ('tap',):
758 self['vbd_refs'].append(dev_uuid)
759 return dev_uuid
761 if cfg_xenapi:
762 dev_info = {}
763 if dev_type == 'vif':
764 if cfg_xenapi.get('MAC'): # don't add if blank
765 dev_info['mac'] = cfg_xenapi.get('MAC')
766 # vifname is the name on the guest, not dom0
767 # TODO: we don't have the ability to find that out or
768 # change it from dom0
769 #if cfg_xenapi.get('device'): # don't add if blank
770 # dev_info['vifname'] = cfg_xenapi.get('device')
771 if cfg_xenapi.get('type'):
772 dev_info['type'] = cfg_xenapi.get('type')
774 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
775 dev_info['uuid'] = dev_uuid
776 self['device'][dev_uuid] = (dev_type, dev_info)
777 self['vif_refs'].append(dev_uuid)
778 return dev_uuid
780 elif dev_type == 'vbd':
781 dev_info['uname'] = cfg_xenapi.get('image', None)
782 dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
783 if cfg_xenapi.get('mode') == 'RW':
784 dev_info['mode'] = 'w'
785 else:
786 dev_info['mode'] = 'r'
788 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
789 dev_info['uuid'] = dev_uuid
790 self['device'][dev_uuid] = (dev_type, dev_info)
791 self['vbd_refs'].append(dev_uuid)
792 return dev_uuid
794 elif dev_type == 'vtpm':
795 if cfg_xenapi.get('type'):
796 dev_info['type'] = cfg_xenapi.get('type')
797 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
798 dev_info['uuid'] = dev_uuid
799 self['device'][dev_uuid] = (dev_type, dev_info)
800 self['vtpm_refs'].append(dev_uuid)
801 return dev_uuid
803 elif dev_type == 'tap':
804 dev_info['uname'] = 'tap:qcow:%s' % cfg_xenapi.get('image')
805 dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
807 if cfg_xenapi.get('mode') == 'RW':
808 dev_info['mode'] = 'w'
809 else:
810 dev_info['mode'] = 'r'
812 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
813 dev_info['uuid'] = dev_uuid
814 self['device'][dev_uuid] = (dev_type, dev_info)
815 self['vbd_refs'].append(dev_uuid)
816 return dev_uuid
818 return ''
820 def device_sxpr(self, dev_uuid = None, dev_type = None, dev_info = None):
821 """Get Device SXPR by either giving the device UUID or (type, config).
823 @rtype: list of lists
824 @return: device config sxpr
825 """
826 sxpr = []
827 if dev_uuid != None and dev_uuid in self['device']:
828 dev_type, dev_info = self['device'][dev_uuid]
830 if dev_type == None or dev_info == None:
831 raise XendConfigError("Required either UUID or device type and "
832 "configuration dictionary.")
834 sxpr.append(dev_type)
835 config = [(opt, val) for opt, val in dev_info.items() \
836 if opt != 'type']
837 sxpr += config
839 return sxpr
841 def all_devices_sxpr(self):
842 sxprs = []
843 for dev_type, dev_info in self['device'].values():
844 sxpr = self.device_sxpr(dev_type = dev_type, dev_info = dev_info)
845 sxprs.append((dev_type, sxpr))
846 return sxprs
849 #
850 # debugging
851 #
853 if __name__ == "__main__":
854 pass