ia64/xen-unstable

view tools/python/xen/xend/XendConfig.py @ 19758:0573bbe19499

xend: pass-through: sxp.merge() cant deal with values being a list

sxp.merge() can't deal with values being a list so instead
of storing pci options as:

[ 'opts', [ 'key1' 'value1'], [ 'key2', 'value2'], ...]

store them as:

[ 'opts', [ 'key1' 'value1'], ['opts', [ 'key2', 'value2']], ...

Signed-off-by: Simon Horman <horms@verge.net.au>
author Keir Fraser <keir.fraser@citrix.com>
date Tue Jun 16 11:37:41 2009 +0100 (2009-06-16)
parents eeb0fce9aeaf
children a4036225c168
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-2007 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 import XendOptions
26 from xen.xend import XendAPIStore
27 from xen.xend.XendPPCI import XendPPCI
28 from xen.xend.XendDPCI import XendDPCI
29 from xen.xend.XendPSCSI import XendPSCSI
30 from xen.xend.XendDSCSI import XendDSCSI
31 from xen.xend.XendError import VmError
32 from xen.xend.XendDevices import XendDevices
33 from xen.xend.PrettyPrint import prettyprintstring
34 from xen.xend.XendConstants import DOM_STATE_HALTED, AUTO_PHP_SLOT_STR
35 from xen.xend.xenstore.xstransact import xstransact
36 from xen.xend.server.BlktapController import blktap_disk_types
37 from xen.xend.server.netif import randomMAC
38 from xen.util.blkif import blkdev_name_to_number, blkdev_uname_to_file
39 from xen.util.pci import pci_opts_list_from_sxp
40 from xen.util import xsconstants
41 import xen.util.auxbin
43 log = logging.getLogger("xend.XendConfig")
44 log.setLevel(logging.WARN)
47 """
48 XendConfig API
50 XendConfig will try to mirror as closely the Xen API VM Struct
51 with extra parameters for those options that are not supported.
53 """
55 def reverse_dict(adict):
56 """Return the reverse mapping of a dictionary."""
57 return dict([(v, k) for k, v in adict.items()])
59 def bool0(v):
60 return v != '0' and v != 'False' and bool(v)
62 # Recursively copy a data struct, scrubbing out VNC passwords.
63 # Will scrub any dict entry with a key of 'vncpasswd' or any
64 # 2-element list whose first member is 'vncpasswd'. It will
65 # also scrub a string matching '(vncpasswd XYZ)'. Everything
66 # else is no-op passthrough
67 def scrub_password(data):
68 if type(data) == dict or type(data) == XendConfig:
69 scrubbed = {}
70 for key in data.keys():
71 if key == "vncpasswd":
72 scrubbed[key] = "XXXXXXXX"
73 else:
74 scrubbed[key] = scrub_password(data[key])
75 return scrubbed
76 elif type(data) == list:
77 if len(data) == 2 and type(data[0]) == str and data[0] == 'vncpasswd':
78 return ['vncpasswd', 'XXXXXXXX']
79 else:
80 scrubbed = []
81 for entry in data:
82 scrubbed.append(scrub_password(entry))
83 return scrubbed
84 elif type(data) == tuple:
85 scrubbed = []
86 for entry in data:
87 scrubbed.append(scrub_password(entry))
88 return tuple(scrubbed)
89 elif type(data) == str:
90 return re.sub(r'\(vncpasswd\s+[^\)]+\)','(vncpasswd XXXXXX)', data)
91 else:
92 return data
94 #
95 # CPU fields:
96 #
97 # VCPUs_max -- the maximum number of vcpus that this domain may ever have.
98 # aka XendDomainInfo.getVCpuCount().
99 # vcpus -- the legacy configuration name for above.
100 # max_vcpu_id -- vcpus_number - 1. This is given to us by Xen.
101 #
102 # cpus -- the list of pCPUs available to each vCPU.
103 #
104 # vcpu_avail -- a bitmap telling the guest domain whether it may use each of
105 # its VCPUs. This is translated to
106 # <dompath>/cpu/<id>/availability = {online,offline} for use
107 # by the guest domain.
108 # VCPUs_live -- the number of VCPUs currently up, as reported by Xen. This
109 # is changed by changing vcpu_avail, and waiting for the
110 # domain to respond.
111 #
114 # Mapping from XendConfig configuration keys to the old
115 # legacy configuration keys that map directly.
117 XENAPI_CFG_TO_LEGACY_CFG = {
118 'uuid': 'uuid',
119 'VCPUs_max': 'vcpus',
120 'cpus': 'cpus',
121 'name_label': 'name',
122 'actions_after_shutdown': 'on_poweroff',
123 'actions_after_reboot': 'on_reboot',
124 'actions_after_crash': 'on_crash',
125 'PV_bootloader': 'bootloader',
126 'PV_bootloader_args': 'bootloader_args',
127 }
129 LEGACY_CFG_TO_XENAPI_CFG = reverse_dict(XENAPI_CFG_TO_LEGACY_CFG)
131 # Platform configuration keys and their types.
132 XENAPI_PLATFORM_CFG_TYPES = {
133 'acpi': int,
134 'apic': int,
135 'boot': str,
136 'device_model': str,
137 'loader': str,
138 'display' : str,
139 'fda': str,
140 'fdb': str,
141 'keymap': str,
142 'isa' : int,
143 'localtime': int,
144 'monitor': int,
145 'nographic': int,
146 'pae' : int,
147 'rtc_timeoffset': int,
148 'serial': str,
149 'sdl': int,
150 'opengl': int,
151 'soundhw': str,
152 'stdvga': int,
153 'videoram': int,
154 'usb': int,
155 'usbdevice': str,
156 'hpet': int,
157 'vnc': int,
158 'vncconsole': int,
159 'vncdisplay': int,
160 'vnclisten': str,
161 'timer_mode': int,
162 'vpt_align': int,
163 'viridian': int,
164 'vncpasswd': str,
165 'vncunused': int,
166 'xauthority': str,
167 'pci': str,
168 'vhpt': int,
169 'guest_os_type': str,
170 'hap': int,
171 'xen_extended_power_mgmt': int,
172 'pci_msitranslate': int,
173 'pci_power_mgmt': int,
174 'xen_platform_pci': int,
175 }
177 # Xen API console 'other_config' keys.
178 XENAPI_CONSOLE_OTHER_CFG = ['vncunused', 'vncdisplay', 'vnclisten',
179 'vncpasswd', 'sdl', 'vnc', 'display', 'xauthority',
180 'keymap', 'opengl']
182 # List of XendConfig configuration keys that have no direct equivalent
183 # in the old world.
185 XENAPI_CFG_TYPES = {
186 'uuid': str,
187 'name_label': str,
188 'name_description': str,
189 'user_version': str,
190 'is_a_template': bool0,
191 'auto_power_on': bool0,
192 'resident_on': str,
193 'memory_static_min': int, # note these are stored in bytes, not KB!
194 'memory_static_max': int,
195 'memory_dynamic_min': int,
196 'memory_dynamic_max': int,
197 'cpus': list,
198 'vcpus_params': dict,
199 'VCPUs_max': int,
200 'VCPUs_at_startup': int,
201 'VCPUs_live': int,
202 'actions_after_shutdown': str,
203 'actions_after_reboot': str,
204 'actions_after_crash': str,
205 'PV_bootloader': str,
206 'PV_kernel': str,
207 'PV_ramdisk': str,
208 'PV_args': str,
209 'PV_bootloader_args': str,
210 'HVM_boot_policy': str,
211 'HVM_boot_params': dict,
212 'PCI_bus': str,
213 'platform': dict,
214 'tools_version': dict,
215 'other_config': dict,
216 'target': int,
217 'security_label': str,
218 'pci': str,
219 'cpuid' : dict,
220 'cpuid_check' : dict,
221 'machine_address_size': int,
222 'suppress_spurious_page_faults': bool0,
223 's3_integrity' : int,
224 'superpages' : int,
225 }
227 # List of legacy configuration keys that have no equivalent in the
228 # Xen API, but are still stored in XendConfig.
230 LEGACY_UNSUPPORTED_BY_XENAPI_CFG = [
231 # roundtripped (dynamic, unmodified)
232 'shadow_memory',
233 'vcpu_avail',
234 'features',
235 # read/write
236 'on_xend_start',
237 'on_xend_stop',
238 # read-only
239 'domid',
240 'start_time',
241 'cpu_time',
242 'online_vcpus',
243 # write-once
244 'cpu',
245 'cpus',
246 ]
248 LEGACY_CFG_TYPES = {
249 'uuid': str,
250 'name': str,
251 'vcpus': int,
252 'vcpu_avail': long,
253 'memory': int,
254 'shadow_memory': int,
255 'maxmem': int,
256 'start_time': float,
257 'cpu_time': float,
258 'features': str,
259 'localtime': int,
260 'name': str,
261 'on_poweroff': str,
262 'on_reboot': str,
263 'on_crash': str,
264 'on_xend_stop': str,
265 'on_xend_start': str,
266 'online_vcpus': int,
267 'rtc/timeoffset': str,
268 'bootloader': str,
269 'bootloader_args': str,
270 }
272 # Values that should be stored in xenstore's /vm/<uuid> that is used
273 # by Xend. Used in XendDomainInfo to restore running VM state from
274 # xenstore.
275 LEGACY_XENSTORE_VM_PARAMS = [
276 'uuid',
277 'name',
278 'vcpus',
279 'vcpu_avail',
280 'memory',
281 'shadow_memory',
282 'maxmem',
283 'start_time',
284 'name',
285 'on_poweroff',
286 'on_crash',
287 'on_reboot',
288 'on_xend_start',
289 'on_xend_stop',
290 'bootloader',
291 'bootloader_args',
292 ]
294 ##
295 ## Config Choices
296 ##
298 CONFIG_RESTART_MODES = ('restart', 'destroy', 'preserve', 'rename-restart',
299 'coredump-destroy', 'coredump-restart')
300 CONFIG_OLD_DOM_STATES = ('running', 'blocked', 'paused', 'shutdown',
301 'crashed', 'dying')
303 class XendConfigError(VmError):
304 def __str__(self):
305 return 'Invalid Configuration: %s' % str(self.value)
307 ##
308 ## XendConfig Class (an extended dictionary)
309 ##
311 class XendConfig(dict):
312 """ The new Xend VM Configuration.
314 Stores the configuration in xenapi compatible format but retains
315 import and export functions for SXP.
316 """
317 def __init__(self, filename = None, sxp_obj = None,
318 xapi = None, dominfo = None):
320 dict.__init__(self)
321 self.update(self._defaults())
323 if filename:
324 try:
325 sxp_obj = sxp.parse(open(filename,'r'))
326 sxp_obj = sxp_obj[0]
327 except IOError, e:
328 raise XendConfigError("Unable to read file: %s" % filename)
330 if sxp_obj:
331 self._sxp_to_xapi(sxp_obj)
332 self._sxp_to_xapi_unsupported(sxp_obj)
333 elif xapi:
334 self.update_with_xenapi_config(xapi)
335 elif dominfo:
336 # output from xc.domain_getinfo
337 self._dominfo_to_xapi(dominfo, update_mem = True)
339 log.debug('XendConfig.init: %s' % scrub_password(self))
341 # validators go here
342 self.validate()
344 """ In time, we should enable this type checking addition. It is great
345 also for tracking bugs and unintended writes to XendDomainInfo.info
346 def __setitem__(self, key, value):
347 type_conv = XENAPI_CFG_TYPES.get(key)
348 if callable(type_conv):
349 try:
350 dict.__setitem__(self, key, type_conv(value))
351 except (ValueError, TypeError):
352 raise XendConfigError("Wrong type for configuration value " +
353 "%s. Expected %s" %
354 (key, type_conv.__name__))
355 else:
356 dict.__setitem__(self, key, value)
357 """
359 def _defaults(self):
360 defaults = {
361 'name_label': 'Domain-Unnamed',
362 'actions_after_shutdown': 'destroy',
363 'actions_after_reboot': 'restart',
364 'actions_after_crash': 'restart',
365 'actions_after_suspend': '',
366 'is_a_template': False,
367 'auto_power_on': False,
368 'is_control_domain': False,
369 'features': '',
370 'PV_bootloader': '',
371 'PV_kernel': '',
372 'PV_ramdisk': '',
373 'PV_args': '',
374 'PV_bootloader_args': '',
375 'HVM_boot_policy': '',
376 'HVM_boot_params': {},
377 'memory_static_min': 0,
378 'memory_dynamic_min': 0,
379 'shadow_memory': 0,
380 'memory_static_max': 0,
381 'memory_dynamic_max': 0,
382 'devices': {},
383 'on_xend_start': 'ignore',
384 'on_xend_stop': 'ignore',
385 'cpus': [],
386 'VCPUs_max': 1,
387 'VCPUs_live': 1,
388 'VCPUs_at_startup': 1,
389 'vcpus_params': {},
390 'console_refs': [],
391 'vif_refs': [],
392 'vbd_refs': [],
393 'vtpm_refs': [],
394 'other_config': {},
395 'platform': {},
396 'target': 0,
397 'superpages': 0,
398 }
400 return defaults
402 #
403 # Here we assume these values exist in the dict.
404 # If they don't we have a bigger problem, lets not
405 # try and 'fix it up' but acutually fix the cause ;-)
406 #
407 def _memory_sanity_check(self):
408 log.trace("_memory_sanity_check memory_static_min: %s, "
409 "memory_static_max: %i, "
410 "memory_dynamic_min: %i, "
411 "memory_dynamic_max: %i",
412 self["memory_static_min"],
413 self["memory_static_max"],
414 self["memory_dynamic_min"],
415 self["memory_dynamic_max"])
417 if not self["memory_static_min"] <= self["memory_static_max"]:
418 raise XendConfigError("memory_static_min must be less " \
419 "than or equal to memory_static_max")
420 if not self["memory_static_min"] <= self["memory_dynamic_min"]:
421 raise XendConfigError("memory_static_min must be less " \
422 "than or equal to memory_dynamic_min")
423 if not self["memory_dynamic_max"] <= self["memory_static_max"]:
424 raise XendConfigError("memory_dynamic_max must be less " \
425 "than or equal to memory_static_max")
426 if not self["memory_dynamic_max"] > 0:
427 raise XendConfigError("memory_dynamic_max must be greater " \
428 "than zero")
429 if not self["memory_static_max"] > 0:
430 raise XendConfigError("memory_static_max must be greater " \
431 "than zero")
433 def _actions_sanity_check(self):
434 for event in ['shutdown', 'reboot', 'crash']:
435 if self['actions_after_' + event] not in CONFIG_RESTART_MODES:
436 raise XendConfigError('Invalid event handling mode: ' +
437 event)
439 def _vcpus_sanity_check(self):
440 if 'VCPUs_max' in self and 'vcpu_avail' not in self:
441 self['vcpu_avail'] = (1 << self['VCPUs_max']) - 1
442 if 'online_vcpus' in self:
443 self['VCPUs_live'] = self['online_vcpus']
445 def _uuid_sanity_check(self):
446 """Make sure UUID is in proper string format with hyphens."""
447 if 'uuid' not in self or not self['uuid']:
448 self['uuid'] = uuid.createString()
449 else:
450 self['uuid'] = uuid.toString(uuid.fromString(self['uuid']))
452 def _name_sanity_check(self):
453 if 'name_label' not in self:
454 self['name_label'] = 'Domain-' + self['uuid']
456 def _platform_sanity_check(self):
457 if 'keymap' not in self['platform'] and XendOptions.instance().get_keymap():
458 self['platform']['keymap'] = XendOptions.instance().get_keymap()
460 if self.is_hvm() or self.has_rfb():
461 if 'device_model' not in self['platform']:
462 self['platform']['device_model'] = xen.util.auxbin.pathTo("qemu-dm")
464 if self.is_hvm():
465 if 'timer_mode' not in self['platform']:
466 self['platform']['timer_mode'] = 1
467 if 'viridian' not in self['platform']:
468 self['platform']['viridian'] = 0
469 if 'rtc_timeoffset' not in self['platform']:
470 self['platform']['rtc_timeoffset'] = 0
471 if 'hpet' not in self['platform']:
472 self['platform']['hpet'] = 0
473 if 'xen_platform_pci' not in self['platform']:
474 self['platform']['xen_platform_pci'] = 1
475 if 'vpt_align' not in self['platform']:
476 self['platform']['vpt_align'] = 1
477 if 'loader' not in self['platform']:
478 # Old configs may have hvmloader set as PV_kernel param
479 if self.has_key('PV_kernel') and self['PV_kernel'] != '':
480 self['platform']['loader'] = self['PV_kernel']
481 self['PV_kernel'] = ''
482 else:
483 self['platform']['loader'] = "/usr/lib/xen/boot/hvmloader"
484 log.debug("Loader is %s" % str(self['platform']['loader']))
486 # Compatibility hack, can go away soon.
487 if 'soundhw' not in self['platform'] and \
488 self['platform'].get('enable_audio'):
489 self['platform']['soundhw'] = 'sb16'
491 def validate(self):
492 self._uuid_sanity_check()
493 self._name_sanity_check()
494 self._memory_sanity_check()
495 self._actions_sanity_check()
496 self._vcpus_sanity_check()
497 self._platform_sanity_check()
499 def _dominfo_to_xapi(self, dominfo, update_mem = False):
500 self['domid'] = dominfo['domid']
501 self['online_vcpus'] = dominfo['online_vcpus']
502 self['VCPUs_max'] = dominfo['max_vcpu_id'] + 1
504 if update_mem:
505 self['memory_dynamic_min'] = dominfo['mem_kb'] * 1024
506 self['memory_dynamic_max'] = dominfo['mem_kb'] * 1024
507 self['memory_static_min'] = 0
508 self['memory_static_max'] = dominfo['maxmem_kb'] * 1024
509 self._memory_sanity_check()
511 self['cpu_time'] = dominfo['cpu_time']/1e9
512 if dominfo.get('ssidref'):
513 ssidref = int(dominfo.get('ssidref'))
514 import xen.util.xsm.xsm as security
515 self['security_label'] = security.ssidref2security_label(ssidref)
517 self['shutdown_reason'] = dominfo['shutdown_reason']
519 # parse state into Xen API states
520 self['running'] = dominfo['running']
521 self['crashed'] = dominfo['crashed']
522 self['dying'] = dominfo['dying']
523 self['shutdown'] = dominfo['shutdown']
524 self['paused'] = dominfo['paused']
525 self['blocked'] = dominfo['blocked']
527 if 'name' in dominfo:
528 self['name_label'] = dominfo['name']
530 if 'handle' in dominfo:
531 self['uuid'] = uuid.toString(dominfo['handle'])
533 def parse_cpuid(self, cfg, field):
534 def int2bin(n, count=32):
535 return "".join([str((n >> y) & 1) for y in range(count-1, -1, -1)])
537 for input, regs in cfg[field].iteritems():
538 if not regs is dict:
539 cfg[field][input] = dict(regs)
541 cpuid = {}
542 for input in cfg[field]:
543 inputs = input.split(',')
544 if inputs[0][0:2] == '0x':
545 inputs[0] = str(int(inputs[0], 16))
546 if len(inputs) == 2:
547 if inputs[1][0:2] == '0x':
548 inputs[1] = str(int(inputs[1], 16))
549 new_input = ','.join(inputs)
550 cpuid[new_input] = {} # new input
551 for reg in cfg[field][input]:
552 val = cfg[field][input][reg]
553 if val[0:2] == '0x':
554 cpuid[new_input][reg] = int2bin(int(val, 16))
555 else:
556 cpuid[new_input][reg] = val
557 cfg[field] = cpuid
559 def _parse_sxp(self, sxp_cfg):
560 """ Populate this XendConfig using the parsed SXP.
562 @param sxp_cfg: Parsed SXP Configuration
563 @type sxp_cfg: list of lists
564 @rtype: dictionary
565 @return: A dictionary containing the parsed options of the SXP.
566 """
567 cfg = {}
569 for key, typ in XENAPI_CFG_TYPES.items():
570 val = sxp.child_value(sxp_cfg, key)
571 if val is not None:
572 try:
573 cfg[key] = typ(val)
574 except (ValueError, TypeError), e:
575 log.warn('Unable to convert type value for key: %s' % key)
577 # Convert deprecated options to current equivalents.
579 restart = sxp.child_value(sxp_cfg, 'restart')
580 if restart:
581 if restart == 'onreboot':
582 cfg['on_poweroff'] = 'destroy'
583 cfg['on_reboot'] = 'restart'
584 cfg['on_crash'] = 'destroy'
585 elif restart == 'always':
586 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
587 cfg[opt] = 'restart'
588 elif restart == 'never':
589 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
590 cfg[opt] = 'never'
591 else:
592 log.warn('Ignoring unrecognised value for deprecated option:'
593 'restart = \'%s\'', restart)
595 # Handle memory, passed in as MiB
597 if sxp.child_value(sxp_cfg, "memory") != None:
598 cfg["memory"] = int(sxp.child_value(sxp_cfg, "memory"))
599 if sxp.child_value(sxp_cfg, "maxmem") != None:
600 cfg["maxmem"] = int(sxp.child_value(sxp_cfg, "maxmem"))
602 # Convert scheduling parameters to vcpus_params
603 if 'vcpus_params' not in cfg:
604 cfg['vcpus_params'] = {}
605 cfg["vcpus_params"]["weight"] = \
606 int(sxp.child_value(sxp_cfg, "cpu_weight", 256))
607 cfg["vcpus_params"]["cap"] = \
608 int(sxp.child_value(sxp_cfg, "cpu_cap", 0))
610 # Only extract options we know about.
611 extract_keys = LEGACY_UNSUPPORTED_BY_XENAPI_CFG + \
612 XENAPI_CFG_TO_LEGACY_CFG.values()
614 for key in extract_keys:
615 val = sxp.child_value(sxp_cfg, key)
616 if val != None:
617 try:
618 cfg[key] = LEGACY_CFG_TYPES[key](val)
619 except KeyError:
620 cfg[key] = val
621 except (TypeError, ValueError), e:
622 log.warn("Unable to parse key %s: %s: %s" %
623 (key, str(val), e))
625 if 'platform' not in cfg:
626 cfg['platform'] = {}
627 localtime = sxp.child_value(sxp_cfg, 'localtime')
628 if localtime is not None:
629 cfg['platform']['localtime'] = localtime
631 # Compatibility hack -- can go soon.
632 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
633 val = sxp.child_value(sxp_cfg, "platform_" + key, None)
634 if val is not None:
635 self['platform'][key] = val
637 # Compatibility hack -- can go soon.
638 boot_order = sxp.child_value(sxp_cfg, 'HVM_boot')
639 if boot_order:
640 cfg['HVM_boot_policy'] = 'BIOS order'
641 cfg['HVM_boot_params'] = { 'order' : boot_order }
644 # Parsing the device SXP's.
645 cfg['devices'] = {}
646 for dev in sxp.children(sxp_cfg, 'device'):
647 config = sxp.child0(dev)
648 dev_type = sxp.name(config)
649 self.device_add(dev_type, cfg_sxp = config, target = cfg)
651 # Extract missing data from configuration entries
652 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
653 if image_sxp:
654 image_vcpus = sxp.child_value(image_sxp, 'vcpus')
655 if image_vcpus != None:
656 try:
657 if 'VCPUs_max' not in cfg:
658 cfg['VCPUs_max'] = int(image_vcpus)
659 elif cfg['VCPUs_max'] != int(image_vcpus):
660 cfg['VCPUs_max'] = int(image_vcpus)
661 log.warn('Overriding vcpus from %d to %d using image'
662 'vcpus value.', cfg['VCPUs_max'])
663 except ValueError, e:
664 raise XendConfigError('integer expeceted: %s: %s' %
665 image_sxp, e)
667 # Deprecated cpu configuration
668 if 'cpu' in cfg:
669 if 'cpus' in cfg:
670 cfg['cpus'] = "%s,%s" % (str(cfg['cpu']), cfg['cpus'])
671 else:
672 cfg['cpus'] = str(cfg['cpu'])
674 # Convert 'cpus' to list of list of ints
675 cpus_list = []
676 if 'cpus' in cfg:
677 # Convert the following string to list of ints.
678 # The string supports a list of ranges (0-3),
679 # seperated by commas, and negation (^1).
680 # Precedence is settled by order of the string:
681 # "0-3,^1" -> [0,2,3]
682 # "0-3,^1,1" -> [0,1,2,3]
683 def cnv(s):
684 l = []
685 for c in s.split(','):
686 if c.find('-') != -1:
687 (x, y) = c.split('-')
688 for i in range(int(x), int(y)+1):
689 l.append(int(i))
690 else:
691 # remove this element from the list
692 if c[0] == '^':
693 l = [x for x in l if x != int(c[1:])]
694 else:
695 l.append(int(c))
696 return l
698 if type(cfg['cpus']) == list:
699 if len(cfg['cpus']) > 0 and type(cfg['cpus'][0]) == list:
700 # If sxp_cfg was created from config.sxp,
701 # the form of 'cpus' is list of list of string.
702 # Convert 'cpus' to list of list of ints.
703 # Conversion examples:
704 # [['1']] -> [[1]]
705 # [['0','2'],['1','3']] -> [[0,2],[1,3]]
706 try:
707 for c1 in cfg['cpus']:
708 cpus = []
709 for c2 in c1:
710 cpus.append(int(c2))
711 cpus_list.append(cpus)
712 except ValueError, e:
713 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
714 else:
715 # Conversion examples:
716 # ["1"] -> [[1]]
717 # ["0,2","1,3"] -> [[0,2],[1,3]]
718 # ["0-3,^1","1-4,^2"] -> [[0,2,3],[1,3,4]]
719 try:
720 for c in cfg['cpus']:
721 cpus = cnv(c)
722 cpus_list.append(cpus)
723 except ValueError, e:
724 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
726 if len(cpus_list) != cfg['vcpus']:
727 raise XendConfigError('vcpus and the item number of cpus are not same')
728 else:
729 # Conversion examples:
730 # vcpus=1:
731 # "1" -> [[1]]
732 # "0-3,^1" -> [[0,2,3]]
733 # vcpus=2:
734 # "1" -> [[1],[1]]
735 # "0-3,^1" -> [[0,2,3],[0,2,3]]
736 try:
737 cpus = cnv(cfg['cpus'])
738 for v in range(0, cfg['vcpus']):
739 cpus_list.append(cpus)
740 except ValueError, e:
741 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
742 else:
743 # Generation examples:
744 # vcpus=1:
745 # -> [[]]
746 # vcpus=2:
747 # -> [[],[]]
748 for v in range(0, cfg['vcpus']):
749 cpus_list.append(list())
751 cfg['cpus'] = cpus_list
753 # Parse cpuid
754 if 'cpuid' in cfg:
755 self.parse_cpuid(cfg, 'cpuid')
756 if 'cpuid_check' in cfg:
757 self.parse_cpuid(cfg, 'cpuid_check')
759 import xen.util.xsm.xsm as security
760 if security.on() == xsconstants.XS_POLICY_USE:
761 from xen.util.acmpolicy import ACM_LABEL_UNLABELED
762 if not 'security' in cfg and sxp.child_value(sxp_cfg, 'security'):
763 cfg['security'] = sxp.child_value(sxp_cfg, 'security')
764 elif not cfg.get('security_label'):
765 cfg['security'] = [['access_control',
766 ['policy', security.get_active_policy_name() ],
767 ['label', ACM_LABEL_UNLABELED ]]]
769 if 'security' in cfg and not cfg.get('security_label'):
770 secinfo = cfg['security']
771 # The xm command sends a list formatted like this:
772 # [['access_control', ['policy', 'xm-test'],['label', 'red']],
773 # ['ssidref', 196611]]
774 policy = ""
775 label = ""
776 for idx in range(0, len(secinfo)):
777 if secinfo[idx][0] == "access_control":
778 for aidx in range(1, len(secinfo[idx])):
779 if secinfo[idx][aidx][0] == "policy":
780 policy = secinfo[idx][aidx][1]
781 if secinfo[idx][aidx][0] == "label":
782 label = secinfo[idx][aidx][1]
783 cfg['security_label'] = \
784 security.set_security_label(policy, label)
785 if not sxp.child_value(sxp_cfg, 'security_label'):
786 del cfg['security']
788 sec_lab = cfg['security_label'].split(":")
789 if len(sec_lab) != 3:
790 raise XendConfigError("Badly formatted security label: %s"
791 % cfg['security_label'])
793 old_state = sxp.child_value(sxp_cfg, 'state')
794 if old_state:
795 for i in range(len(CONFIG_OLD_DOM_STATES)):
796 cfg[CONFIG_OLD_DOM_STATES[i]] = int(old_state[i] != '-')
798 return cfg
801 def _sxp_to_xapi(self, sxp_cfg):
802 """Read in an SXP Configuration object and
803 populate at much of the Xen API with valid values.
804 """
805 log.debug('_sxp_to_xapi(%s)' % scrub_password(sxp_cfg))
807 # _parse_sxp() below will call device_add() and construct devices.
808 # Some devices may require VM's uuid, so setup self['uuid']
809 # beforehand.
810 self['uuid'] = sxp.child_value(sxp_cfg, 'uuid', uuid.createString())
812 cfg = self._parse_sxp(sxp_cfg)
814 for key, typ in XENAPI_CFG_TYPES.items():
815 val = cfg.get(key)
816 if val is not None:
817 self[key] = typ(val)
819 # Convert parameters that can be directly mapped from
820 # the Legacy Config to Xen API Config
822 for apikey, cfgkey in XENAPI_CFG_TO_LEGACY_CFG.items():
823 try:
824 type_conv = XENAPI_CFG_TYPES.get(apikey)
825 if callable(type_conv):
826 self[apikey] = type_conv(cfg[cfgkey])
827 else:
828 log.warn("Unconverted key: " + apikey)
829 self[apikey] = cfg[cfgkey]
830 except KeyError:
831 pass
833 # Lets try and handle memory correctly
835 MiB = 1024 * 1024
837 if "memory" in cfg:
838 self["memory_static_min"] = 0
839 self["memory_static_max"] = int(cfg["memory"]) * MiB
840 self["memory_dynamic_min"] = int(cfg["memory"]) * MiB
841 self["memory_dynamic_max"] = int(cfg["memory"]) * MiB
843 if "maxmem" in cfg:
844 self["memory_static_max"] = int(cfg["maxmem"]) * MiB
846 self._memory_sanity_check()
848 def update_with(n, o):
849 if not self.get(n):
850 self[n] = cfg.get(o, '')
852 update_with('PV_bootloader', 'bootloader')
853 update_with('PV_bootloader_args', 'bootloader_args')
855 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
856 if image_sxp:
857 self.update_with_image_sxp(image_sxp)
859 # Convert Legacy HVM parameters to Xen API configuration
860 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
861 if key in cfg:
862 self['platform'][key] = cfg[key]
864 # set device references in the configuration
865 self['devices'] = cfg.get('devices', {})
866 self['console_refs'] = cfg.get('console_refs', [])
867 self['vif_refs'] = cfg.get('vif_refs', [])
868 self['vbd_refs'] = cfg.get('vbd_refs', [])
869 self['vtpm_refs'] = cfg.get('vtpm_refs', [])
871 # coalesce hvm vnc frame buffer with vfb config
872 if self.is_hvm() and int(self['platform'].get('vnc', 0)) != 0:
873 # add vfb device if it isn't there already
874 if not self.has_rfb():
875 dev_config = ['vfb']
876 dev_config.append(['vnc', '1'])
877 # copy VNC related params from platform config to vfb dev conf
878 for key in ['vncpasswd', 'vncunused', 'vncdisplay',
879 'vnclisten']:
880 if key in self['platform']:
881 dev_config.append([key, self['platform'][key]])
883 self.device_add('vfb', cfg_sxp = dev_config)
886 def has_rfb(self):
887 for console_uuid in self['console_refs']:
888 if self['devices'][console_uuid][1].get('protocol') == 'rfb':
889 return True
890 if self['devices'][console_uuid][0] == 'vfb':
891 return True
892 return False
894 def _sxp_to_xapi_unsupported(self, sxp_cfg):
895 """Read in an SXP configuration object and populate
896 values are that not related directly supported in
897 the Xen API.
898 """
900 log.debug('_sxp_to_xapi_unsupported(%s)' % scrub_password(sxp_cfg))
902 # Parse and convert parameters used to configure
903 # the image (as well as HVM images)
904 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
905 if image_sxp:
906 image_type = sxp.name(image_sxp)
907 if image_type != 'hvm' and image_type != 'linux':
908 self['platform']['image_type'] = image_type
910 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
911 val = sxp.child_value(image_sxp, key, None)
912 if val is not None and val != '':
913 self['platform'][key] = val
915 notes = sxp.children(image_sxp, 'notes')
916 if notes:
917 self['notes'] = self.notes_from_sxp(notes[0])
919 self._hvm_boot_params_from_sxp(image_sxp)
921 # extract backend value
923 backend = []
924 for c in sxp.children(sxp_cfg, 'backend'):
925 backend.append(sxp.name(sxp.child0(c)))
926 if backend:
927 self['backend'] = backend
929 # Parse and convert other Non Xen API parameters.
930 def _set_cfg_if_exists(sxp_arg):
931 val = sxp.child_value(sxp_cfg, sxp_arg)
932 if val != None:
933 if LEGACY_CFG_TYPES.get(sxp_arg):
934 self[sxp_arg] = LEGACY_CFG_TYPES[sxp_arg](val)
935 else:
936 self[sxp_arg] = val
938 _set_cfg_if_exists('shadow_memory')
939 _set_cfg_if_exists('features')
940 _set_cfg_if_exists('on_xend_stop')
941 _set_cfg_if_exists('on_xend_start')
942 _set_cfg_if_exists('vcpu_avail')
944 # Parse and store runtime configuration
945 _set_cfg_if_exists('start_time')
946 _set_cfg_if_exists('cpu_time')
947 _set_cfg_if_exists('shutdown_reason')
948 _set_cfg_if_exists('up_time')
949 _set_cfg_if_exists('status') # TODO, deprecated
951 def _get_old_state_string(self):
952 """Returns the old xm state string.
953 @rtype: string
954 @return: old state string
955 """
956 state_string = ''
957 for state_name in CONFIG_OLD_DOM_STATES:
958 on_off = self.get(state_name, 0)
959 if on_off:
960 state_string += state_name[0]
961 else:
962 state_string += '-'
964 return state_string
967 def update_config(self, dominfo):
968 """Update configuration with the output from xc.domain_getinfo().
970 @param dominfo: Domain information via xc.domain_getinfo()
971 @type dominfo: dict
972 """
973 self._dominfo_to_xapi(dominfo)
974 self.validate()
976 def update_with_xenapi_config(self, xapi):
977 """Update configuration with a Xen API VM struct
979 @param xapi: Xen API VM Struct
980 @type xapi: dict
981 """
983 log.debug('update_with_xenapi_config: %s' % scrub_password(xapi))
985 for key, val in xapi.items():
986 type_conv = XENAPI_CFG_TYPES.get(key)
987 if type_conv is None:
988 key = key.lower()
989 type_conv = XENAPI_CFG_TYPES.get(key)
990 if callable(type_conv):
991 self[key] = type_conv(val)
992 else:
993 self[key] = val
995 # XenAPI defines platform as a string-string map. If platform
996 # configuration exists, convert values to appropriate type.
997 if 'platform' in xapi:
998 for key, val in xapi['platform'].items():
999 type_conv = XENAPI_PLATFORM_CFG_TYPES.get(key)
1000 if type_conv is None:
1001 key = key.lower()
1002 type_conv = XENAPI_PLATFORM_CFG_TYPES.get(key)
1003 if callable(type_conv):
1004 self['platform'][key] = type_conv(val)
1005 else:
1006 self['platform'][key] = val
1008 self['vcpus_params']['weight'] = \
1009 int(self['vcpus_params'].get('weight', 256))
1010 self['vcpus_params']['cap'] = int(self['vcpus_params'].get('cap', 0))
1012 def cpuid_to_sxp(self, sxpr, field):
1013 regs_list = []
1014 for input, regs in self[field].iteritems():
1015 reg_list = []
1016 for reg, val in regs.iteritems():
1017 reg_list.append([reg, val])
1018 regs_list.append([input, reg_list])
1019 sxpr.append([field, regs_list])
1022 def to_sxp(self, domain = None, ignore_devices = False, ignore = [],
1023 legacy_only = True):
1024 """ Get SXP representation of this config object.
1026 Incompat: removed store_mfn, console_mfn
1028 @keyword domain: (optional) XendDomainInfo to get extra information
1029 from such as domid and running devices.
1030 @type domain: XendDomainInfo
1031 @keyword ignore: (optional) list of 'keys' that we do not want
1032 to export.
1033 @type ignore: list of strings
1034 @rtype: list of list (SXP representation)
1035 """
1036 sxpr = ['domain']
1038 # TODO: domid/dom is the same thing but called differently
1039 # depending if it is from xenstore or sxpr.
1041 if domain.getDomid() is not None:
1042 sxpr.append(['domid', domain.getDomid()])
1044 if not legacy_only:
1045 for name, typ in XENAPI_CFG_TYPES.items():
1046 if name in self and self[name] not in (None, []):
1047 if typ == dict:
1048 s = self[name].items()
1049 elif typ == list:
1050 s = self[name]
1051 else:
1052 s = str(self[name])
1053 sxpr.append([name, s])
1055 for xenapi, legacy in XENAPI_CFG_TO_LEGACY_CFG.items():
1056 if self.has_key(xenapi) and self[xenapi] not in (None, []):
1057 if type(self[xenapi]) == bool:
1058 # convert booleans to ints before making an sxp item
1059 sxpr.append([legacy, int(self[xenapi])])
1060 else:
1061 sxpr.append([legacy, self[xenapi]])
1063 MiB = 1024*1024
1065 sxpr.append(["maxmem", int(self["memory_static_max"])/MiB])
1066 sxpr.append(["memory", int(self["memory_dynamic_max"])/MiB])
1068 for legacy in LEGACY_UNSUPPORTED_BY_XENAPI_CFG:
1069 if legacy in ('domid', 'uuid', 'cpus'): # skip these
1070 continue
1071 if self.has_key(legacy) and self[legacy] not in (None, []):
1072 sxpr.append([legacy, self[legacy]])
1074 if self.has_key('security_label'):
1075 sxpr.append(['security_label', self['security_label']])
1077 sxpr.append(['image', self.image_sxpr()])
1078 sxpr.append(['status', domain._stateGet()])
1080 if domain.getDomid() is not None:
1081 sxpr.append(['state', self._get_old_state_string()])
1083 if domain:
1084 if domain.store_mfn:
1085 sxpr.append(['store_mfn', domain.store_mfn])
1086 if domain.console_mfn:
1087 sxpr.append(['console_mfn', domain.console_mfn])
1090 # Marshall devices (running or from configuration)
1091 if not ignore_devices:
1092 txn = xstransact()
1093 try:
1094 for cls in XendDevices.valid_devices():
1095 found = False
1097 # figure if there is a dev controller is valid and running
1098 if domain and domain.getDomid() != None:
1099 try:
1100 controller = domain.getDeviceController(cls)
1101 configs = controller.configurations(txn)
1102 for config in configs:
1103 if sxp.name(config) in ('vbd', 'tap'):
1104 dev_uuid = sxp.child_value(config, 'uuid')
1105 dev_type, dev_cfg = self['devices'][dev_uuid]
1106 if sxp.child_value(config, 'bootable', None) is None:
1107 is_bootable = dev_cfg.get('bootable', 0)
1108 config.append(['bootable', int(is_bootable)])
1109 config.append(['VDI', dev_cfg.get('VDI', '')])
1111 sxpr.append(['device', config])
1113 found = True
1114 except:
1115 log.exception("dumping sxp from device controllers")
1116 pass
1118 # if we didn't find that device, check the existing config
1119 # for a device in the same class
1120 if not found:
1121 for dev_type, dev_info in self.all_devices_sxpr():
1122 if dev_type == cls:
1123 sxpr.append(['device', dev_info])
1125 txn.commit()
1126 except:
1127 txn.abort()
1128 raise
1130 if 'cpuid' in self:
1131 self.cpuid_to_sxp(sxpr, 'cpuid')
1132 if 'cpuid_check' in self:
1133 self.cpuid_to_sxp(sxpr, 'cpuid_check')
1135 log.debug(sxpr)
1137 return sxpr
1139 def _blkdev_name_to_number(self, dev):
1140 if 'ioemu:' in dev:
1141 _, dev = dev.split(':', 1)
1142 try:
1143 dev, _ = dev.split(':', 1)
1144 except ValueError:
1145 pass
1147 try:
1148 devid = int(dev)
1149 except ValueError:
1150 # devid is not a number but a string containing either device
1151 # name (e.g. xvda) or device_type/device_id (e.g. vbd/51728)
1152 dev2 = type(dev) is str and dev.split('/')[-1] or None
1153 if dev2 == None:
1154 log.debug("Could not check the device %s", dev)
1155 return None
1156 try:
1157 devid = int(dev2)
1158 except ValueError:
1159 (xenbus, devid) = blkdev_name_to_number(dev2)
1160 if devid == None:
1161 log.debug("The device %s is not device name", dev2)
1162 return None
1163 return devid
1165 def device_duplicate_check(self, dev_type, dev_info, defined_config, config):
1166 defined_devices_sxpr = self.all_devices_sxpr(target = defined_config)
1168 if dev_type == 'vbd' or dev_type == 'tap':
1169 dev_uname = dev_info.get('uname')
1170 blkdev_name = dev_info.get('dev')
1171 devid = self._blkdev_name_to_number(blkdev_name)
1172 if devid == None or dev_uname == None:
1173 return
1175 for o_dev_type, o_dev_info in defined_devices_sxpr:
1176 if o_dev_type == 'vbd' or o_dev_type == 'tap':
1177 blkdev_file = blkdev_uname_to_file(dev_uname)
1178 o_dev_uname = sxp.child_value(o_dev_info, 'uname')
1179 if o_dev_uname != None:
1180 o_blkdev_file = blkdev_uname_to_file(o_dev_uname)
1181 if blkdev_file == o_blkdev_file:
1182 raise XendConfigError('The file "%s" is already used' %
1183 blkdev_file)
1184 if dev_uname == o_dev_uname:
1185 raise XendConfigError('The uname "%s" is already defined' %
1186 dev_uname)
1187 o_blkdev_name = sxp.child_value(o_dev_info, 'dev')
1188 o_devid = self._blkdev_name_to_number(o_blkdev_name)
1189 if o_devid != None and devid == o_devid:
1190 name_array = blkdev_name.split(':', 2)
1191 if len(name_array) == 2 and name_array[1] == 'cdrom':
1193 # Since the device is a cdrom, we are most likely
1194 # inserting, changing, or removing a cd. We can
1195 # update the old device instead of creating a new
1196 # one.
1198 if o_dev_uname != None and dev_uname == None:
1200 # We are removing a cd. We can simply update
1201 # the uname on the existing device.
1203 merge_sxp = sxp.from_string("('vbd' ('uname' ''))")
1204 else:
1205 merge_sxp = config
1207 dev_uuid = sxp.child_value(o_dev_info, 'uuid')
1208 if dev_uuid != None and \
1209 self.device_update(dev_uuid, cfg_sxp = merge_sxp):
1210 return dev_uuid
1212 raise XendConfigError('The device "%s" is already defined' %
1213 blkdev_name)
1215 elif dev_type == 'vif':
1216 dev_mac = dev_info.get('mac')
1218 for o_dev_type, o_dev_info in defined_devices_sxpr:
1219 if dev_type == o_dev_type:
1220 if dev_mac.lower() == sxp.child_value(o_dev_info, 'mac').lower():
1221 raise XendConfigError('The mac "%s" is already defined' %
1222 dev_mac)
1223 return None
1225 def create_dpci_from_sxp(self, pci_devs):
1226 for pci_dev in pci_devs:
1227 dpci_uuid = pci_dev.get('uuid')
1228 log.debug("create_dpci_from_sxp: %s" % pci_dev)
1229 ppci_uuid = XendPPCI.get_by_sbdf(pci_dev['domain'],
1230 pci_dev['bus'],
1231 pci_dev['slot'],
1232 pci_dev['func'])
1233 if ppci_uuid is None:
1234 continue
1235 dpci_record = {
1236 'VM': self['uuid'],
1237 'PPCI': ppci_uuid,
1238 'hotplug_slot': pci_dev.get('vslot',
1239 '0x' + AUTO_PHP_SLOT_STR)
1242 dpci_opts = pci_dev.get('opts')
1243 if dpci_opts and len(dpci_opts) > 0:
1244 dpci_record['options'] = dpci_opts
1246 XendDPCI(dpci_uuid, dpci_record)
1248 def device_add(self, dev_type, cfg_sxp = None, cfg_xenapi = None,
1249 target = None):
1250 """Add a device configuration in SXP format or XenAPI struct format.
1252 For SXP, it could be either:
1254 [device, [vbd, [uname ...]]
1256 or:
1258 [vbd, [uname ..]]
1260 @type cfg_sxp: list of lists (parsed sxp object)
1261 @param cfg_sxp: SXP configuration object
1262 @type cfg_xenapi: dict
1263 @param cfg_xenapi: A device configuration from Xen API (eg. vbd,vif)
1264 @param target: write device information to
1265 @type target: None or a dictionary
1266 @rtype: string
1267 @return: Assigned UUID of the device.
1268 """
1269 if target == None:
1270 target = self
1272 if dev_type not in XendDevices.valid_devices():
1273 raise XendConfigError("XendConfig: %s not a valid device type" %
1274 dev_type)
1276 if cfg_sxp == None and cfg_xenapi == None:
1277 raise XendConfigError("XendConfig: device_add requires some "
1278 "config.")
1280 #if cfg_sxp:
1281 # log.debug("XendConfig.device_add: %s" % str(cfg_sxp))
1282 #if cfg_xenapi:
1283 # log.debug("XendConfig.device_add: %s" % str(cfg_xenapi))
1285 if cfg_sxp:
1286 if sxp.child0(cfg_sxp) == 'device':
1287 config = sxp.child0(cfg_sxp)
1288 else:
1289 config = cfg_sxp
1291 dev_type = sxp.name(config)
1292 dev_info = {}
1294 if dev_type == 'pci':
1295 pci_devs_uuid = sxp.child_value(config, 'uuid',
1296 uuid.createString())
1298 pci_dict = self.pci_convert_sxp_to_dict(config)
1299 pci_devs = pci_dict['devs']
1301 # create XenAPI DPCI objects.
1302 self.create_dpci_from_sxp(pci_devs)
1304 target['devices'][pci_devs_uuid] = (dev_type,
1305 {'devs': pci_devs,
1306 'uuid': pci_devs_uuid})
1308 log.debug("XendConfig: reading device: %s" % pci_devs)
1310 return pci_devs_uuid
1312 if dev_type == 'vscsi':
1313 vscsi_devs_uuid = sxp.child_value(config, 'uuid',
1314 uuid.createString())
1315 vscsi_dict = self.vscsi_convert_sxp_to_dict(config)
1316 vscsi_devs = vscsi_dict['devs']
1317 vscsi_mode = vscsi_dict['feature-host']
1318 vscsi_be = vscsi_dict.get('backend', None)
1320 # create XenAPI DSCSI objects.
1321 for vscsi_dev in vscsi_devs:
1322 dscsi_uuid = vscsi_dev.get('uuid')
1323 pscsi_uuid = XendPSCSI.get_by_HCTL(vscsi_dev['p-dev'])
1324 if pscsi_uuid is None:
1325 continue
1326 dscsi_record = {
1327 'VM': self['uuid'],
1328 'PSCSI': pscsi_uuid,
1329 'virtual_HCTL': vscsi_dev.get('v-dev')
1331 XendDSCSI(dscsi_uuid, dscsi_record)
1333 vscsi_info = {
1334 'devs': vscsi_devs,
1335 'feature-host': vscsi_mode,
1336 'uuid': vscsi_devs_uuid
1338 if vscsi_be is not None:
1339 vscsi_info['backend'] = vscsi_be
1340 target['devices'][vscsi_devs_uuid] = (dev_type, vscsi_info)
1341 log.debug("XendConfig: reading device: %s,%s" % \
1342 (vscsi_devs, vscsi_mode))
1343 return vscsi_devs_uuid
1345 for opt_val in config[1:]:
1346 try:
1347 opt, val = opt_val
1348 dev_info[opt] = val
1349 except (TypeError, ValueError): # unpack error
1350 pass
1352 if dev_type == 'vbd':
1353 if dev_info.get('dev', '').startswith('ioemu:'):
1354 dev_info['driver'] = 'ioemu'
1355 else:
1356 dev_info['driver'] = 'paravirtualised'
1358 if dev_type == 'tap':
1359 if dev_info['uname'].split(':')[1] not in blktap_disk_types:
1360 raise XendConfigError("tap:%s not a valid disk type" %
1361 dev_info['uname'].split(':')[1])
1363 if dev_type == 'vif':
1364 if not dev_info.get('mac'):
1365 dev_info['mac'] = randomMAC()
1367 ret_uuid = self.device_duplicate_check(dev_type, dev_info, target, config)
1368 if ret_uuid != None:
1369 return ret_uuid
1371 if dev_type == 'vif':
1372 if dev_info.get('policy') and dev_info.get('label'):
1373 dev_info['security_label'] = "%s:%s:%s" % \
1374 (xsconstants.ACM_POLICY_ID,
1375 dev_info['policy'],dev_info['label'])
1377 # create uuid if it doesn't exist
1378 dev_uuid = dev_info.get('uuid', None)
1379 if not dev_uuid:
1380 dev_uuid = uuid.createString()
1381 dev_info['uuid'] = dev_uuid
1383 # store dev references by uuid for certain device types
1384 target['devices'][dev_uuid] = (dev_type, dev_info)
1385 if dev_type in ('vif', 'vbd', 'vtpm'):
1386 param = '%s_refs' % dev_type
1387 if param not in target:
1388 target[param] = []
1389 if dev_uuid not in target[param]:
1390 if dev_type == 'vbd' and 'bootable' not in dev_info:
1391 # Compat hack -- mark first disk bootable
1392 dev_info['bootable'] = int(not target[param])
1393 target[param].append(dev_uuid)
1394 elif dev_type == 'tap':
1395 if 'vbd_refs' not in target:
1396 target['vbd_refs'] = []
1397 if dev_uuid not in target['vbd_refs']:
1398 if 'bootable' not in dev_info:
1399 # Compat hack -- mark first disk bootable
1400 dev_info['bootable'] = int(not target['vbd_refs'])
1401 target['vbd_refs'].append(dev_uuid)
1403 elif dev_type == 'vfb':
1404 # Populate other config with aux data that is associated
1405 # with vfb
1407 other_config = {}
1408 for key in XENAPI_CONSOLE_OTHER_CFG:
1409 if key in dev_info:
1410 other_config[key] = dev_info[key]
1411 target['devices'][dev_uuid][1]['other_config'] = other_config
1414 if 'console_refs' not in target:
1415 target['console_refs'] = []
1417 # Treat VFB devices as console devices so they are found
1418 # through Xen API
1419 if dev_uuid not in target['console_refs']:
1420 target['console_refs'].append(dev_uuid)
1422 # Cope with old-format save files which say under vfb
1423 # (type vfb) rather than (vfb 1)
1424 try:
1425 vfb_type = dev_info['type']
1426 except KeyError:
1427 vfb_type = None
1428 log.debug("iwj dev_type=%s vfb type %s" %
1429 (dev_type, `vfb_type`))
1431 if vfb_type == 'vnc' or vfb_type == 'sdl':
1432 dev_info[vfb_type] = 1
1433 del dev_info['type']
1434 log.debug("iwj dev_type=%s vfb setting dev_info['%s']" %
1435 (dev_type, vfb_type))
1436 if dev_info.get('serial') is not None :
1437 # Create two serial backends now, the location value is bogus, but does not matter
1438 cfg = self.console_add('vt100', '0')
1439 c_uuid = uuid.createString()
1440 target['devices'][c_uuid] = ('console', cfg)
1441 target['console_refs'].append(c_uuid)
1442 cfg = self.console_add('vt100', '1')
1443 c_uuid = uuid.createString()
1444 target['devices'][c_uuid] = ('console', cfg)
1445 target['console_refs'].append(c_uuid)
1447 elif dev_type == 'console':
1448 if 'console_refs' not in target:
1449 target['console_refs'] = []
1450 if dev_uuid not in target['console_refs']:
1451 target['console_refs'].append(dev_uuid)
1453 log.debug("XendConfig: reading device: %s" % scrub_password(dev_info))
1454 return dev_uuid
1456 if cfg_xenapi:
1457 dev_info = {}
1458 dev_uuid = ''
1459 if dev_type == 'vif':
1460 dev_info['mac'] = cfg_xenapi.get('MAC')
1461 if not dev_info['mac']:
1462 dev_info['mac'] = randomMAC()
1463 # vifname is the name on the guest, not dom0
1464 # TODO: we don't have the ability to find that out or
1465 # change it from dom0
1466 #if cfg_xenapi.get('device'): # don't add if blank
1467 # dev_info['vifname'] = cfg_xenapi.get('device')
1468 if cfg_xenapi.get('type'):
1469 dev_info['type'] = cfg_xenapi.get('type')
1470 if cfg_xenapi.get('name'):
1471 dev_info['name'] = cfg_xenapi.get('name')
1472 if cfg_xenapi.get('network'):
1473 network = XendAPIStore.get(
1474 cfg_xenapi.get('network'), 'network')
1475 dev_info['bridge'] = network.get_name_label()
1477 if cfg_xenapi.get('security_label'):
1478 dev_info['security_label'] = \
1479 cfg_xenapi.get('security_label')
1481 dev_uuid = cfg_xenapi.get('uuid', None)
1482 if not dev_uuid:
1483 dev_uuid = uuid.createString()
1484 dev_info['uuid'] = dev_uuid
1485 target['devices'][dev_uuid] = (dev_type, dev_info)
1486 target['vif_refs'].append(dev_uuid)
1488 elif dev_type in ('vbd', 'tap'):
1489 dev_info['type'] = cfg_xenapi.get('type', 'Disk')
1490 if dev_info['type'] == 'CD':
1491 old_vbd_type = 'cdrom'
1492 else:
1493 old_vbd_type = 'disk'
1495 dev_info['uname'] = cfg_xenapi.get('image', '')
1496 dev_info['dev'] = '%s:%s' % (cfg_xenapi.get('device'),
1497 old_vbd_type)
1498 dev_info['bootable'] = int(cfg_xenapi.get('bootable', 0))
1499 dev_info['driver'] = cfg_xenapi.get('driver', '')
1500 dev_info['VDI'] = cfg_xenapi.get('VDI', '')
1502 if cfg_xenapi.get('mode') == 'RW':
1503 dev_info['mode'] = 'w'
1504 else:
1505 dev_info['mode'] = 'r'
1507 dev_uuid = cfg_xenapi.get('uuid', None)
1508 if not dev_uuid:
1509 dev_uuid = uuid.createString()
1510 dev_info['uuid'] = dev_uuid
1511 target['devices'][dev_uuid] = (dev_type, dev_info)
1512 target['vbd_refs'].append(dev_uuid)
1514 elif dev_type == 'vtpm':
1515 if cfg_xenapi.get('type'):
1516 dev_info['type'] = cfg_xenapi.get('type')
1518 dev_uuid = cfg_xenapi.get('uuid', None)
1519 if not dev_uuid:
1520 dev_uuid = uuid.createString()
1521 dev_info['uuid'] = dev_uuid
1522 dev_info['other_config'] = cfg_xenapi.get('other_config', {})
1523 target['devices'][dev_uuid] = (dev_type, dev_info)
1524 target['vtpm_refs'].append(dev_uuid)
1526 elif dev_type == 'console':
1527 dev_uuid = cfg_xenapi.get('uuid', None)
1528 if not dev_uuid:
1529 dev_uuid = uuid.createString()
1530 dev_info['uuid'] = dev_uuid
1531 dev_info['protocol'] = cfg_xenapi.get('protocol', 'rfb')
1532 console_other_config = cfg_xenapi.get('other_config', {})
1533 dev_info['other_config'] = console_other_config
1534 if dev_info['protocol'] == 'rfb':
1535 # collapse other config into devinfo for things
1536 # such as vncpasswd, vncunused, etc.
1537 dev_info.update(console_other_config)
1538 dev_info['vnc'] = console_other_config.get('vnc', '0')
1539 dev_info['sdl'] = console_other_config.get('sdl', '0')
1540 target['devices'][dev_uuid] = ('vfb', dev_info)
1541 target['console_refs'].append(dev_uuid)
1543 # if console is rfb, set device_model ensuring qemu
1544 # is invoked for pvfb services
1545 if 'device_model' not in target['platform']:
1546 target['platform']['device_model'] = \
1547 xen.util.auxbin.pathTo("qemu-dm")
1549 # Finally, if we are a pvfb, we need to make a vkbd
1550 # as well that is not really exposed to Xen API
1551 vkbd_uuid = uuid.createString()
1552 target['devices'][vkbd_uuid] = ('vkbd', {})
1554 elif dev_info['protocol'] == 'vt100':
1555 # if someone tries to create a VT100 console
1556 # via the Xen API, we'll have to ignore it
1557 # because we create one automatically in
1558 # XendDomainInfo._update_consoles
1559 raise XendConfigError('Creating vt100 consoles via '
1560 'Xen API is unsupported')
1562 return dev_uuid
1564 # no valid device to add
1565 return ''
1567 def phantom_device_add(self, dev_type, cfg_xenapi = None,
1568 target = None):
1569 """Add a phantom tap device configuration in XenAPI struct format.
1570 """
1572 if target == None:
1573 target = self
1575 if dev_type not in XendDevices.valid_devices() and \
1576 dev_type not in XendDevices.pseudo_devices():
1577 raise XendConfigError("XendConfig: %s not a valid device type" %
1578 dev_type)
1580 if cfg_xenapi == None:
1581 raise XendConfigError("XendConfig: device_add requires some "
1582 "config.")
1584 if cfg_xenapi:
1585 log.debug("XendConfig.phantom_device_add: %s" % str(cfg_xenapi))
1587 if cfg_xenapi:
1588 dev_info = {}
1589 if dev_type in ('vbd', 'tap'):
1590 if dev_type == 'vbd':
1591 dev_info['uname'] = cfg_xenapi.get('image', '')
1592 dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
1593 elif dev_type == 'tap':
1594 if cfg_xenapi.get('image').find('tap:') == -1:
1595 dev_info['uname'] = 'tap:qcow:%s' % cfg_xenapi.get('image')
1596 dev_info['dev'] = '/dev/%s' % cfg_xenapi.get('device')
1597 dev_info['uname'] = cfg_xenapi.get('image')
1598 dev_info['mode'] = cfg_xenapi.get('mode')
1599 dev_info['backend'] = '0'
1600 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
1601 dev_info['uuid'] = dev_uuid
1602 self['devices'][dev_uuid] = (dev_type, dev_info)
1603 self['vbd_refs'].append(dev_uuid)
1604 return dev_uuid
1606 return ''
1608 def pci_convert_dict_to_sxp(self, dev, state, sub_state = None):
1609 pci_sxp = ['pci', self.dev_dict_to_sxp(dev), ['state', state]]
1610 if sub_state != None:
1611 pci_sxp.append(['sub_state', sub_state])
1612 return pci_sxp
1614 def pci_convert_sxp_to_dict(self, dev_sxp):
1615 """Convert pci device sxp to dict
1616 @param dev_sxp: device configuration
1617 @type dev_sxp: SXP object (parsed config)
1618 @return: dev_config
1619 @rtype: dictionary
1620 """
1621 # Parsing the device SXP's. In most cases, the SXP looks
1622 # like this:
1624 # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]]
1626 # However, for PCI devices it looks like this:
1628 # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1], [func, 2]]]
1630 # It seems the reasoning for this difference is because
1631 # pciif.py needs all the PCI device configurations at
1632 # the same time when creating the devices.
1634 # To further complicate matters, Xen 2.0 configuration format
1635 # uses the following for pci device configuration:
1637 # [device, [pci, [domain, 0], [bus, 0], [dev, 1], [func, 2]]]
1639 # For PCI device hotplug support, the SXP of PCI devices is
1640 # extendend like this:
1642 # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1], [func, 2],
1643 # [vslot, 0]],
1644 # [state, 'Initialising']]]
1646 # 'vslot' shows the virtual hotplug slot number which the PCI device
1647 # is inserted in. This is only effective for HVM domains.
1649 # state 'Initialising' indicates that the device is being attached,
1650 # while state 'Closing' indicates that the device is being detached.
1652 # The Dict looks like this:
1654 # { devs: [{domain: 0, bus: 0, slot: 1, func: 2, vslot: 0}],
1655 # states: ['Initialising'] }
1657 dev_config = {}
1659 pci_devs = []
1660 for pci_dev in sxp.children(dev_sxp, 'dev'):
1661 pci_dev_info = dict(pci_dev[1:])
1662 if 'opts' in pci_dev_info:
1663 pci_dev_info['opts'] = pci_opts_list_from_sxp(pci_dev)
1664 # append uuid to each pci device that does't already have one.
1665 if not pci_dev_info.has_key('uuid'):
1666 dpci_uuid = pci_dev_info.get('uuid', uuid.createString())
1667 pci_dev_info['uuid'] = dpci_uuid
1668 pci_devs.append(pci_dev_info)
1669 dev_config['devs'] = pci_devs
1671 pci_states = []
1672 for pci_state in sxp.children(dev_sxp, 'state'):
1673 try:
1674 pci_states.append(pci_state[1])
1675 except IndexError:
1676 raise XendError("Error reading state while parsing pci sxp")
1677 dev_config['states'] = pci_states
1679 return dev_config
1681 def vscsi_convert_sxp_to_dict(self, dev_sxp):
1682 """Convert vscsi device sxp to dict
1683 @param dev_sxp: device configuration
1684 @type dev_sxp: SXP object (parsed config)
1685 @return: dev_config
1686 @rtype: dictionary
1687 """
1688 # Parsing the device SXP's. In most cases, the SXP looks
1689 # like this:
1691 # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]]
1693 # However, for SCSI devices it looks like this:
1695 # [device,
1696 # [vscsi,
1697 # [feature-host, 0],
1698 # [backend, 0],
1699 # [dev,
1700 # [devid, 0], [p-devname, sdb], [p-dev, 1:0:0:1],
1701 # [v-dev, 0:0:0:0], [state, 1]
1702 # ],
1703 # [dev,
1704 # [devid, 0], [p-devname, sdc], [p-dev, 1:0:0:2],
1705 # [v-dev, 0:0:0:1], [satet, 1]
1706 # ]
1707 # ],
1708 # [vscsi,
1709 # [feature-host, 1],
1710 # [backend, 0],
1711 # [dev,
1712 # [devid, 1], [p-devname, sdg], [p-dev, 2:0:0:0],
1713 # [v-dev, 1:0:0:0], [state, 1]
1714 # ],
1715 # [dev,
1716 # [devid, 1], [p-devname, sdh], [p-dev, 2:0:0:1],
1717 # [v-dev, 1:0:0:1], [satet, 1]
1718 # ]
1719 # ]
1720 # ]
1722 # It seems the reasoning for this difference is because
1723 # vscsiif.py needs all the SCSI device configurations with
1724 # same host number at the same time when creating the devices.
1726 # For SCSI device hotplug support, the SXP of SCSI devices is
1727 # extendend like this:
1729 # [device,
1730 # [vscsi,
1731 # [feature-host, 0],
1732 # [backend, 0],
1733 # [dev,
1734 # [devid, 0], [p-devname, sdd], [p-dev, 1:0:0:3],
1735 # [v-dev, 0:0:0:2], [state, 1]
1736 # ]
1737 # ]
1738 # ]
1740 # state xenbusState['Initialising'] indicates that the device is
1741 # being attached, while state xenbusState['Closing'] indicates
1742 # that the device is being detached.
1744 # The Dict looks like this:
1746 # { devs: [ {devid: 0, p-devname: sdd, p-dev: 1:0:0:3,
1747 # v-dev: 0:0:0:2, state: 1} ],
1748 # feature-host: 1 , backend: 0 }
1750 dev_config = {}
1752 vscsi_devs = []
1753 for vscsi_dev in sxp.children(dev_sxp, 'dev'):
1754 vscsi_dev_info = {}
1755 for opt_val in vscsi_dev[1:]:
1756 try:
1757 opt, val = opt_val
1758 vscsi_dev_info[opt] = val
1759 except TypeError:
1760 pass
1761 # append uuid for each vscsi device.
1762 vscsi_uuid = vscsi_dev_info.get('uuid', uuid.createString())
1763 vscsi_dev_info['uuid'] = vscsi_uuid
1764 vscsi_devs.append(vscsi_dev_info)
1765 dev_config['devs'] = vscsi_devs
1767 vscsi_mode = sxp.children(dev_sxp, 'feature-host')[0]
1768 dev_config['feature-host'] = vscsi_mode[1]
1769 try:
1770 vscsi_be = sxp.children(dev_sxp, 'backend')[0]
1771 dev_config['backend'] = vscsi_be[1]
1772 except IndexError:
1773 pass
1775 return dev_config
1777 def console_add(self, protocol, location, other_config = {}):
1778 dev_uuid = uuid.createString()
1779 if protocol == 'vt100':
1780 dev_info = {
1781 'uuid': dev_uuid,
1782 'protocol': protocol,
1783 'location': location,
1784 'other_config': other_config,
1787 if 'devices' not in self:
1788 self['devices'] = {}
1790 self['devices'][dev_uuid] = ('console', dev_info)
1791 self['console_refs'].append(dev_uuid)
1792 return dev_info
1794 return {}
1796 def console_update(self, console_uuid, key, value):
1797 for dev_uuid, (dev_type, dev_info) in self['devices'].items():
1798 if dev_uuid == console_uuid:
1799 dev_info[key] = value
1800 # collapse other_config into dev_info for things
1801 # such as vncpasswd, vncunused, etc.
1802 if key == 'other_config':
1803 for k in XENAPI_CONSOLE_OTHER_CFG:
1804 if k in dev_info and k not in value:
1805 del dev_info[k]
1806 dev_info.update(value)
1807 break
1809 def console_get_all(self, protocol):
1810 if protocol == 'vt100':
1811 consoles = [dinfo for dtype, dinfo in self['devices'].values()
1812 if dtype == 'console']
1813 return [c for c in consoles if c.get('protocol') == protocol]
1815 elif protocol == 'rfb':
1816 vfbs = [dinfo for dtype, dinfo in self['devices'].values()
1817 if dtype == 'vfb']
1819 # move all non-console key values to other_config before
1820 # returning console config
1821 valid_keys = ['uuid', 'location']
1822 for vfb in vfbs:
1823 other_config = {}
1824 for key, val in vfb.items():
1825 if key not in valid_keys:
1826 other_config[key] = vfb[key]
1827 del vfb[key]
1828 vfb['other_config'] = other_config
1829 vfb['protocol'] = 'rfb'
1831 return vfbs
1833 else:
1834 return []
1836 def device_update(self, dev_uuid, cfg_sxp = [], cfg_xenapi = {}):
1837 """Update an existing device with the new configuration.
1839 @rtype: boolean
1840 @return: Returns True if succesfully found and updated a device conf
1841 """
1842 if dev_uuid in self['devices'] and cfg_sxp:
1843 if sxp.child0(cfg_sxp) == 'device':
1844 config = sxp.child0(cfg_sxp)
1845 else:
1846 config = cfg_sxp
1848 dev_type, dev_info = self['devices'][dev_uuid]
1850 if dev_type == 'pci': # Special case for pci
1851 pci_dict = self.pci_convert_sxp_to_dict(config)
1852 pci_devs = pci_dict['devs']
1854 # destroy existing XenAPI DPCI objects
1855 for dpci_uuid in XendDPCI.get_by_VM(self['uuid']):
1856 XendAPIStore.deregister(dpci_uuid, "DPCI")
1858 # create XenAPI DPCI objects.
1859 self.create_dpci_from_sxp(pci_devs)
1861 self['devices'][dev_uuid] = (dev_type,
1862 {'devs': pci_devs,
1863 'uuid': dev_uuid})
1864 return True
1866 if dev_type == 'vscsi': # Special case for vscsi
1867 vscsi_dict = self.vscsi_convert_sxp_to_dict(config)
1868 vscsi_devs = vscsi_dict['devs']
1869 vscsi_mode = vscsi_dict['feature-host']
1870 vscsi_be = vscsi_dict.get('backend', None)
1872 # destroy existing XenAPI DSCSI objects
1873 vscsi_devid = int(dev_info['devs'][0]['devid'])
1874 for dscsi_uuid in XendDSCSI.get_by_VM(self['uuid']):
1875 dscsi_inst = XendAPIStore.get(dscsi_uuid, 'DSCSI')
1876 if vscsi_devid == dscsi_inst.get_virtual_host():
1877 XendAPIStore.deregister(dscsi_uuid, "DSCSI")
1879 # create XenAPI DSCSI objects.
1880 for vscsi_dev in vscsi_devs:
1881 dscsi_uuid = vscsi_dev.get('uuid')
1882 pscsi_uuid = XendPSCSI.get_by_HCTL(vscsi_dev['p-dev'])
1883 if pscsi_uuid is None:
1884 continue
1885 dscsi_record = {
1886 'VM': self['uuid'],
1887 'PSCSI': pscsi_uuid,
1888 'virtual_HCTL': vscsi_dev.get('v-dev')
1890 XendDSCSI(dscsi_uuid, dscsi_record)
1892 vscsi_info = {
1893 'devs': vscsi_devs,
1894 'feature-host': vscsi_mode,
1895 'uuid': dev_uuid
1897 if vscsi_be is not None:
1898 vscsi_info['backend'] = vscsi_be
1899 self['devices'][dev_uuid] = (dev_type, vscsi_info)
1900 return True
1902 for opt_val in config[1:]:
1903 try:
1904 opt, val = opt_val
1905 dev_info[opt] = val
1906 except (TypeError, ValueError):
1907 pass # no value for this config option
1909 self['devices'][dev_uuid] = (dev_type, dev_info)
1910 return True
1912 elif dev_uuid in self['devices'] and cfg_xenapi:
1913 dev_type, dev_info = self['devices'][dev_uuid]
1914 for key, val in cfg_xenapi.items():
1915 dev_info[key] = val
1916 self['devices'][dev_uuid] = (dev_type, dev_info)
1917 return True
1919 return False
1922 def device_sxpr(self, dev_uuid = None, dev_type = None, dev_info = None, target = None):
1923 """Get Device SXPR by either giving the device UUID or (type, config).
1925 @rtype: list of lists
1926 @return: device config sxpr
1927 """
1928 sxpr = []
1930 if target == None:
1931 target = self
1933 if dev_uuid != None and dev_uuid in target['devices']:
1934 dev_type, dev_info = target['devices'][dev_uuid]
1936 if dev_type == None or dev_info == None:
1937 raise XendConfigError("Required either UUID or device type and "
1938 "configuration dictionary.")
1940 sxpr.append(dev_type)
1941 if dev_type in ('console', 'vfb'):
1942 config = [(opt, val) for opt, val in dev_info.items()
1943 if opt != 'other_config']
1944 else:
1945 config = [(opt, val) for opt, val in dev_info.items()]
1947 sxpr += config
1949 return sxpr
1951 def ordered_device_refs(self, target = None):
1952 result = []
1954 if target == None:
1955 target = self
1957 # vkbd devices *must* be before vfb devices, otherwise
1958 # there is a race condition when setting up devices
1959 # where the daemon spawned for the vfb may write stuff
1960 # into xenstore vkbd backend, before DevController has
1961 # setup permissions on the vkbd backend path. This race
1962 # results in domain creation failing with 'device already
1963 # connected' messages
1964 result.extend([u for u in target['devices'].keys() if target['devices'][u][0] == 'vkbd'])
1966 result.extend(target.get('console_refs', []) +
1967 target.get('vbd_refs', []) +
1968 target.get('vif_refs', []) +
1969 target.get('vtpm_refs', []))
1971 result.extend([u for u in target['devices'].keys() if u not in result])
1972 return result
1974 # This includes a generic equivalent of pci_opts_list_to_sxp()
1975 def dev_dict_to_sxp(self, dev):
1976 def f((key, val)):
1977 if isinstance(val, types.ListType):
1978 return map(lambda x: [key, x], val)
1979 return [[key, val]]
1980 dev_sxp = ['dev'] + reduce(lambda x, y: x + y, map(f, dev.items()))
1981 return dev_sxp
1983 def all_devices_sxpr(self, target = None):
1984 """Returns the SXPR for all devices in the current configuration."""
1985 sxprs = []
1987 if target == None:
1988 target = self
1990 if 'devices' not in target:
1991 return sxprs
1993 ordered_refs = self.ordered_device_refs(target = target)
1994 for dev_uuid in ordered_refs:
1995 dev_type, dev_info = target['devices'][dev_uuid]
1996 if dev_type == 'pci' or dev_type == 'vscsi': # special case for pci devices
1997 if dev_type == 'pci':
1998 sxpr = ['pci', ['uuid', dev_info['uuid']]]
1999 elif dev_type == 'vscsi':
2000 sxpr = ['vscsi', ['uuid', dev_info['uuid']],
2001 ['feature-host', dev_info['feature-host']]]
2002 if dev_info.has_key('backend'):
2003 sxpr.append(['backend', dev_info['backend']])
2004 for pci_dev_info in dev_info['devs']:
2005 sxpr.append(self.dev_dict_to_sxp(pci_dev_info))
2006 sxprs.append((dev_type, sxpr))
2007 else:
2008 sxpr = self.device_sxpr(dev_type = dev_type,
2009 dev_info = dev_info,
2010 target = target)
2011 sxprs.append((dev_type, sxpr))
2013 return sxprs
2015 def image_sxpr(self):
2016 """Returns a backwards compatible image SXP expression that is
2017 used in xenstore's /vm/<uuid>/image value and xm list."""
2018 image = [self.image_type()]
2019 if self.has_key('PV_kernel'):
2020 image.append(['kernel', self['PV_kernel']])
2021 if self.has_key('PV_ramdisk') and self['PV_ramdisk']:
2022 image.append(['ramdisk', self['PV_ramdisk']])
2023 if self.has_key('PV_args') and self['PV_args']:
2024 image.append(['args', self['PV_args']])
2025 if self.has_key('superpages'):
2026 image.append(['superpages', self['superpages']])
2028 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
2029 if key in self['platform']:
2030 image.append([key, self['platform'][key]])
2032 if 'notes' in self:
2033 image.append(self.notes_sxp(self['notes']))
2035 return image
2037 def update_with_image_sxp(self, image_sxp, bootloader = False):
2038 # Convert Legacy "image" config to Xen API PV_*
2039 # configuration
2040 log.debug("update_with_image_sxp(%s)" % scrub_password(image_sxp))
2042 # user-specified args must come last: previous releases did this and
2043 # some domU kernels rely upon the ordering.
2044 kernel_args = sxp.child_value(image_sxp, 'args', '')
2046 # attempt to extract extra arguments from SXP config
2047 arg_ip = sxp.child_value(image_sxp, 'ip')
2048 if arg_ip and not re.search(r'ip=[^ ]+', kernel_args):
2049 kernel_args = 'ip=%s ' % arg_ip + kernel_args
2050 arg_root = sxp.child_value(image_sxp, 'root')
2051 if arg_root and not re.search(r'root=', kernel_args):
2052 kernel_args = 'root=%s ' % arg_root + kernel_args
2054 if bootloader:
2055 self['_temp_using_bootloader'] = '1'
2056 self['_temp_kernel'] = sxp.child_value(image_sxp, 'kernel','')
2057 self['_temp_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
2058 self['_temp_args'] = kernel_args
2059 else:
2060 self['PV_kernel'] = sxp.child_value(image_sxp, 'kernel','')
2061 self['PV_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
2062 self['PV_args'] = kernel_args
2064 self['superpages'] = sxp.child_value(image_sxp, 'superpages',0)
2066 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
2067 val = sxp.child_value(image_sxp, key, None)
2068 if val is not None and val != '':
2069 self['platform'][key] = val
2071 notes = sxp.children(image_sxp, 'notes')
2072 if notes:
2073 self['notes'] = self.notes_from_sxp(notes[0])
2075 self._hvm_boot_params_from_sxp(image_sxp)
2077 def set_notes(self, notes):
2078 'Add parsed elfnotes to image'
2079 self['notes'] = notes
2081 def get_notes(self):
2082 try:
2083 return self['notes'] or {}
2084 except KeyError:
2085 return {}
2087 def notes_from_sxp(self, nsxp):
2088 notes = {}
2089 for note in sxp.children(nsxp):
2090 notes[note[0]] = note[1]
2091 return notes
2093 def notes_sxp(self, notes):
2094 nsxp = ['notes']
2095 for k, v in notes.iteritems():
2096 nsxp.append([k, str(v)])
2097 return nsxp
2099 def _hvm_boot_params_from_sxp(self, image_sxp):
2100 boot = sxp.child_value(image_sxp, 'boot', None)
2101 if boot is not None:
2102 self['HVM_boot_policy'] = 'BIOS order'
2103 self['HVM_boot_params'] = { 'order' : boot }
2105 def is_hvm(self):
2106 return self['HVM_boot_policy'] != ''
2108 def target(self):
2109 return self['target']
2111 def image_type(self):
2112 stored_type = self['platform'].get('image_type')
2113 return stored_type or (self.is_hvm() and 'hvm' or 'linux')
2115 def is_hap(self):
2116 return self['platform'].get('hap', 0)
2118 def update_platform_pci(self):
2119 if not self.is_hvm():
2120 return
2122 pci = []
2123 for dev_type, dev_info in self.all_devices_sxpr():
2124 if dev_type != 'pci':
2125 continue
2126 for dev in sxp.children(dev_info, 'dev'):
2127 domain = sxp.child_value(dev, 'domain')
2128 bus = sxp.child_value(dev, 'bus')
2129 slot = sxp.child_value(dev, 'slot')
2130 func = sxp.child_value(dev, 'func')
2131 vslot = sxp.child_value(dev, 'vslot')
2132 opts = pci_opts_list_from_sxp(dev)
2133 pci.append([domain, bus, slot, func, vslot, opts])
2134 self['platform']['pci'] = pci