ia64/xen-unstable

view tools/python/xen/xend/XendConfig.py @ 19657:9ff5c79b0ceb

xend: Device duplicate check fix

I've checked the duplicate-check code here and I found that's checked
only in the context of one domain but not cross-domain. The thing is
that we should check tap/vbd device cross-domain not to allow another
guest to use the same disk image in some circumstances to prevent VM's
disk corruption.

The patch included denies disk image addition under those
circumstances:

1. We're adding read-only disk that's already used as write-exclusive

2. We're adding write-shared disk that's already used as
write-exclusive

3. We're adding write-exclusive disk that's already used

4. We're adding read-only disk that's already used as write-shared*
(because of I/O caching issues etc.)

The vif device duplicate check remains the same it was and it's
checked in the context of current domain only so that behaviour has
been preserved.

Signed-off-by: Michal Novotny <minovotn@redhat.com>
author Keir Fraser <keir.fraser@citrix.com>
date Wed May 27 11:28:45 2009 +0100 (2009-05-27)
parents 205b1badbcfd
children e95c4611a0ae
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
22 import XendDomain
24 from xen.xend import sxp
25 from xen.xend import uuid
26 from xen.xend import XendOptions
27 from xen.xend import XendAPIStore
28 from xen.xend.XendPPCI import XendPPCI
29 from xen.xend.XendDPCI import XendDPCI
30 from xen.xend.XendPSCSI import XendPSCSI
31 from xen.xend.XendDSCSI import XendDSCSI
32 from xen.xend.XendError import VmError
33 from xen.xend.XendDevices import XendDevices
34 from xen.xend.PrettyPrint import prettyprintstring
35 from xen.xend.XendConstants import DOM_STATE_HALTED
36 from xen.xend.xenstore.xstransact import xstransact
37 from xen.xend.server.BlktapController import blktap_disk_types
38 from xen.xend.server.netif import randomMAC
39 from xen.util.blkif import blkdev_name_to_number, blkdev_uname_to_file
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 # The bootable flag is never written to the
1105 # store as part of the device config.
1106 dev_uuid = sxp.child_value(config, 'uuid')
1107 dev_type, dev_cfg = self['devices'][dev_uuid]
1108 is_bootable = dev_cfg.get('bootable', 0)
1109 config.append(['bootable', int(is_bootable)])
1110 config.append(['VDI', dev_cfg.get('VDI', '')])
1112 sxpr.append(['device', config])
1114 found = True
1115 except:
1116 log.exception("dumping sxp from device controllers")
1117 pass
1119 # if we didn't find that device, check the existing config
1120 # for a device in the same class
1121 if not found:
1122 for dev_type, dev_info in self.all_devices_sxpr():
1123 if dev_type == cls:
1124 sxpr.append(['device', dev_info])
1126 txn.commit()
1127 except:
1128 txn.abort()
1129 raise
1131 if 'cpuid' in self:
1132 self.cpuid_to_sxp(sxpr, 'cpuid')
1133 if 'cpuid_check' in self:
1134 self.cpuid_to_sxp(sxpr, 'cpuid_check')
1136 log.debug(sxpr)
1138 return sxpr
1140 def _blkdev_name_to_number(self, dev):
1141 if 'ioemu:' in dev:
1142 _, dev = dev.split(':', 1)
1143 try:
1144 dev, _ = dev.split(':', 1)
1145 except ValueError:
1146 pass
1148 try:
1149 devid = int(dev)
1150 except ValueError:
1151 # devid is not a number but a string containing either device
1152 # name (e.g. xvda) or device_type/device_id (e.g. vbd/51728)
1153 dev2 = type(dev) is str and dev.split('/')[-1] or None
1154 if dev2 == None:
1155 log.debug("Could not check the device %s", dev)
1156 return None
1157 try:
1158 devid = int(dev2)
1159 except ValueError:
1160 (xenbus, devid) = blkdev_name_to_number(dev2)
1161 if devid == None:
1162 log.debug("The device %s is not device name", dev2)
1163 return None
1164 return devid
1166 def device_tuple_value_from_dev_info(self, dev_info, key):
1167 for x in dev_info:
1168 if (type(x) != str):
1169 for xx in x:
1170 if (xx[0] == key):
1171 return xx[1]
1172 return None
1174 # This function translates all block device modes (incl. aliases) to
1175 # one common label per each device mode. Those modes can be:
1176 # read-only (ro), write-exclusive (wx) and write-shared (ws)
1177 def block_device_mode_translate(self, mode):
1178 # Device modes can be read-only (ro), write-exclusive (wx) or
1179 # write-shared (ws), otherwise an error is raised
1180 if mode == "w" or mode == "wr":
1181 return "wx"
1182 elif mode == "r" or mode == "ro":
1183 return "ro"
1184 elif mode == "!" or mode == "w!":
1185 return "ws"
1187 # If no mode defined we consider this as write-exclusive
1188 return "wx"
1190 # Detect device duplicates for vbd, tap and vif devices for domain and
1191 # duplicate unames in global context not to destroy virtual block devices
1192 def device_duplicate_check(self, dev_type, dev_info, defined_config, config):
1193 # Enumerate all devices for all domains
1194 allSxprs = []
1195 val = XendDomain.instance().domains.values()
1196 for v in val:
1197 sxpr = v.getDeviceSxprs(dev_type)
1198 for key in sxpr:
1199 try:
1200 index = allSxprs.index(key)
1201 except:
1202 allSxprs.append(key)
1204 # Enumerate devices for current domain
1205 sxpr = self.all_devices_sxpr(target = defined_config)
1207 # For vif interface we won't check cross-domain
1208 if sxpr == None and dev_type == 'vif':
1209 return
1211 # Preset None values to all variables we'll be checking
1212 new_uname = None
1213 uname = None
1214 dev = None
1215 mac = None
1216 mode = None
1218 # Disk device
1219 if dev_type in ['vbd', 'tap']:
1220 for x in config:
1221 if type(x) != str and (x[0] in ['uname', 'dev', 'mode']):
1222 if x[0] == 'uname':
1223 new_uname = x[1]
1224 if x[0] == 'dev':
1225 dev = x[1]
1226 if x[0] == 'mode':
1227 mode = x[1]
1229 # If we don't have uname entry (happens in virt-manager) return
1230 if new_uname == None:
1231 return
1233 new_uname = new_uname.split(":")[len(new_uname.split(":"))-1]
1234 # We need to allow when uname is zero length, eg. hdc:cdrom device
1235 if len(new_uname) == 0:
1236 log.debug("Null uname when attaching disk device, allowing %s..."
1237 % dev)
1238 return
1240 log.debug("Checking for duplicate for uname: %s, dev: %s, mode: %s"
1241 % (new_uname, dev, mode))
1242 # No device in dev found
1243 if dev == None:
1244 return
1246 devid = self._blkdev_name_to_number(dev)
1247 if devid == None:
1248 return
1250 for o_dev_info in sxpr:
1251 # Get information only for tap/vbd block devices
1252 if o_dev_info[0] in ['tap', 'vbd']:
1253 uname = self.device_tuple_value_from_dev_info(o_dev_info, "uname")
1254 dev = self.device_tuple_value_from_dev_info(o_dev_info, "dev")
1255 dev_uname = None
1256 if uname != None:
1257 dev_uname = uname.split(":")[len(uname.split(":"))-1]
1258 if new_uname == dev_uname:
1259 raise XendConfigError('The uname "%s" is already defined' %
1260 dev_uname)
1262 blkdev = dev.split(":")[0]
1263 blkdevid = self._blkdev_name_to_number(blkdev)
1264 if blkdevid != None and devid == blkdevid:
1265 raise XendConfigError('The device "%s" is already defined' %
1266 blkdev)
1268 tMode = self.block_device_mode_translate(mode)
1270 # Device/uname not found in the context of current domain but we
1271 # need to have a look to global context. We deny addition of device
1272 # in those cases:
1273 # 1. We're adding read-only disk that's already used as write-exclusive
1274 # 2. We're adding write-shared disk that's already used as write-exclusive
1275 # 3. We're adding write-exclusive disk that's already used
1276 # 4. We're adding read-only disk that's already used as write-shared
1277 for o_dev_info in allSxprs:
1278 backend = self.device_tuple_value_from_dev_info(o_dev_info, "backend")
1279 params = xstransact.Read(backend, "params")
1280 aMode = self.block_device_mode_translate(
1281 xstransact.Read(backend, "mode") )
1282 dev_uname = params.split(":")[len(params.split(":"))-1]
1283 if new_uname == dev_uname:
1284 if ((tMode == "ro" and aMode == "wx")
1285 or (tMode == "ws" and aMode == "wx")
1286 or (tMode == "ro" and aMode == "ws")
1287 or (tMode == "wx")):
1288 raise XendConfigError('The uname "%s" is already used by another domain' %
1289 dev_uname)
1291 # Virtual network adapter
1292 elif dev_type == 'vif':
1293 dev_mac = dev_info.get('mac')
1295 for o_dev_type, o_dev_info in sxpr:
1296 if dev_type == o_dev_type:
1297 if dev_mac.lower() == sxp.child_value(o_dev_info, 'mac').lower():
1298 raise XendConfigError('The mac "%s" is already defined' %
1299 dev_mac)
1301 return None
1303 def device_add(self, dev_type, cfg_sxp = None, cfg_xenapi = None,
1304 target = None):
1305 """Add a device configuration in SXP format or XenAPI struct format.
1307 For SXP, it could be either:
1309 [device, [vbd, [uname ...]]
1311 or:
1313 [vbd, [uname ..]]
1315 @type cfg_sxp: list of lists (parsed sxp object)
1316 @param cfg_sxp: SXP configuration object
1317 @type cfg_xenapi: dict
1318 @param cfg_xenapi: A device configuration from Xen API (eg. vbd,vif)
1319 @param target: write device information to
1320 @type target: None or a dictionary
1321 @rtype: string
1322 @return: Assigned UUID of the device.
1323 """
1324 if target == None:
1325 target = self
1327 if dev_type not in XendDevices.valid_devices():
1328 raise XendConfigError("XendConfig: %s not a valid device type" %
1329 dev_type)
1331 if cfg_sxp == None and cfg_xenapi == None:
1332 raise XendConfigError("XendConfig: device_add requires some "
1333 "config.")
1335 #if cfg_sxp:
1336 # log.debug("XendConfig.device_add: %s" % str(cfg_sxp))
1337 #if cfg_xenapi:
1338 # log.debug("XendConfig.device_add: %s" % str(cfg_xenapi))
1340 if cfg_sxp:
1341 if sxp.child0(cfg_sxp) == 'device':
1342 config = sxp.child0(cfg_sxp)
1343 else:
1344 config = cfg_sxp
1346 dev_type = sxp.name(config)
1347 dev_info = {}
1349 if dev_type == 'pci':
1350 pci_devs_uuid = sxp.child_value(config, 'uuid',
1351 uuid.createString())
1353 pci_dict = self.pci_convert_sxp_to_dict(config)
1354 pci_devs = pci_dict['devs']
1356 # create XenAPI DPCI objects.
1357 for pci_dev in pci_devs:
1358 dpci_uuid = pci_dev.get('uuid')
1359 ppci_uuid = XendPPCI.get_by_sbdf(pci_dev['domain'],
1360 pci_dev['bus'],
1361 pci_dev['slot'],
1362 pci_dev['func'])
1363 if ppci_uuid is None:
1364 continue
1365 dpci_record = {
1366 'VM': self['uuid'],
1367 'PPCI': ppci_uuid,
1368 'hotplug_slot': pci_dev.get('requested_vslot', 0)
1371 dpci_opts = pci_dev.get('opts')
1372 if dpci_opts and len(dpci_opts) > 0:
1373 dpci_record['options'] = dpci_opts
1375 XendDPCI(dpci_uuid, dpci_record)
1377 target['devices'][pci_devs_uuid] = (dev_type,
1378 {'devs': pci_devs,
1379 'uuid': pci_devs_uuid})
1381 log.debug("XendConfig: reading device: %s" % pci_devs)
1383 return pci_devs_uuid
1385 if dev_type == 'vscsi':
1386 vscsi_devs_uuid = sxp.child_value(config, 'uuid',
1387 uuid.createString())
1388 vscsi_dict = self.vscsi_convert_sxp_to_dict(config)
1389 vscsi_devs = vscsi_dict['devs']
1390 vscsi_mode = vscsi_dict['feature-host']
1391 vscsi_be = vscsi_dict.get('backend', None)
1393 # create XenAPI DSCSI objects.
1394 for vscsi_dev in vscsi_devs:
1395 dscsi_uuid = vscsi_dev.get('uuid')
1396 pscsi_uuid = XendPSCSI.get_by_HCTL(vscsi_dev['p-dev'])
1397 if pscsi_uuid is None:
1398 continue
1399 dscsi_record = {
1400 'VM': self['uuid'],
1401 'PSCSI': pscsi_uuid,
1402 'virtual_HCTL': vscsi_dev.get('v-dev')
1404 XendDSCSI(dscsi_uuid, dscsi_record)
1406 vscsi_info = {
1407 'devs': vscsi_devs,
1408 'feature-host': vscsi_mode,
1409 'uuid': vscsi_devs_uuid
1411 if vscsi_be is not None:
1412 vscsi_info['backend'] = vscsi_be
1413 target['devices'][vscsi_devs_uuid] = (dev_type, vscsi_info)
1414 log.debug("XendConfig: reading device: %s,%s" % \
1415 (vscsi_devs, vscsi_mode))
1416 return vscsi_devs_uuid
1418 for opt_val in config[1:]:
1419 try:
1420 opt, val = opt_val
1421 dev_info[opt] = val
1422 except (TypeError, ValueError): # unpack error
1423 pass
1425 if dev_type == 'vbd':
1426 if dev_info.get('dev', '').startswith('ioemu:'):
1427 dev_info['driver'] = 'ioemu'
1428 else:
1429 dev_info['driver'] = 'paravirtualised'
1431 if dev_type == 'tap':
1432 if dev_info['uname'].split(':')[1] not in blktap_disk_types:
1433 raise XendConfigError("tap:%s not a valid disk type" %
1434 dev_info['uname'].split(':')[1])
1436 if dev_type == 'vif':
1437 if not dev_info.get('mac'):
1438 dev_info['mac'] = randomMAC()
1440 ret_uuid = self.device_duplicate_check(dev_type, dev_info, target, config)
1441 if ret_uuid != None:
1442 return ret_uuid
1444 if dev_type == 'vif':
1445 if dev_info.get('policy') and dev_info.get('label'):
1446 dev_info['security_label'] = "%s:%s:%s" % \
1447 (xsconstants.ACM_POLICY_ID,
1448 dev_info['policy'],dev_info['label'])
1450 # create uuid if it doesn't exist
1451 dev_uuid = dev_info.get('uuid', None)
1452 if not dev_uuid:
1453 dev_uuid = uuid.createString()
1454 dev_info['uuid'] = dev_uuid
1456 # store dev references by uuid for certain device types
1457 target['devices'][dev_uuid] = (dev_type, dev_info)
1458 if dev_type in ('vif', 'vbd', 'vtpm'):
1459 param = '%s_refs' % dev_type
1460 if param not in target:
1461 target[param] = []
1462 if dev_uuid not in target[param]:
1463 if dev_type == 'vbd' and 'bootable' not in dev_info:
1464 # Compat hack -- mark first disk bootable
1465 dev_info['bootable'] = int(not target[param])
1466 target[param].append(dev_uuid)
1467 elif dev_type == 'tap':
1468 if 'vbd_refs' not in target:
1469 target['vbd_refs'] = []
1470 if dev_uuid not in target['vbd_refs']:
1471 if 'bootable' not in dev_info:
1472 # Compat hack -- mark first disk bootable
1473 dev_info['bootable'] = int(not target['vbd_refs'])
1474 target['vbd_refs'].append(dev_uuid)
1476 elif dev_type == 'vfb':
1477 # Populate other config with aux data that is associated
1478 # with vfb
1480 other_config = {}
1481 for key in XENAPI_CONSOLE_OTHER_CFG:
1482 if key in dev_info:
1483 other_config[key] = dev_info[key]
1484 target['devices'][dev_uuid][1]['other_config'] = other_config
1487 if 'console_refs' not in target:
1488 target['console_refs'] = []
1490 # Treat VFB devices as console devices so they are found
1491 # through Xen API
1492 if dev_uuid not in target['console_refs']:
1493 target['console_refs'].append(dev_uuid)
1495 # Cope with old-format save files which say under vfb
1496 # (type vfb) rather than (vfb 1)
1497 try:
1498 vfb_type = dev_info['type']
1499 except KeyError:
1500 vfb_type = None
1501 log.debug("iwj dev_type=%s vfb type %s" %
1502 (dev_type, `vfb_type`))
1504 if vfb_type == 'vnc' or vfb_type == 'sdl':
1505 dev_info[vfb_type] = 1
1506 del dev_info['type']
1507 log.debug("iwj dev_type=%s vfb setting dev_info['%s']" %
1508 (dev_type, vfb_type))
1510 elif dev_type == 'console':
1511 if 'console_refs' not in target:
1512 target['console_refs'] = []
1513 if dev_uuid not in target['console_refs']:
1514 target['console_refs'].append(dev_uuid)
1516 log.debug("XendConfig: reading device: %s" % scrub_password(dev_info))
1517 return dev_uuid
1519 if cfg_xenapi:
1520 dev_info = {}
1521 dev_uuid = ''
1522 if dev_type == 'vif':
1523 dev_info['mac'] = cfg_xenapi.get('MAC')
1524 if not dev_info['mac']:
1525 dev_info['mac'] = randomMAC()
1526 # vifname is the name on the guest, not dom0
1527 # TODO: we don't have the ability to find that out or
1528 # change it from dom0
1529 #if cfg_xenapi.get('device'): # don't add if blank
1530 # dev_info['vifname'] = cfg_xenapi.get('device')
1531 if cfg_xenapi.get('type'):
1532 dev_info['type'] = cfg_xenapi.get('type')
1533 if cfg_xenapi.get('name'):
1534 dev_info['name'] = cfg_xenapi.get('name')
1535 if cfg_xenapi.get('network'):
1536 network = XendAPIStore.get(
1537 cfg_xenapi.get('network'), 'network')
1538 dev_info['bridge'] = network.get_name_label()
1540 if cfg_xenapi.get('security_label'):
1541 dev_info['security_label'] = \
1542 cfg_xenapi.get('security_label')
1544 dev_uuid = cfg_xenapi.get('uuid', None)
1545 if not dev_uuid:
1546 dev_uuid = uuid.createString()
1547 dev_info['uuid'] = dev_uuid
1548 target['devices'][dev_uuid] = (dev_type, dev_info)
1549 target['vif_refs'].append(dev_uuid)
1551 elif dev_type in ('vbd', 'tap'):
1552 dev_info['type'] = cfg_xenapi.get('type', 'Disk')
1553 if dev_info['type'] == 'CD':
1554 old_vbd_type = 'cdrom'
1555 else:
1556 old_vbd_type = 'disk'
1558 dev_info['uname'] = cfg_xenapi.get('image', '')
1559 dev_info['dev'] = '%s:%s' % (cfg_xenapi.get('device'),
1560 old_vbd_type)
1561 dev_info['bootable'] = int(cfg_xenapi.get('bootable', 0))
1562 dev_info['driver'] = cfg_xenapi.get('driver', '')
1563 dev_info['VDI'] = cfg_xenapi.get('VDI', '')
1565 if cfg_xenapi.get('mode') == 'RW':
1566 dev_info['mode'] = 'w'
1567 else:
1568 dev_info['mode'] = 'r'
1570 dev_uuid = cfg_xenapi.get('uuid', None)
1571 if not dev_uuid:
1572 dev_uuid = uuid.createString()
1573 dev_info['uuid'] = dev_uuid
1574 target['devices'][dev_uuid] = (dev_type, dev_info)
1575 target['vbd_refs'].append(dev_uuid)
1577 elif dev_type == 'vtpm':
1578 if cfg_xenapi.get('type'):
1579 dev_info['type'] = cfg_xenapi.get('type')
1581 dev_uuid = cfg_xenapi.get('uuid', None)
1582 if not dev_uuid:
1583 dev_uuid = uuid.createString()
1584 dev_info['uuid'] = dev_uuid
1585 dev_info['other_config'] = cfg_xenapi.get('other_config', {})
1586 target['devices'][dev_uuid] = (dev_type, dev_info)
1587 target['vtpm_refs'].append(dev_uuid)
1589 elif dev_type == 'console':
1590 dev_uuid = cfg_xenapi.get('uuid', None)
1591 if not dev_uuid:
1592 dev_uuid = uuid.createString()
1593 dev_info['uuid'] = dev_uuid
1594 dev_info['protocol'] = cfg_xenapi.get('protocol', 'rfb')
1595 console_other_config = cfg_xenapi.get('other_config', {})
1596 dev_info['other_config'] = console_other_config
1597 if dev_info['protocol'] == 'rfb':
1598 # collapse other config into devinfo for things
1599 # such as vncpasswd, vncunused, etc.
1600 dev_info.update(console_other_config)
1601 dev_info['vnc'] = console_other_config.get('vnc', '0')
1602 dev_info['sdl'] = console_other_config.get('sdl', '0')
1603 target['devices'][dev_uuid] = ('vfb', dev_info)
1604 target['console_refs'].append(dev_uuid)
1606 # if console is rfb, set device_model ensuring qemu
1607 # is invoked for pvfb services
1608 if 'device_model' not in target['platform']:
1609 target['platform']['device_model'] = \
1610 xen.util.auxbin.pathTo("qemu-dm")
1612 # Finally, if we are a pvfb, we need to make a vkbd
1613 # as well that is not really exposed to Xen API
1614 vkbd_uuid = uuid.createString()
1615 target['devices'][vkbd_uuid] = ('vkbd', {})
1617 elif dev_info['protocol'] == 'vt100':
1618 # if someone tries to create a VT100 console
1619 # via the Xen API, we'll have to ignore it
1620 # because we create one automatically in
1621 # XendDomainInfo._update_consoles
1622 raise XendConfigError('Creating vt100 consoles via '
1623 'Xen API is unsupported')
1625 return dev_uuid
1627 # no valid device to add
1628 return ''
1630 def phantom_device_add(self, dev_type, cfg_xenapi = None,
1631 target = None):
1632 """Add a phantom tap device configuration in XenAPI struct format.
1633 """
1635 if target == None:
1636 target = self
1638 if dev_type not in XendDevices.valid_devices() and \
1639 dev_type not in XendDevices.pseudo_devices():
1640 raise XendConfigError("XendConfig: %s not a valid device type" %
1641 dev_type)
1643 if cfg_xenapi == None:
1644 raise XendConfigError("XendConfig: device_add requires some "
1645 "config.")
1647 if cfg_xenapi:
1648 log.debug("XendConfig.phantom_device_add: %s" % str(cfg_xenapi))
1650 if cfg_xenapi:
1651 dev_info = {}
1652 if dev_type in ('vbd', 'tap'):
1653 if dev_type == 'vbd':
1654 dev_info['uname'] = cfg_xenapi.get('image', '')
1655 dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
1656 elif dev_type == 'tap':
1657 if cfg_xenapi.get('image').find('tap:') == -1:
1658 dev_info['uname'] = 'tap:qcow:%s' % cfg_xenapi.get('image')
1659 dev_info['dev'] = '/dev/%s' % cfg_xenapi.get('device')
1660 dev_info['uname'] = cfg_xenapi.get('image')
1661 dev_info['mode'] = cfg_xenapi.get('mode')
1662 dev_info['backend'] = '0'
1663 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
1664 dev_info['uuid'] = dev_uuid
1665 self['devices'][dev_uuid] = (dev_type, dev_info)
1666 self['vbd_refs'].append(dev_uuid)
1667 return dev_uuid
1669 return ''
1671 def pci_convert_sxp_to_dict(self, dev_sxp):
1672 """Convert pci device sxp to dict
1673 @param dev_sxp: device configuration
1674 @type dev_sxp: SXP object (parsed config)
1675 @return: dev_config
1676 @rtype: dictionary
1677 """
1678 # Parsing the device SXP's. In most cases, the SXP looks
1679 # like this:
1681 # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]]
1683 # However, for PCI devices it looks like this:
1685 # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1], [func, 2]]]
1687 # It seems the reasoning for this difference is because
1688 # pciif.py needs all the PCI device configurations at
1689 # the same time when creating the devices.
1691 # To further complicate matters, Xen 2.0 configuration format
1692 # uses the following for pci device configuration:
1694 # [device, [pci, [domain, 0], [bus, 0], [dev, 1], [func, 2]]]
1696 # For PCI device hotplug support, the SXP of PCI devices is
1697 # extendend like this:
1699 # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1], [func, 2],
1700 # [vslot, 0]],
1701 # [state, 'Initialising']]]
1703 # 'vslot' shows the virtual hotplug slot number which the PCI device
1704 # is inserted in. This is only effective for HVM domains.
1706 # state 'Initialising' indicates that the device is being attached,
1707 # while state 'Closing' indicates that the device is being detached.
1709 # The Dict looks like this:
1711 # { devs: [{domain: 0, bus: 0, slot: 1, func: 2, vslot: 0}],
1712 # states: ['Initialising'] }
1714 dev_config = {}
1716 pci_devs = []
1717 for pci_dev in sxp.children(dev_sxp, 'dev'):
1718 pci_dev_info = {}
1719 for opt_val in pci_dev[1:]:
1720 try:
1721 opt, val = opt_val
1722 pci_dev_info[opt] = val
1723 except (TypeError, ValueError):
1724 pass
1725 # append uuid for each pci device.
1726 dpci_uuid = pci_dev_info.get('uuid', uuid.createString())
1727 pci_dev_info['uuid'] = dpci_uuid
1728 pci_devs.append(pci_dev_info)
1729 dev_config['devs'] = pci_devs
1731 pci_states = []
1732 for pci_state in sxp.children(dev_sxp, 'state'):
1733 try:
1734 pci_states.append(pci_state[1])
1735 except IndexError:
1736 raise XendError("Error reading state while parsing pci sxp")
1737 dev_config['states'] = pci_states
1739 return dev_config
1741 def vscsi_convert_sxp_to_dict(self, dev_sxp):
1742 """Convert vscsi device sxp to dict
1743 @param dev_sxp: device configuration
1744 @type dev_sxp: SXP object (parsed config)
1745 @return: dev_config
1746 @rtype: dictionary
1747 """
1748 # Parsing the device SXP's. In most cases, the SXP looks
1749 # like this:
1751 # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]]
1753 # However, for SCSI devices it looks like this:
1755 # [device,
1756 # [vscsi,
1757 # [feature-host, 0],
1758 # [backend, 0],
1759 # [dev,
1760 # [devid, 0], [p-devname, sdb], [p-dev, 1:0:0:1],
1761 # [v-dev, 0:0:0:0], [state, 1]
1762 # ],
1763 # [dev,
1764 # [devid, 0], [p-devname, sdc], [p-dev, 1:0:0:2],
1765 # [v-dev, 0:0:0:1], [satet, 1]
1766 # ]
1767 # ],
1768 # [vscsi,
1769 # [feature-host, 1],
1770 # [backend, 0],
1771 # [dev,
1772 # [devid, 1], [p-devname, sdg], [p-dev, 2:0:0:0],
1773 # [v-dev, 1:0:0:0], [state, 1]
1774 # ],
1775 # [dev,
1776 # [devid, 1], [p-devname, sdh], [p-dev, 2:0:0:1],
1777 # [v-dev, 1:0:0:1], [satet, 1]
1778 # ]
1779 # ]
1780 # ]
1782 # It seems the reasoning for this difference is because
1783 # vscsiif.py needs all the SCSI device configurations with
1784 # same host number at the same time when creating the devices.
1786 # For SCSI device hotplug support, the SXP of SCSI devices is
1787 # extendend like this:
1789 # [device,
1790 # [vscsi,
1791 # [feature-host, 0],
1792 # [backend, 0],
1793 # [dev,
1794 # [devid, 0], [p-devname, sdd], [p-dev, 1:0:0:3],
1795 # [v-dev, 0:0:0:2], [state, 1]
1796 # ]
1797 # ]
1798 # ]
1800 # state xenbusState['Initialising'] indicates that the device is
1801 # being attached, while state xenbusState['Closing'] indicates
1802 # that the device is being detached.
1804 # The Dict looks like this:
1806 # { devs: [ {devid: 0, p-devname: sdd, p-dev: 1:0:0:3,
1807 # v-dev: 0:0:0:2, state: 1} ],
1808 # feature-host: 1 , backend: 0 }
1810 dev_config = {}
1812 vscsi_devs = []
1813 for vscsi_dev in sxp.children(dev_sxp, 'dev'):
1814 vscsi_dev_info = {}
1815 for opt_val in vscsi_dev[1:]:
1816 try:
1817 opt, val = opt_val
1818 vscsi_dev_info[opt] = val
1819 except TypeError:
1820 pass
1821 # append uuid for each vscsi device.
1822 vscsi_uuid = vscsi_dev_info.get('uuid', uuid.createString())
1823 vscsi_dev_info['uuid'] = vscsi_uuid
1824 vscsi_devs.append(vscsi_dev_info)
1825 dev_config['devs'] = vscsi_devs
1827 vscsi_mode = sxp.children(dev_sxp, 'feature-host')[0]
1828 dev_config['feature-host'] = vscsi_mode[1]
1829 try:
1830 vscsi_be = sxp.children(dev_sxp, 'backend')[0]
1831 dev_config['backend'] = vscsi_be[1]
1832 except IndexError:
1833 pass
1835 return dev_config
1837 def console_add(self, protocol, location, other_config = {}):
1838 dev_uuid = uuid.createString()
1839 if protocol == 'vt100':
1840 dev_info = {
1841 'uuid': dev_uuid,
1842 'protocol': protocol,
1843 'location': location,
1844 'other_config': other_config,
1847 if 'devices' not in self:
1848 self['devices'] = {}
1850 self['devices'][dev_uuid] = ('console', dev_info)
1851 self['console_refs'].append(dev_uuid)
1852 return dev_info
1854 return {}
1856 def console_update(self, console_uuid, key, value):
1857 for dev_uuid, (dev_type, dev_info) in self['devices'].items():
1858 if dev_uuid == console_uuid:
1859 dev_info[key] = value
1860 # collapse other_config into dev_info for things
1861 # such as vncpasswd, vncunused, etc.
1862 if key == 'other_config':
1863 for k in XENAPI_CONSOLE_OTHER_CFG:
1864 if k in dev_info and k not in value:
1865 del dev_info[k]
1866 dev_info.update(value)
1867 break
1869 def console_get_all(self, protocol):
1870 if protocol == 'vt100':
1871 consoles = [dinfo for dtype, dinfo in self['devices'].values()
1872 if dtype == 'console']
1873 return [c for c in consoles if c.get('protocol') == protocol]
1875 elif protocol == 'rfb':
1876 vfbs = [dinfo for dtype, dinfo in self['devices'].values()
1877 if dtype == 'vfb']
1879 # move all non-console key values to other_config before
1880 # returning console config
1881 valid_keys = ['uuid', 'location']
1882 for vfb in vfbs:
1883 other_config = {}
1884 for key, val in vfb.items():
1885 if key not in valid_keys:
1886 other_config[key] = vfb[key]
1887 del vfb[key]
1888 vfb['other_config'] = other_config
1889 vfb['protocol'] = 'rfb'
1891 return vfbs
1893 else:
1894 return []
1896 def device_update(self, dev_uuid, cfg_sxp = [], cfg_xenapi = {}):
1897 """Update an existing device with the new configuration.
1899 @rtype: boolean
1900 @return: Returns True if succesfully found and updated a device conf
1901 """
1902 if dev_uuid in self['devices'] and cfg_sxp:
1903 if sxp.child0(cfg_sxp) == 'device':
1904 config = sxp.child0(cfg_sxp)
1905 else:
1906 config = cfg_sxp
1908 dev_type, dev_info = self['devices'][dev_uuid]
1910 if dev_type == 'pci': # Special case for pci
1911 pci_dict = self.pci_convert_sxp_to_dict(config)
1912 pci_devs = pci_dict['devs']
1914 # destroy existing XenAPI DPCI objects
1915 for dpci_uuid in XendDPCI.get_by_VM(self['uuid']):
1916 XendAPIStore.deregister(dpci_uuid, "DPCI")
1918 # create XenAPI DPCI objects.
1919 for pci_dev in pci_devs:
1920 dpci_uuid = pci_dev.get('uuid')
1921 ppci_uuid = XendPPCI.get_by_sbdf(pci_dev['domain'],
1922 pci_dev['bus'],
1923 pci_dev['slot'],
1924 pci_dev['func'])
1925 if ppci_uuid is None:
1926 continue
1927 dpci_record = {
1928 'VM': self['uuid'],
1929 'PPCI': ppci_uuid,
1930 'hotplug_slot': pci_dev.get('requested_vslot', 0)
1933 dpci_opts = pci_dev.get('opts')
1934 if dpci_opts and len(dpci_opts) > 0:
1935 dpci_record['options'] = dpci_opts
1937 XendDPCI(dpci_uuid, dpci_record)
1939 self['devices'][dev_uuid] = (dev_type,
1940 {'devs': pci_devs,
1941 'uuid': dev_uuid})
1942 return True
1944 if dev_type == 'vscsi': # Special case for vscsi
1945 vscsi_dict = self.vscsi_convert_sxp_to_dict(config)
1946 vscsi_devs = vscsi_dict['devs']
1947 vscsi_mode = vscsi_dict['feature-host']
1948 vscsi_be = vscsi_dict.get('backend', None)
1950 # destroy existing XenAPI DSCSI objects
1951 vscsi_devid = int(dev_info['devs'][0]['devid'])
1952 for dscsi_uuid in XendDSCSI.get_by_VM(self['uuid']):
1953 dscsi_inst = XendAPIStore.get(dscsi_uuid, 'DSCSI')
1954 if vscsi_devid == dscsi_inst.get_virtual_host():
1955 XendAPIStore.deregister(dscsi_uuid, "DSCSI")
1957 # create XenAPI DSCSI objects.
1958 for vscsi_dev in vscsi_devs:
1959 dscsi_uuid = vscsi_dev.get('uuid')
1960 pscsi_uuid = XendPSCSI.get_by_HCTL(vscsi_dev['p-dev'])
1961 if pscsi_uuid is None:
1962 continue
1963 dscsi_record = {
1964 'VM': self['uuid'],
1965 'PSCSI': pscsi_uuid,
1966 'virtual_HCTL': vscsi_dev.get('v-dev')
1968 XendDSCSI(dscsi_uuid, dscsi_record)
1970 vscsi_info = {
1971 'devs': vscsi_devs,
1972 'feature-host': vscsi_mode,
1973 'uuid': dev_uuid
1975 if vscsi_be is not None:
1976 vscsi_info['backend'] = vscsi_be
1977 self['devices'][dev_uuid] = (dev_type, vscsi_info)
1978 return True
1980 for opt_val in config[1:]:
1981 try:
1982 opt, val = opt_val
1983 dev_info[opt] = val
1984 except (TypeError, ValueError):
1985 pass # no value for this config option
1987 self['devices'][dev_uuid] = (dev_type, dev_info)
1988 return True
1990 elif dev_uuid in self['devices'] and cfg_xenapi:
1991 dev_type, dev_info = self['devices'][dev_uuid]
1992 for key, val in cfg_xenapi.items():
1993 dev_info[key] = val
1994 self['devices'][dev_uuid] = (dev_type, dev_info)
1995 return True
1997 return False
2000 def device_sxpr(self, dev_uuid = None, dev_type = None, dev_info = None, target = None):
2001 """Get Device SXPR by either giving the device UUID or (type, config).
2003 @rtype: list of lists
2004 @return: device config sxpr
2005 """
2006 sxpr = []
2008 if target == None:
2009 target = self
2011 if dev_uuid != None and dev_uuid in target['devices']:
2012 dev_type, dev_info = target['devices'][dev_uuid]
2014 if dev_type == None or dev_info == None:
2015 raise XendConfigError("Required either UUID or device type and "
2016 "configuration dictionary.")
2018 sxpr.append(dev_type)
2019 if dev_type in ('console', 'vfb'):
2020 config = [(opt, val) for opt, val in dev_info.items()
2021 if opt != 'other_config']
2022 else:
2023 config = [(opt, val) for opt, val in dev_info.items()]
2025 sxpr += config
2027 return sxpr
2029 def ordered_device_refs(self, target = None):
2030 result = []
2032 if target == None:
2033 target = self
2035 # vkbd devices *must* be before vfb devices, otherwise
2036 # there is a race condition when setting up devices
2037 # where the daemon spawned for the vfb may write stuff
2038 # into xenstore vkbd backend, before DevController has
2039 # setup permissions on the vkbd backend path. This race
2040 # results in domain creation failing with 'device already
2041 # connected' messages
2042 result.extend([u for u in target['devices'].keys() if target['devices'][u][0] == 'vkbd'])
2044 result.extend(target.get('console_refs', []) +
2045 target.get('vbd_refs', []) +
2046 target.get('vif_refs', []) +
2047 target.get('vtpm_refs', []))
2049 result.extend([u for u in target['devices'].keys() if u not in result])
2050 return result
2052 def all_devices_sxpr(self, target = None):
2053 """Returns the SXPR for all devices in the current configuration."""
2054 sxprs = []
2056 if target == None:
2057 target = self
2059 if 'devices' not in target:
2060 return sxprs
2062 ordered_refs = self.ordered_device_refs(target = target)
2063 for dev_uuid in ordered_refs:
2064 dev_type, dev_info = target['devices'][dev_uuid]
2065 if dev_type == 'pci' or dev_type == 'vscsi': # special case for pci devices
2066 if dev_type == 'pci':
2067 sxpr = ['pci', ['uuid', dev_info['uuid']]]
2068 elif dev_type == 'vscsi':
2069 sxpr = ['vscsi', ['uuid', dev_info['uuid']],
2070 ['feature-host', dev_info['feature-host']]]
2071 if dev_info.has_key('backend'):
2072 sxpr.append(['backend', dev_info['backend']])
2073 for pci_dev_info in dev_info['devs']:
2074 pci_dev_sxpr = ['dev']
2075 for opt, val in pci_dev_info.items():
2076 pci_dev_sxpr.append([opt, val])
2077 sxpr.append(pci_dev_sxpr)
2078 sxprs.append((dev_type, sxpr))
2079 else:
2080 sxpr = self.device_sxpr(dev_type = dev_type,
2081 dev_info = dev_info,
2082 target = target)
2083 sxprs.append((dev_type, sxpr))
2085 return sxprs
2087 def image_sxpr(self):
2088 """Returns a backwards compatible image SXP expression that is
2089 used in xenstore's /vm/<uuid>/image value and xm list."""
2090 image = [self.image_type()]
2091 if self.has_key('PV_kernel'):
2092 image.append(['kernel', self['PV_kernel']])
2093 if self.has_key('PV_ramdisk') and self['PV_ramdisk']:
2094 image.append(['ramdisk', self['PV_ramdisk']])
2095 if self.has_key('PV_args') and self['PV_args']:
2096 image.append(['args', self['PV_args']])
2097 if self.has_key('superpages'):
2098 image.append(['superpages', self['superpages']])
2100 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
2101 if key in self['platform']:
2102 image.append([key, self['platform'][key]])
2104 if 'notes' in self:
2105 image.append(self.notes_sxp(self['notes']))
2107 return image
2109 def update_with_image_sxp(self, image_sxp, bootloader = False):
2110 # Convert Legacy "image" config to Xen API PV_*
2111 # configuration
2112 log.debug("update_with_image_sxp(%s)" % scrub_password(image_sxp))
2114 # user-specified args must come last: previous releases did this and
2115 # some domU kernels rely upon the ordering.
2116 kernel_args = sxp.child_value(image_sxp, 'args', '')
2118 # attempt to extract extra arguments from SXP config
2119 arg_ip = sxp.child_value(image_sxp, 'ip')
2120 if arg_ip and not re.search(r'ip=[^ ]+', kernel_args):
2121 kernel_args = 'ip=%s ' % arg_ip + kernel_args
2122 arg_root = sxp.child_value(image_sxp, 'root')
2123 if arg_root and not re.search(r'root=', kernel_args):
2124 kernel_args = 'root=%s ' % arg_root + kernel_args
2126 if bootloader:
2127 self['_temp_using_bootloader'] = '1'
2128 self['_temp_kernel'] = sxp.child_value(image_sxp, 'kernel','')
2129 self['_temp_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
2130 self['_temp_args'] = kernel_args
2131 else:
2132 self['PV_kernel'] = sxp.child_value(image_sxp, 'kernel','')
2133 self['PV_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
2134 self['PV_args'] = kernel_args
2136 self['superpages'] = sxp.child_value(image_sxp, 'superpages',0)
2138 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
2139 val = sxp.child_value(image_sxp, key, None)
2140 if val is not None and val != '':
2141 self['platform'][key] = val
2143 notes = sxp.children(image_sxp, 'notes')
2144 if notes:
2145 self['notes'] = self.notes_from_sxp(notes[0])
2147 self._hvm_boot_params_from_sxp(image_sxp)
2149 def set_notes(self, notes):
2150 'Add parsed elfnotes to image'
2151 self['notes'] = notes
2153 def get_notes(self):
2154 try:
2155 return self['notes'] or {}
2156 except KeyError:
2157 return {}
2159 def notes_from_sxp(self, nsxp):
2160 notes = {}
2161 for note in sxp.children(nsxp):
2162 notes[note[0]] = note[1]
2163 return notes
2165 def notes_sxp(self, notes):
2166 nsxp = ['notes']
2167 for k, v in notes.iteritems():
2168 nsxp.append([k, str(v)])
2169 return nsxp
2171 def _hvm_boot_params_from_sxp(self, image_sxp):
2172 boot = sxp.child_value(image_sxp, 'boot', None)
2173 if boot is not None:
2174 self['HVM_boot_policy'] = 'BIOS order'
2175 self['HVM_boot_params'] = { 'order' : boot }
2177 def is_hvm(self):
2178 return self['HVM_boot_policy'] != ''
2180 def target(self):
2181 return self['target']
2183 def image_type(self):
2184 stored_type = self['platform'].get('image_type')
2185 return stored_type or (self.is_hvm() and 'hvm' or 'linux')
2187 def is_hap(self):
2188 return self['platform'].get('hap', 0)