ia64/xen-unstable

view tools/python/xen/xend/XendConfig.py @ 19722:691087b8d4ac

xend: pass-through: Use AUTO_PHP_SLOT as unknown vslot

This fixes a few cases where 0 is still used for an known vslot.

Signed-off-by: Simon Horman <horms@verge.net.au>
author Keir Fraser <keir.fraser@citrix.com>
date Thu Jun 04 10:43:20 2009 +0100 (2009-06-04)
parents 8f64f43624e8
children 376c0749586e
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 import xsconstants
40 import xen.util.auxbin
42 log = logging.getLogger("xend.XendConfig")
43 log.setLevel(logging.WARN)
46 """
47 XendConfig API
49 XendConfig will try to mirror as closely the Xen API VM Struct
50 with extra parameters for those options that are not supported.
52 """
54 def reverse_dict(adict):
55 """Return the reverse mapping of a dictionary."""
56 return dict([(v, k) for k, v in adict.items()])
58 def bool0(v):
59 return v != '0' and v != 'False' and bool(v)
61 # Recursively copy a data struct, scrubbing out VNC passwords.
62 # Will scrub any dict entry with a key of 'vncpasswd' or any
63 # 2-element list whose first member is 'vncpasswd'. It will
64 # also scrub a string matching '(vncpasswd XYZ)'. Everything
65 # else is no-op passthrough
66 def scrub_password(data):
67 if type(data) == dict or type(data) == XendConfig:
68 scrubbed = {}
69 for key in data.keys():
70 if key == "vncpasswd":
71 scrubbed[key] = "XXXXXXXX"
72 else:
73 scrubbed[key] = scrub_password(data[key])
74 return scrubbed
75 elif type(data) == list:
76 if len(data) == 2 and type(data[0]) == str and data[0] == 'vncpasswd':
77 return ['vncpasswd', 'XXXXXXXX']
78 else:
79 scrubbed = []
80 for entry in data:
81 scrubbed.append(scrub_password(entry))
82 return scrubbed
83 elif type(data) == tuple:
84 scrubbed = []
85 for entry in data:
86 scrubbed.append(scrub_password(entry))
87 return tuple(scrubbed)
88 elif type(data) == str:
89 return re.sub(r'\(vncpasswd\s+[^\)]+\)','(vncpasswd XXXXXX)', data)
90 else:
91 return data
93 #
94 # CPU fields:
95 #
96 # VCPUs_max -- the maximum number of vcpus that this domain may ever have.
97 # aka XendDomainInfo.getVCpuCount().
98 # vcpus -- the legacy configuration name for above.
99 # max_vcpu_id -- vcpus_number - 1. This is given to us by Xen.
100 #
101 # cpus -- the list of pCPUs available to each vCPU.
102 #
103 # vcpu_avail -- a bitmap telling the guest domain whether it may use each of
104 # its VCPUs. This is translated to
105 # <dompath>/cpu/<id>/availability = {online,offline} for use
106 # by the guest domain.
107 # VCPUs_live -- the number of VCPUs currently up, as reported by Xen. This
108 # is changed by changing vcpu_avail, and waiting for the
109 # domain to respond.
110 #
113 # Mapping from XendConfig configuration keys to the old
114 # legacy configuration keys that map directly.
116 XENAPI_CFG_TO_LEGACY_CFG = {
117 'uuid': 'uuid',
118 'VCPUs_max': 'vcpus',
119 'cpus': 'cpus',
120 'name_label': 'name',
121 'actions_after_shutdown': 'on_poweroff',
122 'actions_after_reboot': 'on_reboot',
123 'actions_after_crash': 'on_crash',
124 'PV_bootloader': 'bootloader',
125 'PV_bootloader_args': 'bootloader_args',
126 }
128 LEGACY_CFG_TO_XENAPI_CFG = reverse_dict(XENAPI_CFG_TO_LEGACY_CFG)
130 # Platform configuration keys and their types.
131 XENAPI_PLATFORM_CFG_TYPES = {
132 'acpi': int,
133 'apic': int,
134 'boot': str,
135 'device_model': str,
136 'loader': str,
137 'display' : str,
138 'fda': str,
139 'fdb': str,
140 'keymap': str,
141 'isa' : int,
142 'localtime': int,
143 'monitor': int,
144 'nographic': int,
145 'pae' : int,
146 'rtc_timeoffset': int,
147 'serial': str,
148 'sdl': int,
149 'opengl': int,
150 'soundhw': str,
151 'stdvga': int,
152 'videoram': int,
153 'usb': int,
154 'usbdevice': str,
155 'hpet': int,
156 'vnc': int,
157 'vncconsole': int,
158 'vncdisplay': int,
159 'vnclisten': str,
160 'timer_mode': int,
161 'vpt_align': int,
162 'viridian': int,
163 'vncpasswd': str,
164 'vncunused': int,
165 'xauthority': str,
166 'pci': str,
167 'vhpt': int,
168 'guest_os_type': str,
169 'hap': int,
170 'xen_extended_power_mgmt': int,
171 'pci_msitranslate': int,
172 'pci_power_mgmt': int,
173 'xen_platform_pci': int,
174 }
176 # Xen API console 'other_config' keys.
177 XENAPI_CONSOLE_OTHER_CFG = ['vncunused', 'vncdisplay', 'vnclisten',
178 'vncpasswd', 'sdl', 'vnc', 'display', 'xauthority',
179 'keymap', 'opengl']
181 # List of XendConfig configuration keys that have no direct equivalent
182 # in the old world.
184 XENAPI_CFG_TYPES = {
185 'uuid': str,
186 'name_label': str,
187 'name_description': str,
188 'user_version': str,
189 'is_a_template': bool0,
190 'auto_power_on': bool0,
191 'resident_on': str,
192 'memory_static_min': int, # note these are stored in bytes, not KB!
193 'memory_static_max': int,
194 'memory_dynamic_min': int,
195 'memory_dynamic_max': int,
196 'cpus': list,
197 'vcpus_params': dict,
198 'VCPUs_max': int,
199 'VCPUs_at_startup': int,
200 'VCPUs_live': int,
201 'actions_after_shutdown': str,
202 'actions_after_reboot': str,
203 'actions_after_crash': str,
204 'PV_bootloader': str,
205 'PV_kernel': str,
206 'PV_ramdisk': str,
207 'PV_args': str,
208 'PV_bootloader_args': str,
209 'HVM_boot_policy': str,
210 'HVM_boot_params': dict,
211 'PCI_bus': str,
212 'platform': dict,
213 'tools_version': dict,
214 'other_config': dict,
215 'target': int,
216 'security_label': str,
217 'pci': str,
218 'cpuid' : dict,
219 'cpuid_check' : dict,
220 'machine_address_size': int,
221 'suppress_spurious_page_faults': bool0,
222 's3_integrity' : int,
223 'superpages' : int,
224 }
226 # List of legacy configuration keys that have no equivalent in the
227 # Xen API, but are still stored in XendConfig.
229 LEGACY_UNSUPPORTED_BY_XENAPI_CFG = [
230 # roundtripped (dynamic, unmodified)
231 'shadow_memory',
232 'vcpu_avail',
233 'features',
234 # read/write
235 'on_xend_start',
236 'on_xend_stop',
237 # read-only
238 'domid',
239 'start_time',
240 'cpu_time',
241 'online_vcpus',
242 # write-once
243 'cpu',
244 'cpus',
245 ]
247 LEGACY_CFG_TYPES = {
248 'uuid': str,
249 'name': str,
250 'vcpus': int,
251 'vcpu_avail': long,
252 'memory': int,
253 'shadow_memory': int,
254 'maxmem': int,
255 'start_time': float,
256 'cpu_time': float,
257 'features': str,
258 'localtime': int,
259 'name': str,
260 'on_poweroff': str,
261 'on_reboot': str,
262 'on_crash': str,
263 'on_xend_stop': str,
264 'on_xend_start': str,
265 'online_vcpus': int,
266 'rtc/timeoffset': str,
267 'bootloader': str,
268 'bootloader_args': str,
269 }
271 # Values that should be stored in xenstore's /vm/<uuid> that is used
272 # by Xend. Used in XendDomainInfo to restore running VM state from
273 # xenstore.
274 LEGACY_XENSTORE_VM_PARAMS = [
275 'uuid',
276 'name',
277 'vcpus',
278 'vcpu_avail',
279 'memory',
280 'shadow_memory',
281 'maxmem',
282 'start_time',
283 'name',
284 'on_poweroff',
285 'on_crash',
286 'on_reboot',
287 'on_xend_start',
288 'on_xend_stop',
289 'bootloader',
290 'bootloader_args',
291 ]
293 ##
294 ## Config Choices
295 ##
297 CONFIG_RESTART_MODES = ('restart', 'destroy', 'preserve', 'rename-restart',
298 'coredump-destroy', 'coredump-restart')
299 CONFIG_OLD_DOM_STATES = ('running', 'blocked', 'paused', 'shutdown',
300 'crashed', 'dying')
302 class XendConfigError(VmError):
303 def __str__(self):
304 return 'Invalid Configuration: %s' % str(self.value)
306 ##
307 ## XendConfig Class (an extended dictionary)
308 ##
310 class XendConfig(dict):
311 """ The new Xend VM Configuration.
313 Stores the configuration in xenapi compatible format but retains
314 import and export functions for SXP.
315 """
316 def __init__(self, filename = None, sxp_obj = None,
317 xapi = None, dominfo = None):
319 dict.__init__(self)
320 self.update(self._defaults())
322 if filename:
323 try:
324 sxp_obj = sxp.parse(open(filename,'r'))
325 sxp_obj = sxp_obj[0]
326 except IOError, e:
327 raise XendConfigError("Unable to read file: %s" % filename)
329 if sxp_obj:
330 self._sxp_to_xapi(sxp_obj)
331 self._sxp_to_xapi_unsupported(sxp_obj)
332 elif xapi:
333 self.update_with_xenapi_config(xapi)
334 elif dominfo:
335 # output from xc.domain_getinfo
336 self._dominfo_to_xapi(dominfo, update_mem = True)
338 log.debug('XendConfig.init: %s' % scrub_password(self))
340 # validators go here
341 self.validate()
343 """ In time, we should enable this type checking addition. It is great
344 also for tracking bugs and unintended writes to XendDomainInfo.info
345 def __setitem__(self, key, value):
346 type_conv = XENAPI_CFG_TYPES.get(key)
347 if callable(type_conv):
348 try:
349 dict.__setitem__(self, key, type_conv(value))
350 except (ValueError, TypeError):
351 raise XendConfigError("Wrong type for configuration value " +
352 "%s. Expected %s" %
353 (key, type_conv.__name__))
354 else:
355 dict.__setitem__(self, key, value)
356 """
358 def _defaults(self):
359 defaults = {
360 'name_label': 'Domain-Unnamed',
361 'actions_after_shutdown': 'destroy',
362 'actions_after_reboot': 'restart',
363 'actions_after_crash': 'restart',
364 'actions_after_suspend': '',
365 'is_a_template': False,
366 'auto_power_on': False,
367 'is_control_domain': False,
368 'features': '',
369 'PV_bootloader': '',
370 'PV_kernel': '',
371 'PV_ramdisk': '',
372 'PV_args': '',
373 'PV_bootloader_args': '',
374 'HVM_boot_policy': '',
375 'HVM_boot_params': {},
376 'memory_static_min': 0,
377 'memory_dynamic_min': 0,
378 'shadow_memory': 0,
379 'memory_static_max': 0,
380 'memory_dynamic_max': 0,
381 'devices': {},
382 'on_xend_start': 'ignore',
383 'on_xend_stop': 'ignore',
384 'cpus': [],
385 'VCPUs_max': 1,
386 'VCPUs_live': 1,
387 'VCPUs_at_startup': 1,
388 'vcpus_params': {},
389 'console_refs': [],
390 'vif_refs': [],
391 'vbd_refs': [],
392 'vtpm_refs': [],
393 'other_config': {},
394 'platform': {},
395 'target': 0,
396 'superpages': 0,
397 }
399 return defaults
401 #
402 # Here we assume these values exist in the dict.
403 # If they don't we have a bigger problem, lets not
404 # try and 'fix it up' but acutually fix the cause ;-)
405 #
406 def _memory_sanity_check(self):
407 log.trace("_memory_sanity_check memory_static_min: %s, "
408 "memory_static_max: %i, "
409 "memory_dynamic_min: %i, "
410 "memory_dynamic_max: %i",
411 self["memory_static_min"],
412 self["memory_static_max"],
413 self["memory_dynamic_min"],
414 self["memory_dynamic_max"])
416 if not self["memory_static_min"] <= self["memory_static_max"]:
417 raise XendConfigError("memory_static_min must be less " \
418 "than or equal to memory_static_max")
419 if not self["memory_static_min"] <= self["memory_dynamic_min"]:
420 raise XendConfigError("memory_static_min must be less " \
421 "than or equal to memory_dynamic_min")
422 if not self["memory_dynamic_max"] <= self["memory_static_max"]:
423 raise XendConfigError("memory_dynamic_max must be less " \
424 "than or equal to memory_static_max")
425 if not self["memory_dynamic_max"] > 0:
426 raise XendConfigError("memory_dynamic_max must be greater " \
427 "than zero")
428 if not self["memory_static_max"] > 0:
429 raise XendConfigError("memory_static_max must be greater " \
430 "than zero")
432 def _actions_sanity_check(self):
433 for event in ['shutdown', 'reboot', 'crash']:
434 if self['actions_after_' + event] not in CONFIG_RESTART_MODES:
435 raise XendConfigError('Invalid event handling mode: ' +
436 event)
438 def _vcpus_sanity_check(self):
439 if 'VCPUs_max' in self and 'vcpu_avail' not in self:
440 self['vcpu_avail'] = (1 << self['VCPUs_max']) - 1
441 if 'online_vcpus' in self:
442 self['VCPUs_live'] = self['online_vcpus']
444 def _uuid_sanity_check(self):
445 """Make sure UUID is in proper string format with hyphens."""
446 if 'uuid' not in self or not self['uuid']:
447 self['uuid'] = uuid.createString()
448 else:
449 self['uuid'] = uuid.toString(uuid.fromString(self['uuid']))
451 def _name_sanity_check(self):
452 if 'name_label' not in self:
453 self['name_label'] = 'Domain-' + self['uuid']
455 def _platform_sanity_check(self):
456 if 'keymap' not in self['platform'] and XendOptions.instance().get_keymap():
457 self['platform']['keymap'] = XendOptions.instance().get_keymap()
459 if self.is_hvm() or self.has_rfb():
460 if 'device_model' not in self['platform']:
461 self['platform']['device_model'] = xen.util.auxbin.pathTo("qemu-dm")
463 if self.is_hvm():
464 if 'timer_mode' not in self['platform']:
465 self['platform']['timer_mode'] = 1
466 if 'viridian' not in self['platform']:
467 self['platform']['viridian'] = 0
468 if 'rtc_timeoffset' not in self['platform']:
469 self['platform']['rtc_timeoffset'] = 0
470 if 'hpet' not in self['platform']:
471 self['platform']['hpet'] = 0
472 if 'xen_platform_pci' not in self['platform']:
473 self['platform']['xen_platform_pci'] = 1
474 if 'vpt_align' not in self['platform']:
475 self['platform']['vpt_align'] = 1
476 if 'loader' not in self['platform']:
477 # Old configs may have hvmloader set as PV_kernel param
478 if self.has_key('PV_kernel') and self['PV_kernel'] != '':
479 self['platform']['loader'] = self['PV_kernel']
480 self['PV_kernel'] = ''
481 else:
482 self['platform']['loader'] = "/usr/lib/xen/boot/hvmloader"
483 log.debug("Loader is %s" % str(self['platform']['loader']))
485 # Compatibility hack, can go away soon.
486 if 'soundhw' not in self['platform'] and \
487 self['platform'].get('enable_audio'):
488 self['platform']['soundhw'] = 'sb16'
490 def validate(self):
491 self._uuid_sanity_check()
492 self._name_sanity_check()
493 self._memory_sanity_check()
494 self._actions_sanity_check()
495 self._vcpus_sanity_check()
496 self._platform_sanity_check()
498 def _dominfo_to_xapi(self, dominfo, update_mem = False):
499 self['domid'] = dominfo['domid']
500 self['online_vcpus'] = dominfo['online_vcpus']
501 self['VCPUs_max'] = dominfo['max_vcpu_id'] + 1
503 if update_mem:
504 self['memory_dynamic_min'] = dominfo['mem_kb'] * 1024
505 self['memory_dynamic_max'] = dominfo['mem_kb'] * 1024
506 self['memory_static_min'] = 0
507 self['memory_static_max'] = dominfo['maxmem_kb'] * 1024
508 self._memory_sanity_check()
510 self['cpu_time'] = dominfo['cpu_time']/1e9
511 if dominfo.get('ssidref'):
512 ssidref = int(dominfo.get('ssidref'))
513 import xen.util.xsm.xsm as security
514 self['security_label'] = security.ssidref2security_label(ssidref)
516 self['shutdown_reason'] = dominfo['shutdown_reason']
518 # parse state into Xen API states
519 self['running'] = dominfo['running']
520 self['crashed'] = dominfo['crashed']
521 self['dying'] = dominfo['dying']
522 self['shutdown'] = dominfo['shutdown']
523 self['paused'] = dominfo['paused']
524 self['blocked'] = dominfo['blocked']
526 if 'name' in dominfo:
527 self['name_label'] = dominfo['name']
529 if 'handle' in dominfo:
530 self['uuid'] = uuid.toString(dominfo['handle'])
532 def parse_cpuid(self, cfg, field):
533 def int2bin(n, count=32):
534 return "".join([str((n >> y) & 1) for y in range(count-1, -1, -1)])
536 for input, regs in cfg[field].iteritems():
537 if not regs is dict:
538 cfg[field][input] = dict(regs)
540 cpuid = {}
541 for input in cfg[field]:
542 inputs = input.split(',')
543 if inputs[0][0:2] == '0x':
544 inputs[0] = str(int(inputs[0], 16))
545 if len(inputs) == 2:
546 if inputs[1][0:2] == '0x':
547 inputs[1] = str(int(inputs[1], 16))
548 new_input = ','.join(inputs)
549 cpuid[new_input] = {} # new input
550 for reg in cfg[field][input]:
551 val = cfg[field][input][reg]
552 if val[0:2] == '0x':
553 cpuid[new_input][reg] = int2bin(int(val, 16))
554 else:
555 cpuid[new_input][reg] = val
556 cfg[field] = cpuid
558 def _parse_sxp(self, sxp_cfg):
559 """ Populate this XendConfig using the parsed SXP.
561 @param sxp_cfg: Parsed SXP Configuration
562 @type sxp_cfg: list of lists
563 @rtype: dictionary
564 @return: A dictionary containing the parsed options of the SXP.
565 """
566 cfg = {}
568 for key, typ in XENAPI_CFG_TYPES.items():
569 val = sxp.child_value(sxp_cfg, key)
570 if val is not None:
571 try:
572 cfg[key] = typ(val)
573 except (ValueError, TypeError), e:
574 log.warn('Unable to convert type value for key: %s' % key)
576 # Convert deprecated options to current equivalents.
578 restart = sxp.child_value(sxp_cfg, 'restart')
579 if restart:
580 if restart == 'onreboot':
581 cfg['on_poweroff'] = 'destroy'
582 cfg['on_reboot'] = 'restart'
583 cfg['on_crash'] = 'destroy'
584 elif restart == 'always':
585 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
586 cfg[opt] = 'restart'
587 elif restart == 'never':
588 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
589 cfg[opt] = 'never'
590 else:
591 log.warn('Ignoring unrecognised value for deprecated option:'
592 'restart = \'%s\'', restart)
594 # Handle memory, passed in as MiB
596 if sxp.child_value(sxp_cfg, "memory") != None:
597 cfg["memory"] = int(sxp.child_value(sxp_cfg, "memory"))
598 if sxp.child_value(sxp_cfg, "maxmem") != None:
599 cfg["maxmem"] = int(sxp.child_value(sxp_cfg, "maxmem"))
601 # Convert scheduling parameters to vcpus_params
602 if 'vcpus_params' not in cfg:
603 cfg['vcpus_params'] = {}
604 cfg["vcpus_params"]["weight"] = \
605 int(sxp.child_value(sxp_cfg, "cpu_weight", 256))
606 cfg["vcpus_params"]["cap"] = \
607 int(sxp.child_value(sxp_cfg, "cpu_cap", 0))
609 # Only extract options we know about.
610 extract_keys = LEGACY_UNSUPPORTED_BY_XENAPI_CFG + \
611 XENAPI_CFG_TO_LEGACY_CFG.values()
613 for key in extract_keys:
614 val = sxp.child_value(sxp_cfg, key)
615 if val != None:
616 try:
617 cfg[key] = LEGACY_CFG_TYPES[key](val)
618 except KeyError:
619 cfg[key] = val
620 except (TypeError, ValueError), e:
621 log.warn("Unable to parse key %s: %s: %s" %
622 (key, str(val), e))
624 if 'platform' not in cfg:
625 cfg['platform'] = {}
626 localtime = sxp.child_value(sxp_cfg, 'localtime')
627 if localtime is not None:
628 cfg['platform']['localtime'] = localtime
630 # Compatibility hack -- can go soon.
631 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
632 val = sxp.child_value(sxp_cfg, "platform_" + key, None)
633 if val is not None:
634 self['platform'][key] = val
636 # Compatibility hack -- can go soon.
637 boot_order = sxp.child_value(sxp_cfg, 'HVM_boot')
638 if boot_order:
639 cfg['HVM_boot_policy'] = 'BIOS order'
640 cfg['HVM_boot_params'] = { 'order' : boot_order }
643 # Parsing the device SXP's.
644 cfg['devices'] = {}
645 for dev in sxp.children(sxp_cfg, 'device'):
646 config = sxp.child0(dev)
647 dev_type = sxp.name(config)
648 self.device_add(dev_type, cfg_sxp = config, target = cfg)
650 # Extract missing data from configuration entries
651 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
652 if image_sxp:
653 image_vcpus = sxp.child_value(image_sxp, 'vcpus')
654 if image_vcpus != None:
655 try:
656 if 'VCPUs_max' not in cfg:
657 cfg['VCPUs_max'] = int(image_vcpus)
658 elif cfg['VCPUs_max'] != int(image_vcpus):
659 cfg['VCPUs_max'] = int(image_vcpus)
660 log.warn('Overriding vcpus from %d to %d using image'
661 'vcpus value.', cfg['VCPUs_max'])
662 except ValueError, e:
663 raise XendConfigError('integer expeceted: %s: %s' %
664 image_sxp, e)
666 # Deprecated cpu configuration
667 if 'cpu' in cfg:
668 if 'cpus' in cfg:
669 cfg['cpus'] = "%s,%s" % (str(cfg['cpu']), cfg['cpus'])
670 else:
671 cfg['cpus'] = str(cfg['cpu'])
673 # Convert 'cpus' to list of list of ints
674 cpus_list = []
675 if 'cpus' in cfg:
676 # Convert the following string to list of ints.
677 # The string supports a list of ranges (0-3),
678 # seperated by commas, and negation (^1).
679 # Precedence is settled by order of the string:
680 # "0-3,^1" -> [0,2,3]
681 # "0-3,^1,1" -> [0,1,2,3]
682 def cnv(s):
683 l = []
684 for c in s.split(','):
685 if c.find('-') != -1:
686 (x, y) = c.split('-')
687 for i in range(int(x), int(y)+1):
688 l.append(int(i))
689 else:
690 # remove this element from the list
691 if c[0] == '^':
692 l = [x for x in l if x != int(c[1:])]
693 else:
694 l.append(int(c))
695 return l
697 if type(cfg['cpus']) == list:
698 if len(cfg['cpus']) > 0 and type(cfg['cpus'][0]) == list:
699 # If sxp_cfg was created from config.sxp,
700 # the form of 'cpus' is list of list of string.
701 # Convert 'cpus' to list of list of ints.
702 # Conversion examples:
703 # [['1']] -> [[1]]
704 # [['0','2'],['1','3']] -> [[0,2],[1,3]]
705 try:
706 for c1 in cfg['cpus']:
707 cpus = []
708 for c2 in c1:
709 cpus.append(int(c2))
710 cpus_list.append(cpus)
711 except ValueError, e:
712 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
713 else:
714 # Conversion examples:
715 # ["1"] -> [[1]]
716 # ["0,2","1,3"] -> [[0,2],[1,3]]
717 # ["0-3,^1","1-4,^2"] -> [[0,2,3],[1,3,4]]
718 try:
719 for c in cfg['cpus']:
720 cpus = cnv(c)
721 cpus_list.append(cpus)
722 except ValueError, e:
723 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
725 if len(cpus_list) != cfg['vcpus']:
726 raise XendConfigError('vcpus and the item number of cpus are not same')
727 else:
728 # Conversion examples:
729 # vcpus=1:
730 # "1" -> [[1]]
731 # "0-3,^1" -> [[0,2,3]]
732 # vcpus=2:
733 # "1" -> [[1],[1]]
734 # "0-3,^1" -> [[0,2,3],[0,2,3]]
735 try:
736 cpus = cnv(cfg['cpus'])
737 for v in range(0, cfg['vcpus']):
738 cpus_list.append(cpus)
739 except ValueError, e:
740 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
741 else:
742 # Generation examples:
743 # vcpus=1:
744 # -> [[]]
745 # vcpus=2:
746 # -> [[],[]]
747 for v in range(0, cfg['vcpus']):
748 cpus_list.append(list())
750 cfg['cpus'] = cpus_list
752 # Parse cpuid
753 if 'cpuid' in cfg:
754 self.parse_cpuid(cfg, 'cpuid')
755 if 'cpuid_check' in cfg:
756 self.parse_cpuid(cfg, 'cpuid_check')
758 import xen.util.xsm.xsm as security
759 if security.on() == xsconstants.XS_POLICY_USE:
760 from xen.util.acmpolicy import ACM_LABEL_UNLABELED
761 if not 'security' in cfg and sxp.child_value(sxp_cfg, 'security'):
762 cfg['security'] = sxp.child_value(sxp_cfg, 'security')
763 elif not cfg.get('security_label'):
764 cfg['security'] = [['access_control',
765 ['policy', security.get_active_policy_name() ],
766 ['label', ACM_LABEL_UNLABELED ]]]
768 if 'security' in cfg and not cfg.get('security_label'):
769 secinfo = cfg['security']
770 # The xm command sends a list formatted like this:
771 # [['access_control', ['policy', 'xm-test'],['label', 'red']],
772 # ['ssidref', 196611]]
773 policy = ""
774 label = ""
775 for idx in range(0, len(secinfo)):
776 if secinfo[idx][0] == "access_control":
777 for aidx in range(1, len(secinfo[idx])):
778 if secinfo[idx][aidx][0] == "policy":
779 policy = secinfo[idx][aidx][1]
780 if secinfo[idx][aidx][0] == "label":
781 label = secinfo[idx][aidx][1]
782 cfg['security_label'] = \
783 security.set_security_label(policy, label)
784 if not sxp.child_value(sxp_cfg, 'security_label'):
785 del cfg['security']
787 sec_lab = cfg['security_label'].split(":")
788 if len(sec_lab) != 3:
789 raise XendConfigError("Badly formatted security label: %s"
790 % cfg['security_label'])
792 old_state = sxp.child_value(sxp_cfg, 'state')
793 if old_state:
794 for i in range(len(CONFIG_OLD_DOM_STATES)):
795 cfg[CONFIG_OLD_DOM_STATES[i]] = int(old_state[i] != '-')
797 return cfg
800 def _sxp_to_xapi(self, sxp_cfg):
801 """Read in an SXP Configuration object and
802 populate at much of the Xen API with valid values.
803 """
804 log.debug('_sxp_to_xapi(%s)' % scrub_password(sxp_cfg))
806 # _parse_sxp() below will call device_add() and construct devices.
807 # Some devices may require VM's uuid, so setup self['uuid']
808 # beforehand.
809 self['uuid'] = sxp.child_value(sxp_cfg, 'uuid', uuid.createString())
811 cfg = self._parse_sxp(sxp_cfg)
813 for key, typ in XENAPI_CFG_TYPES.items():
814 val = cfg.get(key)
815 if val is not None:
816 self[key] = typ(val)
818 # Convert parameters that can be directly mapped from
819 # the Legacy Config to Xen API Config
821 for apikey, cfgkey in XENAPI_CFG_TO_LEGACY_CFG.items():
822 try:
823 type_conv = XENAPI_CFG_TYPES.get(apikey)
824 if callable(type_conv):
825 self[apikey] = type_conv(cfg[cfgkey])
826 else:
827 log.warn("Unconverted key: " + apikey)
828 self[apikey] = cfg[cfgkey]
829 except KeyError:
830 pass
832 # Lets try and handle memory correctly
834 MiB = 1024 * 1024
836 if "memory" in cfg:
837 self["memory_static_min"] = 0
838 self["memory_static_max"] = int(cfg["memory"]) * MiB
839 self["memory_dynamic_min"] = int(cfg["memory"]) * MiB
840 self["memory_dynamic_max"] = int(cfg["memory"]) * MiB
842 if "maxmem" in cfg:
843 self["memory_static_max"] = int(cfg["maxmem"]) * MiB
845 self._memory_sanity_check()
847 def update_with(n, o):
848 if not self.get(n):
849 self[n] = cfg.get(o, '')
851 update_with('PV_bootloader', 'bootloader')
852 update_with('PV_bootloader_args', 'bootloader_args')
854 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
855 if image_sxp:
856 self.update_with_image_sxp(image_sxp)
858 # Convert Legacy HVM parameters to Xen API configuration
859 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
860 if key in cfg:
861 self['platform'][key] = cfg[key]
863 # set device references in the configuration
864 self['devices'] = cfg.get('devices', {})
865 self['console_refs'] = cfg.get('console_refs', [])
866 self['vif_refs'] = cfg.get('vif_refs', [])
867 self['vbd_refs'] = cfg.get('vbd_refs', [])
868 self['vtpm_refs'] = cfg.get('vtpm_refs', [])
870 # coalesce hvm vnc frame buffer with vfb config
871 if self.is_hvm() and int(self['platform'].get('vnc', 0)) != 0:
872 # add vfb device if it isn't there already
873 if not self.has_rfb():
874 dev_config = ['vfb']
875 dev_config.append(['vnc', '1'])
876 # copy VNC related params from platform config to vfb dev conf
877 for key in ['vncpasswd', 'vncunused', 'vncdisplay',
878 'vnclisten']:
879 if key in self['platform']:
880 dev_config.append([key, self['platform'][key]])
882 self.device_add('vfb', cfg_sxp = dev_config)
885 def has_rfb(self):
886 for console_uuid in self['console_refs']:
887 if self['devices'][console_uuid][1].get('protocol') == 'rfb':
888 return True
889 if self['devices'][console_uuid][0] == 'vfb':
890 return True
891 return False
893 def _sxp_to_xapi_unsupported(self, sxp_cfg):
894 """Read in an SXP configuration object and populate
895 values are that not related directly supported in
896 the Xen API.
897 """
899 log.debug('_sxp_to_xapi_unsupported(%s)' % scrub_password(sxp_cfg))
901 # Parse and convert parameters used to configure
902 # the image (as well as HVM images)
903 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
904 if image_sxp:
905 image_type = sxp.name(image_sxp)
906 if image_type != 'hvm' and image_type != 'linux':
907 self['platform']['image_type'] = image_type
909 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
910 val = sxp.child_value(image_sxp, key, None)
911 if val is not None and val != '':
912 self['platform'][key] = val
914 notes = sxp.children(image_sxp, 'notes')
915 if notes:
916 self['notes'] = self.notes_from_sxp(notes[0])
918 self._hvm_boot_params_from_sxp(image_sxp)
920 # extract backend value
922 backend = []
923 for c in sxp.children(sxp_cfg, 'backend'):
924 backend.append(sxp.name(sxp.child0(c)))
925 if backend:
926 self['backend'] = backend
928 # Parse and convert other Non Xen API parameters.
929 def _set_cfg_if_exists(sxp_arg):
930 val = sxp.child_value(sxp_cfg, sxp_arg)
931 if val != None:
932 if LEGACY_CFG_TYPES.get(sxp_arg):
933 self[sxp_arg] = LEGACY_CFG_TYPES[sxp_arg](val)
934 else:
935 self[sxp_arg] = val
937 _set_cfg_if_exists('shadow_memory')
938 _set_cfg_if_exists('features')
939 _set_cfg_if_exists('on_xend_stop')
940 _set_cfg_if_exists('on_xend_start')
941 _set_cfg_if_exists('vcpu_avail')
943 # Parse and store runtime configuration
944 _set_cfg_if_exists('start_time')
945 _set_cfg_if_exists('cpu_time')
946 _set_cfg_if_exists('shutdown_reason')
947 _set_cfg_if_exists('up_time')
948 _set_cfg_if_exists('status') # TODO, deprecated
950 def _get_old_state_string(self):
951 """Returns the old xm state string.
952 @rtype: string
953 @return: old state string
954 """
955 state_string = ''
956 for state_name in CONFIG_OLD_DOM_STATES:
957 on_off = self.get(state_name, 0)
958 if on_off:
959 state_string += state_name[0]
960 else:
961 state_string += '-'
963 return state_string
966 def update_config(self, dominfo):
967 """Update configuration with the output from xc.domain_getinfo().
969 @param dominfo: Domain information via xc.domain_getinfo()
970 @type dominfo: dict
971 """
972 self._dominfo_to_xapi(dominfo)
973 self.validate()
975 def update_with_xenapi_config(self, xapi):
976 """Update configuration with a Xen API VM struct
978 @param xapi: Xen API VM Struct
979 @type xapi: dict
980 """
982 log.debug('update_with_xenapi_config: %s' % scrub_password(xapi))
984 for key, val in xapi.items():
985 type_conv = XENAPI_CFG_TYPES.get(key)
986 if type_conv is None:
987 key = key.lower()
988 type_conv = XENAPI_CFG_TYPES.get(key)
989 if callable(type_conv):
990 self[key] = type_conv(val)
991 else:
992 self[key] = val
994 # XenAPI defines platform as a string-string map. If platform
995 # configuration exists, convert values to appropriate type.
996 if 'platform' in xapi:
997 for key, val in xapi['platform'].items():
998 type_conv = XENAPI_PLATFORM_CFG_TYPES.get(key)
999 if type_conv is None:
1000 key = key.lower()
1001 type_conv = XENAPI_PLATFORM_CFG_TYPES.get(key)
1002 if callable(type_conv):
1003 self['platform'][key] = type_conv(val)
1004 else:
1005 self['platform'][key] = val
1007 self['vcpus_params']['weight'] = \
1008 int(self['vcpus_params'].get('weight', 256))
1009 self['vcpus_params']['cap'] = int(self['vcpus_params'].get('cap', 0))
1011 def cpuid_to_sxp(self, sxpr, field):
1012 regs_list = []
1013 for input, regs in self[field].iteritems():
1014 reg_list = []
1015 for reg, val in regs.iteritems():
1016 reg_list.append([reg, val])
1017 regs_list.append([input, reg_list])
1018 sxpr.append([field, regs_list])
1021 def to_sxp(self, domain = None, ignore_devices = False, ignore = [],
1022 legacy_only = True):
1023 """ Get SXP representation of this config object.
1025 Incompat: removed store_mfn, console_mfn
1027 @keyword domain: (optional) XendDomainInfo to get extra information
1028 from such as domid and running devices.
1029 @type domain: XendDomainInfo
1030 @keyword ignore: (optional) list of 'keys' that we do not want
1031 to export.
1032 @type ignore: list of strings
1033 @rtype: list of list (SXP representation)
1034 """
1035 sxpr = ['domain']
1037 # TODO: domid/dom is the same thing but called differently
1038 # depending if it is from xenstore or sxpr.
1040 if domain.getDomid() is not None:
1041 sxpr.append(['domid', domain.getDomid()])
1043 if not legacy_only:
1044 for name, typ in XENAPI_CFG_TYPES.items():
1045 if name in self and self[name] not in (None, []):
1046 if typ == dict:
1047 s = self[name].items()
1048 elif typ == list:
1049 s = self[name]
1050 else:
1051 s = str(self[name])
1052 sxpr.append([name, s])
1054 for xenapi, legacy in XENAPI_CFG_TO_LEGACY_CFG.items():
1055 if self.has_key(xenapi) and self[xenapi] not in (None, []):
1056 if type(self[xenapi]) == bool:
1057 # convert booleans to ints before making an sxp item
1058 sxpr.append([legacy, int(self[xenapi])])
1059 else:
1060 sxpr.append([legacy, self[xenapi]])
1062 MiB = 1024*1024
1064 sxpr.append(["maxmem", int(self["memory_static_max"])/MiB])
1065 sxpr.append(["memory", int(self["memory_dynamic_max"])/MiB])
1067 for legacy in LEGACY_UNSUPPORTED_BY_XENAPI_CFG:
1068 if legacy in ('domid', 'uuid', 'cpus'): # skip these
1069 continue
1070 if self.has_key(legacy) and self[legacy] not in (None, []):
1071 sxpr.append([legacy, self[legacy]])
1073 if self.has_key('security_label'):
1074 sxpr.append(['security_label', self['security_label']])
1076 sxpr.append(['image', self.image_sxpr()])
1077 sxpr.append(['status', domain._stateGet()])
1079 if domain.getDomid() is not None:
1080 sxpr.append(['state', self._get_old_state_string()])
1082 if domain:
1083 if domain.store_mfn:
1084 sxpr.append(['store_mfn', domain.store_mfn])
1085 if domain.console_mfn:
1086 sxpr.append(['console_mfn', domain.console_mfn])
1089 # Marshall devices (running or from configuration)
1090 if not ignore_devices:
1091 txn = xstransact()
1092 try:
1093 for cls in XendDevices.valid_devices():
1094 found = False
1096 # figure if there is a dev controller is valid and running
1097 if domain and domain.getDomid() != None:
1098 try:
1099 controller = domain.getDeviceController(cls)
1100 configs = controller.configurations(txn)
1101 for config in configs:
1102 if sxp.name(config) in ('vbd', 'tap'):
1103 # The bootable flag is never written to the
1104 # store as part of the device config.
1105 dev_uuid = sxp.child_value(config, 'uuid')
1106 dev_type, dev_cfg = self['devices'][dev_uuid]
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))
1437 elif dev_type == 'console':
1438 if 'console_refs' not in target:
1439 target['console_refs'] = []
1440 if dev_uuid not in target['console_refs']:
1441 target['console_refs'].append(dev_uuid)
1443 log.debug("XendConfig: reading device: %s" % scrub_password(dev_info))
1444 return dev_uuid
1446 if cfg_xenapi:
1447 dev_info = {}
1448 dev_uuid = ''
1449 if dev_type == 'vif':
1450 dev_info['mac'] = cfg_xenapi.get('MAC')
1451 if not dev_info['mac']:
1452 dev_info['mac'] = randomMAC()
1453 # vifname is the name on the guest, not dom0
1454 # TODO: we don't have the ability to find that out or
1455 # change it from dom0
1456 #if cfg_xenapi.get('device'): # don't add if blank
1457 # dev_info['vifname'] = cfg_xenapi.get('device')
1458 if cfg_xenapi.get('type'):
1459 dev_info['type'] = cfg_xenapi.get('type')
1460 if cfg_xenapi.get('name'):
1461 dev_info['name'] = cfg_xenapi.get('name')
1462 if cfg_xenapi.get('network'):
1463 network = XendAPIStore.get(
1464 cfg_xenapi.get('network'), 'network')
1465 dev_info['bridge'] = network.get_name_label()
1467 if cfg_xenapi.get('security_label'):
1468 dev_info['security_label'] = \
1469 cfg_xenapi.get('security_label')
1471 dev_uuid = cfg_xenapi.get('uuid', None)
1472 if not dev_uuid:
1473 dev_uuid = uuid.createString()
1474 dev_info['uuid'] = dev_uuid
1475 target['devices'][dev_uuid] = (dev_type, dev_info)
1476 target['vif_refs'].append(dev_uuid)
1478 elif dev_type in ('vbd', 'tap'):
1479 dev_info['type'] = cfg_xenapi.get('type', 'Disk')
1480 if dev_info['type'] == 'CD':
1481 old_vbd_type = 'cdrom'
1482 else:
1483 old_vbd_type = 'disk'
1485 dev_info['uname'] = cfg_xenapi.get('image', '')
1486 dev_info['dev'] = '%s:%s' % (cfg_xenapi.get('device'),
1487 old_vbd_type)
1488 dev_info['bootable'] = int(cfg_xenapi.get('bootable', 0))
1489 dev_info['driver'] = cfg_xenapi.get('driver', '')
1490 dev_info['VDI'] = cfg_xenapi.get('VDI', '')
1492 if cfg_xenapi.get('mode') == 'RW':
1493 dev_info['mode'] = 'w'
1494 else:
1495 dev_info['mode'] = 'r'
1497 dev_uuid = cfg_xenapi.get('uuid', None)
1498 if not dev_uuid:
1499 dev_uuid = uuid.createString()
1500 dev_info['uuid'] = dev_uuid
1501 target['devices'][dev_uuid] = (dev_type, dev_info)
1502 target['vbd_refs'].append(dev_uuid)
1504 elif dev_type == 'vtpm':
1505 if cfg_xenapi.get('type'):
1506 dev_info['type'] = cfg_xenapi.get('type')
1508 dev_uuid = cfg_xenapi.get('uuid', None)
1509 if not dev_uuid:
1510 dev_uuid = uuid.createString()
1511 dev_info['uuid'] = dev_uuid
1512 dev_info['other_config'] = cfg_xenapi.get('other_config', {})
1513 target['devices'][dev_uuid] = (dev_type, dev_info)
1514 target['vtpm_refs'].append(dev_uuid)
1516 elif dev_type == 'console':
1517 dev_uuid = cfg_xenapi.get('uuid', None)
1518 if not dev_uuid:
1519 dev_uuid = uuid.createString()
1520 dev_info['uuid'] = dev_uuid
1521 dev_info['protocol'] = cfg_xenapi.get('protocol', 'rfb')
1522 console_other_config = cfg_xenapi.get('other_config', {})
1523 dev_info['other_config'] = console_other_config
1524 if dev_info['protocol'] == 'rfb':
1525 # collapse other config into devinfo for things
1526 # such as vncpasswd, vncunused, etc.
1527 dev_info.update(console_other_config)
1528 dev_info['vnc'] = console_other_config.get('vnc', '0')
1529 dev_info['sdl'] = console_other_config.get('sdl', '0')
1530 target['devices'][dev_uuid] = ('vfb', dev_info)
1531 target['console_refs'].append(dev_uuid)
1533 # if console is rfb, set device_model ensuring qemu
1534 # is invoked for pvfb services
1535 if 'device_model' not in target['platform']:
1536 target['platform']['device_model'] = \
1537 xen.util.auxbin.pathTo("qemu-dm")
1539 # Finally, if we are a pvfb, we need to make a vkbd
1540 # as well that is not really exposed to Xen API
1541 vkbd_uuid = uuid.createString()
1542 target['devices'][vkbd_uuid] = ('vkbd', {})
1544 elif dev_info['protocol'] == 'vt100':
1545 # if someone tries to create a VT100 console
1546 # via the Xen API, we'll have to ignore it
1547 # because we create one automatically in
1548 # XendDomainInfo._update_consoles
1549 raise XendConfigError('Creating vt100 consoles via '
1550 'Xen API is unsupported')
1552 return dev_uuid
1554 # no valid device to add
1555 return ''
1557 def phantom_device_add(self, dev_type, cfg_xenapi = None,
1558 target = None):
1559 """Add a phantom tap device configuration in XenAPI struct format.
1560 """
1562 if target == None:
1563 target = self
1565 if dev_type not in XendDevices.valid_devices() and \
1566 dev_type not in XendDevices.pseudo_devices():
1567 raise XendConfigError("XendConfig: %s not a valid device type" %
1568 dev_type)
1570 if cfg_xenapi == None:
1571 raise XendConfigError("XendConfig: device_add requires some "
1572 "config.")
1574 if cfg_xenapi:
1575 log.debug("XendConfig.phantom_device_add: %s" % str(cfg_xenapi))
1577 if cfg_xenapi:
1578 dev_info = {}
1579 if dev_type in ('vbd', 'tap'):
1580 if dev_type == 'vbd':
1581 dev_info['uname'] = cfg_xenapi.get('image', '')
1582 dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
1583 elif dev_type == 'tap':
1584 if cfg_xenapi.get('image').find('tap:') == -1:
1585 dev_info['uname'] = 'tap:qcow:%s' % cfg_xenapi.get('image')
1586 dev_info['dev'] = '/dev/%s' % cfg_xenapi.get('device')
1587 dev_info['uname'] = cfg_xenapi.get('image')
1588 dev_info['mode'] = cfg_xenapi.get('mode')
1589 dev_info['backend'] = '0'
1590 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
1591 dev_info['uuid'] = dev_uuid
1592 self['devices'][dev_uuid] = (dev_type, dev_info)
1593 self['vbd_refs'].append(dev_uuid)
1594 return dev_uuid
1596 return ''
1598 def pci_convert_dict_to_sxp(self, dev, state, sub_state = None):
1599 sxp = ['pci', ['dev'] + map(lambda (x, y): [x, y], dev.items()),
1600 ['state', state]]
1601 if sub_state != None:
1602 sxp.append(['sub_state', sub_state])
1603 return sxp
1605 def pci_convert_sxp_to_dict(self, dev_sxp):
1606 """Convert pci device sxp to dict
1607 @param dev_sxp: device configuration
1608 @type dev_sxp: SXP object (parsed config)
1609 @return: dev_config
1610 @rtype: dictionary
1611 """
1612 # Parsing the device SXP's. In most cases, the SXP looks
1613 # like this:
1615 # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]]
1617 # However, for PCI devices it looks like this:
1619 # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1], [func, 2]]]
1621 # It seems the reasoning for this difference is because
1622 # pciif.py needs all the PCI device configurations at
1623 # the same time when creating the devices.
1625 # To further complicate matters, Xen 2.0 configuration format
1626 # uses the following for pci device configuration:
1628 # [device, [pci, [domain, 0], [bus, 0], [dev, 1], [func, 2]]]
1630 # For PCI device hotplug support, the SXP of PCI devices is
1631 # extendend like this:
1633 # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1], [func, 2],
1634 # [vslot, 0]],
1635 # [state, 'Initialising']]]
1637 # 'vslot' shows the virtual hotplug slot number which the PCI device
1638 # is inserted in. This is only effective for HVM domains.
1640 # state 'Initialising' indicates that the device is being attached,
1641 # while state 'Closing' indicates that the device is being detached.
1643 # The Dict looks like this:
1645 # { devs: [{domain: 0, bus: 0, slot: 1, func: 2, vslot: 0}],
1646 # states: ['Initialising'] }
1648 dev_config = {}
1650 pci_devs = []
1651 for pci_dev in sxp.children(dev_sxp, 'dev'):
1652 pci_dev_info = {}
1653 for opt_val in pci_dev[1:]:
1654 try:
1655 opt, val = opt_val
1656 pci_dev_info[opt] = val
1657 except (TypeError, ValueError):
1658 pass
1659 # append uuid to each pci device that does't already have one.
1660 if not pci_dev_info.has_key('uuid'):
1661 dpci_uuid = pci_dev_info.get('uuid', uuid.createString())
1662 pci_dev_info['uuid'] = dpci_uuid
1663 pci_devs.append(pci_dev_info)
1664 dev_config['devs'] = pci_devs
1666 pci_states = []
1667 for pci_state in sxp.children(dev_sxp, 'state'):
1668 try:
1669 pci_states.append(pci_state[1])
1670 except IndexError:
1671 raise XendError("Error reading state while parsing pci sxp")
1672 dev_config['states'] = pci_states
1674 return dev_config
1676 def vscsi_convert_sxp_to_dict(self, dev_sxp):
1677 """Convert vscsi device sxp to dict
1678 @param dev_sxp: device configuration
1679 @type dev_sxp: SXP object (parsed config)
1680 @return: dev_config
1681 @rtype: dictionary
1682 """
1683 # Parsing the device SXP's. In most cases, the SXP looks
1684 # like this:
1686 # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]]
1688 # However, for SCSI devices it looks like this:
1690 # [device,
1691 # [vscsi,
1692 # [feature-host, 0],
1693 # [backend, 0],
1694 # [dev,
1695 # [devid, 0], [p-devname, sdb], [p-dev, 1:0:0:1],
1696 # [v-dev, 0:0:0:0], [state, 1]
1697 # ],
1698 # [dev,
1699 # [devid, 0], [p-devname, sdc], [p-dev, 1:0:0:2],
1700 # [v-dev, 0:0:0:1], [satet, 1]
1701 # ]
1702 # ],
1703 # [vscsi,
1704 # [feature-host, 1],
1705 # [backend, 0],
1706 # [dev,
1707 # [devid, 1], [p-devname, sdg], [p-dev, 2:0:0:0],
1708 # [v-dev, 1:0:0:0], [state, 1]
1709 # ],
1710 # [dev,
1711 # [devid, 1], [p-devname, sdh], [p-dev, 2:0:0:1],
1712 # [v-dev, 1:0:0:1], [satet, 1]
1713 # ]
1714 # ]
1715 # ]
1717 # It seems the reasoning for this difference is because
1718 # vscsiif.py needs all the SCSI device configurations with
1719 # same host number at the same time when creating the devices.
1721 # For SCSI device hotplug support, the SXP of SCSI devices is
1722 # extendend like this:
1724 # [device,
1725 # [vscsi,
1726 # [feature-host, 0],
1727 # [backend, 0],
1728 # [dev,
1729 # [devid, 0], [p-devname, sdd], [p-dev, 1:0:0:3],
1730 # [v-dev, 0:0:0:2], [state, 1]
1731 # ]
1732 # ]
1733 # ]
1735 # state xenbusState['Initialising'] indicates that the device is
1736 # being attached, while state xenbusState['Closing'] indicates
1737 # that the device is being detached.
1739 # The Dict looks like this:
1741 # { devs: [ {devid: 0, p-devname: sdd, p-dev: 1:0:0:3,
1742 # v-dev: 0:0:0:2, state: 1} ],
1743 # feature-host: 1 , backend: 0 }
1745 dev_config = {}
1747 vscsi_devs = []
1748 for vscsi_dev in sxp.children(dev_sxp, 'dev'):
1749 vscsi_dev_info = {}
1750 for opt_val in vscsi_dev[1:]:
1751 try:
1752 opt, val = opt_val
1753 vscsi_dev_info[opt] = val
1754 except TypeError:
1755 pass
1756 # append uuid for each vscsi device.
1757 vscsi_uuid = vscsi_dev_info.get('uuid', uuid.createString())
1758 vscsi_dev_info['uuid'] = vscsi_uuid
1759 vscsi_devs.append(vscsi_dev_info)
1760 dev_config['devs'] = vscsi_devs
1762 vscsi_mode = sxp.children(dev_sxp, 'feature-host')[0]
1763 dev_config['feature-host'] = vscsi_mode[1]
1764 try:
1765 vscsi_be = sxp.children(dev_sxp, 'backend')[0]
1766 dev_config['backend'] = vscsi_be[1]
1767 except IndexError:
1768 pass
1770 return dev_config
1772 def console_add(self, protocol, location, other_config = {}):
1773 dev_uuid = uuid.createString()
1774 if protocol == 'vt100':
1775 dev_info = {
1776 'uuid': dev_uuid,
1777 'protocol': protocol,
1778 'location': location,
1779 'other_config': other_config,
1782 if 'devices' not in self:
1783 self['devices'] = {}
1785 self['devices'][dev_uuid] = ('console', dev_info)
1786 self['console_refs'].append(dev_uuid)
1787 return dev_info
1789 return {}
1791 def console_update(self, console_uuid, key, value):
1792 for dev_uuid, (dev_type, dev_info) in self['devices'].items():
1793 if dev_uuid == console_uuid:
1794 dev_info[key] = value
1795 # collapse other_config into dev_info for things
1796 # such as vncpasswd, vncunused, etc.
1797 if key == 'other_config':
1798 for k in XENAPI_CONSOLE_OTHER_CFG:
1799 if k in dev_info and k not in value:
1800 del dev_info[k]
1801 dev_info.update(value)
1802 break
1804 def console_get_all(self, protocol):
1805 if protocol == 'vt100':
1806 consoles = [dinfo for dtype, dinfo in self['devices'].values()
1807 if dtype == 'console']
1808 return [c for c in consoles if c.get('protocol') == protocol]
1810 elif protocol == 'rfb':
1811 vfbs = [dinfo for dtype, dinfo in self['devices'].values()
1812 if dtype == 'vfb']
1814 # move all non-console key values to other_config before
1815 # returning console config
1816 valid_keys = ['uuid', 'location']
1817 for vfb in vfbs:
1818 other_config = {}
1819 for key, val in vfb.items():
1820 if key not in valid_keys:
1821 other_config[key] = vfb[key]
1822 del vfb[key]
1823 vfb['other_config'] = other_config
1824 vfb['protocol'] = 'rfb'
1826 return vfbs
1828 else:
1829 return []
1831 def device_update(self, dev_uuid, cfg_sxp = [], cfg_xenapi = {}):
1832 """Update an existing device with the new configuration.
1834 @rtype: boolean
1835 @return: Returns True if succesfully found and updated a device conf
1836 """
1837 if dev_uuid in self['devices'] and cfg_sxp:
1838 if sxp.child0(cfg_sxp) == 'device':
1839 config = sxp.child0(cfg_sxp)
1840 else:
1841 config = cfg_sxp
1843 dev_type, dev_info = self['devices'][dev_uuid]
1845 if dev_type == 'pci': # Special case for pci
1846 pci_dict = self.pci_convert_sxp_to_dict(config)
1847 pci_devs = pci_dict['devs']
1849 # destroy existing XenAPI DPCI objects
1850 for dpci_uuid in XendDPCI.get_by_VM(self['uuid']):
1851 XendAPIStore.deregister(dpci_uuid, "DPCI")
1853 # create XenAPI DPCI objects.
1854 self.create_dpci_from_sxp(pci_devs)
1856 self['devices'][dev_uuid] = (dev_type,
1857 {'devs': pci_devs,
1858 'uuid': dev_uuid})
1859 return True
1861 if dev_type == 'vscsi': # Special case for vscsi
1862 vscsi_dict = self.vscsi_convert_sxp_to_dict(config)
1863 vscsi_devs = vscsi_dict['devs']
1864 vscsi_mode = vscsi_dict['feature-host']
1865 vscsi_be = vscsi_dict.get('backend', None)
1867 # destroy existing XenAPI DSCSI objects
1868 vscsi_devid = int(dev_info['devs'][0]['devid'])
1869 for dscsi_uuid in XendDSCSI.get_by_VM(self['uuid']):
1870 dscsi_inst = XendAPIStore.get(dscsi_uuid, 'DSCSI')
1871 if vscsi_devid == dscsi_inst.get_virtual_host():
1872 XendAPIStore.deregister(dscsi_uuid, "DSCSI")
1874 # create XenAPI DSCSI objects.
1875 for vscsi_dev in vscsi_devs:
1876 dscsi_uuid = vscsi_dev.get('uuid')
1877 pscsi_uuid = XendPSCSI.get_by_HCTL(vscsi_dev['p-dev'])
1878 if pscsi_uuid is None:
1879 continue
1880 dscsi_record = {
1881 'VM': self['uuid'],
1882 'PSCSI': pscsi_uuid,
1883 'virtual_HCTL': vscsi_dev.get('v-dev')
1885 XendDSCSI(dscsi_uuid, dscsi_record)
1887 vscsi_info = {
1888 'devs': vscsi_devs,
1889 'feature-host': vscsi_mode,
1890 'uuid': dev_uuid
1892 if vscsi_be is not None:
1893 vscsi_info['backend'] = vscsi_be
1894 self['devices'][dev_uuid] = (dev_type, vscsi_info)
1895 return True
1897 for opt_val in config[1:]:
1898 try:
1899 opt, val = opt_val
1900 dev_info[opt] = val
1901 except (TypeError, ValueError):
1902 pass # no value for this config option
1904 self['devices'][dev_uuid] = (dev_type, dev_info)
1905 return True
1907 elif dev_uuid in self['devices'] and cfg_xenapi:
1908 dev_type, dev_info = self['devices'][dev_uuid]
1909 for key, val in cfg_xenapi.items():
1910 dev_info[key] = val
1911 self['devices'][dev_uuid] = (dev_type, dev_info)
1912 return True
1914 return False
1917 def device_sxpr(self, dev_uuid = None, dev_type = None, dev_info = None, target = None):
1918 """Get Device SXPR by either giving the device UUID or (type, config).
1920 @rtype: list of lists
1921 @return: device config sxpr
1922 """
1923 sxpr = []
1925 if target == None:
1926 target = self
1928 if dev_uuid != None and dev_uuid in target['devices']:
1929 dev_type, dev_info = target['devices'][dev_uuid]
1931 if dev_type == None or dev_info == None:
1932 raise XendConfigError("Required either UUID or device type and "
1933 "configuration dictionary.")
1935 sxpr.append(dev_type)
1936 if dev_type in ('console', 'vfb'):
1937 config = [(opt, val) for opt, val in dev_info.items()
1938 if opt != 'other_config']
1939 else:
1940 config = [(opt, val) for opt, val in dev_info.items()]
1942 sxpr += config
1944 return sxpr
1946 def ordered_device_refs(self, target = None):
1947 result = []
1949 if target == None:
1950 target = self
1952 # vkbd devices *must* be before vfb devices, otherwise
1953 # there is a race condition when setting up devices
1954 # where the daemon spawned for the vfb may write stuff
1955 # into xenstore vkbd backend, before DevController has
1956 # setup permissions on the vkbd backend path. This race
1957 # results in domain creation failing with 'device already
1958 # connected' messages
1959 result.extend([u for u in target['devices'].keys() if target['devices'][u][0] == 'vkbd'])
1961 result.extend(target.get('console_refs', []) +
1962 target.get('vbd_refs', []) +
1963 target.get('vif_refs', []) +
1964 target.get('vtpm_refs', []))
1966 result.extend([u for u in target['devices'].keys() if u not in result])
1967 return result
1969 def all_devices_sxpr(self, target = None):
1970 """Returns the SXPR for all devices in the current configuration."""
1971 sxprs = []
1973 if target == None:
1974 target = self
1976 if 'devices' not in target:
1977 return sxprs
1979 ordered_refs = self.ordered_device_refs(target = target)
1980 for dev_uuid in ordered_refs:
1981 dev_type, dev_info = target['devices'][dev_uuid]
1982 if dev_type == 'pci' or dev_type == 'vscsi': # special case for pci devices
1983 if dev_type == 'pci':
1984 sxpr = ['pci', ['uuid', dev_info['uuid']]]
1985 elif dev_type == 'vscsi':
1986 sxpr = ['vscsi', ['uuid', dev_info['uuid']],
1987 ['feature-host', dev_info['feature-host']]]
1988 if dev_info.has_key('backend'):
1989 sxpr.append(['backend', dev_info['backend']])
1990 for pci_dev_info in dev_info['devs']:
1991 pci_dev_sxpr = ['dev']
1992 for opt, val in pci_dev_info.items():
1993 pci_dev_sxpr.append([opt, val])
1994 sxpr.append(pci_dev_sxpr)
1995 sxprs.append((dev_type, sxpr))
1996 else:
1997 sxpr = self.device_sxpr(dev_type = dev_type,
1998 dev_info = dev_info,
1999 target = target)
2000 sxprs.append((dev_type, sxpr))
2002 return sxprs
2004 def image_sxpr(self):
2005 """Returns a backwards compatible image SXP expression that is
2006 used in xenstore's /vm/<uuid>/image value and xm list."""
2007 image = [self.image_type()]
2008 if self.has_key('PV_kernel'):
2009 image.append(['kernel', self['PV_kernel']])
2010 if self.has_key('PV_ramdisk') and self['PV_ramdisk']:
2011 image.append(['ramdisk', self['PV_ramdisk']])
2012 if self.has_key('PV_args') and self['PV_args']:
2013 image.append(['args', self['PV_args']])
2014 if self.has_key('superpages'):
2015 image.append(['superpages', self['superpages']])
2017 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
2018 if key in self['platform']:
2019 image.append([key, self['platform'][key]])
2021 if 'notes' in self:
2022 image.append(self.notes_sxp(self['notes']))
2024 return image
2026 def update_with_image_sxp(self, image_sxp, bootloader = False):
2027 # Convert Legacy "image" config to Xen API PV_*
2028 # configuration
2029 log.debug("update_with_image_sxp(%s)" % scrub_password(image_sxp))
2031 # user-specified args must come last: previous releases did this and
2032 # some domU kernels rely upon the ordering.
2033 kernel_args = sxp.child_value(image_sxp, 'args', '')
2035 # attempt to extract extra arguments from SXP config
2036 arg_ip = sxp.child_value(image_sxp, 'ip')
2037 if arg_ip and not re.search(r'ip=[^ ]+', kernel_args):
2038 kernel_args = 'ip=%s ' % arg_ip + kernel_args
2039 arg_root = sxp.child_value(image_sxp, 'root')
2040 if arg_root and not re.search(r'root=', kernel_args):
2041 kernel_args = 'root=%s ' % arg_root + kernel_args
2043 if bootloader:
2044 self['_temp_using_bootloader'] = '1'
2045 self['_temp_kernel'] = sxp.child_value(image_sxp, 'kernel','')
2046 self['_temp_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
2047 self['_temp_args'] = kernel_args
2048 else:
2049 self['PV_kernel'] = sxp.child_value(image_sxp, 'kernel','')
2050 self['PV_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
2051 self['PV_args'] = kernel_args
2053 self['superpages'] = sxp.child_value(image_sxp, 'superpages',0)
2055 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
2056 val = sxp.child_value(image_sxp, key, None)
2057 if val is not None and val != '':
2058 self['platform'][key] = val
2060 notes = sxp.children(image_sxp, 'notes')
2061 if notes:
2062 self['notes'] = self.notes_from_sxp(notes[0])
2064 self._hvm_boot_params_from_sxp(image_sxp)
2066 def set_notes(self, notes):
2067 'Add parsed elfnotes to image'
2068 self['notes'] = notes
2070 def get_notes(self):
2071 try:
2072 return self['notes'] or {}
2073 except KeyError:
2074 return {}
2076 def notes_from_sxp(self, nsxp):
2077 notes = {}
2078 for note in sxp.children(nsxp):
2079 notes[note[0]] = note[1]
2080 return notes
2082 def notes_sxp(self, notes):
2083 nsxp = ['notes']
2084 for k, v in notes.iteritems():
2085 nsxp.append([k, str(v)])
2086 return nsxp
2088 def _hvm_boot_params_from_sxp(self, image_sxp):
2089 boot = sxp.child_value(image_sxp, 'boot', None)
2090 if boot is not None:
2091 self['HVM_boot_policy'] = 'BIOS order'
2092 self['HVM_boot_params'] = { 'order' : boot }
2094 def is_hvm(self):
2095 return self['HVM_boot_policy'] != ''
2097 def target(self):
2098 return self['target']
2100 def image_type(self):
2101 stored_type = self['platform'].get('image_type')
2102 return stored_type or (self.is_hvm() and 'hvm' or 'linux')
2104 def is_hap(self):
2105 return self['platform'].get('hap', 0)
2107 def update_platform_pci(self):
2108 if not self.is_hvm():
2109 return
2111 pci = []
2112 for dev_type, dev_info in self.all_devices_sxpr():
2113 if dev_type != 'pci':
2114 continue
2115 for dev in sxp.children(dev_info, 'dev'):
2116 domain = sxp.child_value(dev, 'domain')
2117 bus = sxp.child_value(dev, 'bus')
2118 slot = sxp.child_value(dev, 'slot')
2119 func = sxp.child_value(dev, 'func')
2120 vslot = sxp.child_value(dev, 'vslot')
2121 opts = ''
2122 for opt in sxp.child_value(dev, 'opts', []):
2123 if opts:
2124 opts += ','
2125 opts += '%s=%s' % (opt[0], str(opt[1]))
2126 pci.append([domain, bus, slot, func, vslot, opts])
2127 self['platform']['pci'] = pci