ia64/xen-unstable

view tools/python/xen/xend/XendConfig.py @ 19639:205b1badbcfd

Add support for superpages (hugepages) in PV domain

This patch adds the option "superpages" to the domain configuration
file. If it is set, the domain is populated using 2M pages.

This code does not support fallback to small pages. If the domain can
not be created with 2M pages, the create will fail.

The patch also includes support for saving and restoring domains with
the superpage flag set. However, if a domain has freed small pages
within its physical page array and then extended the array, the
restore will fill in those freed pages. It will then attempt to
allocate more than its memory limit and will fail. This is
significant because apparently Linux does this during boot, thus a
freshly booted Linux image can not be saved and restored successfully.

Signed-off-by: Dave McCracken <dcm@mccr.org>
author Keir Fraser <keir.fraser@citrix.com>
date Tue May 26 09:58:38 2009 +0100 (2009-05-26)
parents 4eef30c14a14
children 9ff5c79b0ceb
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
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 device_add(self, dev_type, cfg_sxp = None, cfg_xenapi = None,
1226 target = None):
1227 """Add a device configuration in SXP format or XenAPI struct format.
1229 For SXP, it could be either:
1231 [device, [vbd, [uname ...]]
1233 or:
1235 [vbd, [uname ..]]
1237 @type cfg_sxp: list of lists (parsed sxp object)
1238 @param cfg_sxp: SXP configuration object
1239 @type cfg_xenapi: dict
1240 @param cfg_xenapi: A device configuration from Xen API (eg. vbd,vif)
1241 @param target: write device information to
1242 @type target: None or a dictionary
1243 @rtype: string
1244 @return: Assigned UUID of the device.
1245 """
1246 if target == None:
1247 target = self
1249 if dev_type not in XendDevices.valid_devices():
1250 raise XendConfigError("XendConfig: %s not a valid device type" %
1251 dev_type)
1253 if cfg_sxp == None and cfg_xenapi == None:
1254 raise XendConfigError("XendConfig: device_add requires some "
1255 "config.")
1257 #if cfg_sxp:
1258 # log.debug("XendConfig.device_add: %s" % str(cfg_sxp))
1259 #if cfg_xenapi:
1260 # log.debug("XendConfig.device_add: %s" % str(cfg_xenapi))
1262 if cfg_sxp:
1263 if sxp.child0(cfg_sxp) == 'device':
1264 config = sxp.child0(cfg_sxp)
1265 else:
1266 config = cfg_sxp
1268 dev_type = sxp.name(config)
1269 dev_info = {}
1271 if dev_type == 'pci':
1272 pci_devs_uuid = sxp.child_value(config, 'uuid',
1273 uuid.createString())
1275 pci_dict = self.pci_convert_sxp_to_dict(config)
1276 pci_devs = pci_dict['devs']
1278 # create XenAPI DPCI objects.
1279 for pci_dev in pci_devs:
1280 dpci_uuid = pci_dev.get('uuid')
1281 ppci_uuid = XendPPCI.get_by_sbdf(pci_dev['domain'],
1282 pci_dev['bus'],
1283 pci_dev['slot'],
1284 pci_dev['func'])
1285 if ppci_uuid is None:
1286 continue
1287 dpci_record = {
1288 'VM': self['uuid'],
1289 'PPCI': ppci_uuid,
1290 'hotplug_slot': pci_dev.get('requested_vslot', 0)
1293 dpci_opts = pci_dev.get('opts')
1294 if dpci_opts and len(dpci_opts) > 0:
1295 dpci_record['options'] = dpci_opts
1297 XendDPCI(dpci_uuid, dpci_record)
1299 target['devices'][pci_devs_uuid] = (dev_type,
1300 {'devs': pci_devs,
1301 'uuid': pci_devs_uuid})
1303 log.debug("XendConfig: reading device: %s" % pci_devs)
1305 return pci_devs_uuid
1307 if dev_type == 'vscsi':
1308 vscsi_devs_uuid = sxp.child_value(config, 'uuid',
1309 uuid.createString())
1310 vscsi_dict = self.vscsi_convert_sxp_to_dict(config)
1311 vscsi_devs = vscsi_dict['devs']
1312 vscsi_mode = vscsi_dict['feature-host']
1313 vscsi_be = vscsi_dict.get('backend', None)
1315 # create XenAPI DSCSI objects.
1316 for vscsi_dev in vscsi_devs:
1317 dscsi_uuid = vscsi_dev.get('uuid')
1318 pscsi_uuid = XendPSCSI.get_by_HCTL(vscsi_dev['p-dev'])
1319 if pscsi_uuid is None:
1320 continue
1321 dscsi_record = {
1322 'VM': self['uuid'],
1323 'PSCSI': pscsi_uuid,
1324 'virtual_HCTL': vscsi_dev.get('v-dev')
1326 XendDSCSI(dscsi_uuid, dscsi_record)
1328 vscsi_info = {
1329 'devs': vscsi_devs,
1330 'feature-host': vscsi_mode,
1331 'uuid': vscsi_devs_uuid
1333 if vscsi_be is not None:
1334 vscsi_info['backend'] = vscsi_be
1335 target['devices'][vscsi_devs_uuid] = (dev_type, vscsi_info)
1336 log.debug("XendConfig: reading device: %s,%s" % \
1337 (vscsi_devs, vscsi_mode))
1338 return vscsi_devs_uuid
1340 for opt_val in config[1:]:
1341 try:
1342 opt, val = opt_val
1343 dev_info[opt] = val
1344 except (TypeError, ValueError): # unpack error
1345 pass
1347 if dev_type == 'vbd':
1348 if dev_info.get('dev', '').startswith('ioemu:'):
1349 dev_info['driver'] = 'ioemu'
1350 else:
1351 dev_info['driver'] = 'paravirtualised'
1353 if dev_type == 'tap':
1354 if dev_info['uname'].split(':')[1] not in blktap_disk_types:
1355 raise XendConfigError("tap:%s not a valid disk type" %
1356 dev_info['uname'].split(':')[1])
1358 if dev_type == 'vif':
1359 if not dev_info.get('mac'):
1360 dev_info['mac'] = randomMAC()
1362 ret_uuid = self.device_duplicate_check(dev_type, dev_info, target, config)
1363 if ret_uuid != None:
1364 return ret_uuid
1366 if dev_type == 'vif':
1367 if dev_info.get('policy') and dev_info.get('label'):
1368 dev_info['security_label'] = "%s:%s:%s" % \
1369 (xsconstants.ACM_POLICY_ID,
1370 dev_info['policy'],dev_info['label'])
1372 # create uuid if it doesn't exist
1373 dev_uuid = dev_info.get('uuid', None)
1374 if not dev_uuid:
1375 dev_uuid = uuid.createString()
1376 dev_info['uuid'] = dev_uuid
1378 # store dev references by uuid for certain device types
1379 target['devices'][dev_uuid] = (dev_type, dev_info)
1380 if dev_type in ('vif', 'vbd', 'vtpm'):
1381 param = '%s_refs' % dev_type
1382 if param not in target:
1383 target[param] = []
1384 if dev_uuid not in target[param]:
1385 if dev_type == 'vbd' and 'bootable' not in dev_info:
1386 # Compat hack -- mark first disk bootable
1387 dev_info['bootable'] = int(not target[param])
1388 target[param].append(dev_uuid)
1389 elif dev_type == 'tap':
1390 if 'vbd_refs' not in target:
1391 target['vbd_refs'] = []
1392 if dev_uuid not in target['vbd_refs']:
1393 if 'bootable' not in dev_info:
1394 # Compat hack -- mark first disk bootable
1395 dev_info['bootable'] = int(not target['vbd_refs'])
1396 target['vbd_refs'].append(dev_uuid)
1398 elif dev_type == 'vfb':
1399 # Populate other config with aux data that is associated
1400 # with vfb
1402 other_config = {}
1403 for key in XENAPI_CONSOLE_OTHER_CFG:
1404 if key in dev_info:
1405 other_config[key] = dev_info[key]
1406 target['devices'][dev_uuid][1]['other_config'] = other_config
1409 if 'console_refs' not in target:
1410 target['console_refs'] = []
1412 # Treat VFB devices as console devices so they are found
1413 # through Xen API
1414 if dev_uuid not in target['console_refs']:
1415 target['console_refs'].append(dev_uuid)
1417 # Cope with old-format save files which say under vfb
1418 # (type vfb) rather than (vfb 1)
1419 try:
1420 vfb_type = dev_info['type']
1421 except KeyError:
1422 vfb_type = None
1423 log.debug("iwj dev_type=%s vfb type %s" %
1424 (dev_type, `vfb_type`))
1426 if vfb_type == 'vnc' or vfb_type == 'sdl':
1427 dev_info[vfb_type] = 1
1428 del dev_info['type']
1429 log.debug("iwj dev_type=%s vfb setting dev_info['%s']" %
1430 (dev_type, vfb_type))
1432 elif dev_type == 'console':
1433 if 'console_refs' not in target:
1434 target['console_refs'] = []
1435 if dev_uuid not in target['console_refs']:
1436 target['console_refs'].append(dev_uuid)
1438 log.debug("XendConfig: reading device: %s" % scrub_password(dev_info))
1439 return dev_uuid
1441 if cfg_xenapi:
1442 dev_info = {}
1443 dev_uuid = ''
1444 if dev_type == 'vif':
1445 dev_info['mac'] = cfg_xenapi.get('MAC')
1446 if not dev_info['mac']:
1447 dev_info['mac'] = randomMAC()
1448 # vifname is the name on the guest, not dom0
1449 # TODO: we don't have the ability to find that out or
1450 # change it from dom0
1451 #if cfg_xenapi.get('device'): # don't add if blank
1452 # dev_info['vifname'] = cfg_xenapi.get('device')
1453 if cfg_xenapi.get('type'):
1454 dev_info['type'] = cfg_xenapi.get('type')
1455 if cfg_xenapi.get('name'):
1456 dev_info['name'] = cfg_xenapi.get('name')
1457 if cfg_xenapi.get('network'):
1458 network = XendAPIStore.get(
1459 cfg_xenapi.get('network'), 'network')
1460 dev_info['bridge'] = network.get_name_label()
1462 if cfg_xenapi.get('security_label'):
1463 dev_info['security_label'] = \
1464 cfg_xenapi.get('security_label')
1466 dev_uuid = cfg_xenapi.get('uuid', None)
1467 if not dev_uuid:
1468 dev_uuid = uuid.createString()
1469 dev_info['uuid'] = dev_uuid
1470 target['devices'][dev_uuid] = (dev_type, dev_info)
1471 target['vif_refs'].append(dev_uuid)
1473 elif dev_type in ('vbd', 'tap'):
1474 dev_info['type'] = cfg_xenapi.get('type', 'Disk')
1475 if dev_info['type'] == 'CD':
1476 old_vbd_type = 'cdrom'
1477 else:
1478 old_vbd_type = 'disk'
1480 dev_info['uname'] = cfg_xenapi.get('image', '')
1481 dev_info['dev'] = '%s:%s' % (cfg_xenapi.get('device'),
1482 old_vbd_type)
1483 dev_info['bootable'] = int(cfg_xenapi.get('bootable', 0))
1484 dev_info['driver'] = cfg_xenapi.get('driver', '')
1485 dev_info['VDI'] = cfg_xenapi.get('VDI', '')
1487 if cfg_xenapi.get('mode') == 'RW':
1488 dev_info['mode'] = 'w'
1489 else:
1490 dev_info['mode'] = 'r'
1492 dev_uuid = cfg_xenapi.get('uuid', None)
1493 if not dev_uuid:
1494 dev_uuid = uuid.createString()
1495 dev_info['uuid'] = dev_uuid
1496 target['devices'][dev_uuid] = (dev_type, dev_info)
1497 target['vbd_refs'].append(dev_uuid)
1499 elif dev_type == 'vtpm':
1500 if cfg_xenapi.get('type'):
1501 dev_info['type'] = cfg_xenapi.get('type')
1503 dev_uuid = cfg_xenapi.get('uuid', None)
1504 if not dev_uuid:
1505 dev_uuid = uuid.createString()
1506 dev_info['uuid'] = dev_uuid
1507 dev_info['other_config'] = cfg_xenapi.get('other_config', {})
1508 target['devices'][dev_uuid] = (dev_type, dev_info)
1509 target['vtpm_refs'].append(dev_uuid)
1511 elif dev_type == 'console':
1512 dev_uuid = cfg_xenapi.get('uuid', None)
1513 if not dev_uuid:
1514 dev_uuid = uuid.createString()
1515 dev_info['uuid'] = dev_uuid
1516 dev_info['protocol'] = cfg_xenapi.get('protocol', 'rfb')
1517 console_other_config = cfg_xenapi.get('other_config', {})
1518 dev_info['other_config'] = console_other_config
1519 if dev_info['protocol'] == 'rfb':
1520 # collapse other config into devinfo for things
1521 # such as vncpasswd, vncunused, etc.
1522 dev_info.update(console_other_config)
1523 dev_info['vnc'] = console_other_config.get('vnc', '0')
1524 dev_info['sdl'] = console_other_config.get('sdl', '0')
1525 target['devices'][dev_uuid] = ('vfb', dev_info)
1526 target['console_refs'].append(dev_uuid)
1528 # if console is rfb, set device_model ensuring qemu
1529 # is invoked for pvfb services
1530 if 'device_model' not in target['platform']:
1531 target['platform']['device_model'] = \
1532 xen.util.auxbin.pathTo("qemu-dm")
1534 # Finally, if we are a pvfb, we need to make a vkbd
1535 # as well that is not really exposed to Xen API
1536 vkbd_uuid = uuid.createString()
1537 target['devices'][vkbd_uuid] = ('vkbd', {})
1539 elif dev_info['protocol'] == 'vt100':
1540 # if someone tries to create a VT100 console
1541 # via the Xen API, we'll have to ignore it
1542 # because we create one automatically in
1543 # XendDomainInfo._update_consoles
1544 raise XendConfigError('Creating vt100 consoles via '
1545 'Xen API is unsupported')
1547 return dev_uuid
1549 # no valid device to add
1550 return ''
1552 def phantom_device_add(self, dev_type, cfg_xenapi = None,
1553 target = None):
1554 """Add a phantom tap device configuration in XenAPI struct format.
1555 """
1557 if target == None:
1558 target = self
1560 if dev_type not in XendDevices.valid_devices() and \
1561 dev_type not in XendDevices.pseudo_devices():
1562 raise XendConfigError("XendConfig: %s not a valid device type" %
1563 dev_type)
1565 if cfg_xenapi == None:
1566 raise XendConfigError("XendConfig: device_add requires some "
1567 "config.")
1569 if cfg_xenapi:
1570 log.debug("XendConfig.phantom_device_add: %s" % str(cfg_xenapi))
1572 if cfg_xenapi:
1573 dev_info = {}
1574 if dev_type in ('vbd', 'tap'):
1575 if dev_type == 'vbd':
1576 dev_info['uname'] = cfg_xenapi.get('image', '')
1577 dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
1578 elif dev_type == 'tap':
1579 if cfg_xenapi.get('image').find('tap:') == -1:
1580 dev_info['uname'] = 'tap:qcow:%s' % cfg_xenapi.get('image')
1581 dev_info['dev'] = '/dev/%s' % cfg_xenapi.get('device')
1582 dev_info['uname'] = cfg_xenapi.get('image')
1583 dev_info['mode'] = cfg_xenapi.get('mode')
1584 dev_info['backend'] = '0'
1585 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
1586 dev_info['uuid'] = dev_uuid
1587 self['devices'][dev_uuid] = (dev_type, dev_info)
1588 self['vbd_refs'].append(dev_uuid)
1589 return dev_uuid
1591 return ''
1593 def pci_convert_sxp_to_dict(self, dev_sxp):
1594 """Convert pci device sxp to dict
1595 @param dev_sxp: device configuration
1596 @type dev_sxp: SXP object (parsed config)
1597 @return: dev_config
1598 @rtype: dictionary
1599 """
1600 # Parsing the device SXP's. In most cases, the SXP looks
1601 # like this:
1603 # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]]
1605 # However, for PCI devices it looks like this:
1607 # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1], [func, 2]]]
1609 # It seems the reasoning for this difference is because
1610 # pciif.py needs all the PCI device configurations at
1611 # the same time when creating the devices.
1613 # To further complicate matters, Xen 2.0 configuration format
1614 # uses the following for pci device configuration:
1616 # [device, [pci, [domain, 0], [bus, 0], [dev, 1], [func, 2]]]
1618 # For PCI device hotplug support, the SXP of PCI devices is
1619 # extendend like this:
1621 # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1], [func, 2],
1622 # [vslot, 0]],
1623 # [state, 'Initialising']]]
1625 # 'vslot' shows the virtual hotplug slot number which the PCI device
1626 # is inserted in. This is only effective for HVM domains.
1628 # state 'Initialising' indicates that the device is being attached,
1629 # while state 'Closing' indicates that the device is being detached.
1631 # The Dict looks like this:
1633 # { devs: [{domain: 0, bus: 0, slot: 1, func: 2, vslot: 0}],
1634 # states: ['Initialising'] }
1636 dev_config = {}
1638 pci_devs = []
1639 for pci_dev in sxp.children(dev_sxp, 'dev'):
1640 pci_dev_info = {}
1641 for opt_val in pci_dev[1:]:
1642 try:
1643 opt, val = opt_val
1644 pci_dev_info[opt] = val
1645 except (TypeError, ValueError):
1646 pass
1647 # append uuid for each pci device.
1648 dpci_uuid = pci_dev_info.get('uuid', uuid.createString())
1649 pci_dev_info['uuid'] = dpci_uuid
1650 pci_devs.append(pci_dev_info)
1651 dev_config['devs'] = pci_devs
1653 pci_states = []
1654 for pci_state in sxp.children(dev_sxp, 'state'):
1655 try:
1656 pci_states.append(pci_state[1])
1657 except IndexError:
1658 raise XendError("Error reading state while parsing pci sxp")
1659 dev_config['states'] = pci_states
1661 return dev_config
1663 def vscsi_convert_sxp_to_dict(self, dev_sxp):
1664 """Convert vscsi device sxp to dict
1665 @param dev_sxp: device configuration
1666 @type dev_sxp: SXP object (parsed config)
1667 @return: dev_config
1668 @rtype: dictionary
1669 """
1670 # Parsing the device SXP's. In most cases, the SXP looks
1671 # like this:
1673 # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]]
1675 # However, for SCSI devices it looks like this:
1677 # [device,
1678 # [vscsi,
1679 # [feature-host, 0],
1680 # [backend, 0],
1681 # [dev,
1682 # [devid, 0], [p-devname, sdb], [p-dev, 1:0:0:1],
1683 # [v-dev, 0:0:0:0], [state, 1]
1684 # ],
1685 # [dev,
1686 # [devid, 0], [p-devname, sdc], [p-dev, 1:0:0:2],
1687 # [v-dev, 0:0:0:1], [satet, 1]
1688 # ]
1689 # ],
1690 # [vscsi,
1691 # [feature-host, 1],
1692 # [backend, 0],
1693 # [dev,
1694 # [devid, 1], [p-devname, sdg], [p-dev, 2:0:0:0],
1695 # [v-dev, 1:0:0:0], [state, 1]
1696 # ],
1697 # [dev,
1698 # [devid, 1], [p-devname, sdh], [p-dev, 2:0:0:1],
1699 # [v-dev, 1:0:0:1], [satet, 1]
1700 # ]
1701 # ]
1702 # ]
1704 # It seems the reasoning for this difference is because
1705 # vscsiif.py needs all the SCSI device configurations with
1706 # same host number at the same time when creating the devices.
1708 # For SCSI device hotplug support, the SXP of SCSI devices is
1709 # extendend like this:
1711 # [device,
1712 # [vscsi,
1713 # [feature-host, 0],
1714 # [backend, 0],
1715 # [dev,
1716 # [devid, 0], [p-devname, sdd], [p-dev, 1:0:0:3],
1717 # [v-dev, 0:0:0:2], [state, 1]
1718 # ]
1719 # ]
1720 # ]
1722 # state xenbusState['Initialising'] indicates that the device is
1723 # being attached, while state xenbusState['Closing'] indicates
1724 # that the device is being detached.
1726 # The Dict looks like this:
1728 # { devs: [ {devid: 0, p-devname: sdd, p-dev: 1:0:0:3,
1729 # v-dev: 0:0:0:2, state: 1} ],
1730 # feature-host: 1 , backend: 0 }
1732 dev_config = {}
1734 vscsi_devs = []
1735 for vscsi_dev in sxp.children(dev_sxp, 'dev'):
1736 vscsi_dev_info = {}
1737 for opt_val in vscsi_dev[1:]:
1738 try:
1739 opt, val = opt_val
1740 vscsi_dev_info[opt] = val
1741 except TypeError:
1742 pass
1743 # append uuid for each vscsi device.
1744 vscsi_uuid = vscsi_dev_info.get('uuid', uuid.createString())
1745 vscsi_dev_info['uuid'] = vscsi_uuid
1746 vscsi_devs.append(vscsi_dev_info)
1747 dev_config['devs'] = vscsi_devs
1749 vscsi_mode = sxp.children(dev_sxp, 'feature-host')[0]
1750 dev_config['feature-host'] = vscsi_mode[1]
1751 try:
1752 vscsi_be = sxp.children(dev_sxp, 'backend')[0]
1753 dev_config['backend'] = vscsi_be[1]
1754 except IndexError:
1755 pass
1757 return dev_config
1759 def console_add(self, protocol, location, other_config = {}):
1760 dev_uuid = uuid.createString()
1761 if protocol == 'vt100':
1762 dev_info = {
1763 'uuid': dev_uuid,
1764 'protocol': protocol,
1765 'location': location,
1766 'other_config': other_config,
1769 if 'devices' not in self:
1770 self['devices'] = {}
1772 self['devices'][dev_uuid] = ('console', dev_info)
1773 self['console_refs'].append(dev_uuid)
1774 return dev_info
1776 return {}
1778 def console_update(self, console_uuid, key, value):
1779 for dev_uuid, (dev_type, dev_info) in self['devices'].items():
1780 if dev_uuid == console_uuid:
1781 dev_info[key] = value
1782 # collapse other_config into dev_info for things
1783 # such as vncpasswd, vncunused, etc.
1784 if key == 'other_config':
1785 for k in XENAPI_CONSOLE_OTHER_CFG:
1786 if k in dev_info and k not in value:
1787 del dev_info[k]
1788 dev_info.update(value)
1789 break
1791 def console_get_all(self, protocol):
1792 if protocol == 'vt100':
1793 consoles = [dinfo for dtype, dinfo in self['devices'].values()
1794 if dtype == 'console']
1795 return [c for c in consoles if c.get('protocol') == protocol]
1797 elif protocol == 'rfb':
1798 vfbs = [dinfo for dtype, dinfo in self['devices'].values()
1799 if dtype == 'vfb']
1801 # move all non-console key values to other_config before
1802 # returning console config
1803 valid_keys = ['uuid', 'location']
1804 for vfb in vfbs:
1805 other_config = {}
1806 for key, val in vfb.items():
1807 if key not in valid_keys:
1808 other_config[key] = vfb[key]
1809 del vfb[key]
1810 vfb['other_config'] = other_config
1811 vfb['protocol'] = 'rfb'
1813 return vfbs
1815 else:
1816 return []
1818 def device_update(self, dev_uuid, cfg_sxp = [], cfg_xenapi = {}):
1819 """Update an existing device with the new configuration.
1821 @rtype: boolean
1822 @return: Returns True if succesfully found and updated a device conf
1823 """
1824 if dev_uuid in self['devices'] and cfg_sxp:
1825 if sxp.child0(cfg_sxp) == 'device':
1826 config = sxp.child0(cfg_sxp)
1827 else:
1828 config = cfg_sxp
1830 dev_type, dev_info = self['devices'][dev_uuid]
1832 if dev_type == 'pci': # Special case for pci
1833 pci_dict = self.pci_convert_sxp_to_dict(config)
1834 pci_devs = pci_dict['devs']
1836 # destroy existing XenAPI DPCI objects
1837 for dpci_uuid in XendDPCI.get_by_VM(self['uuid']):
1838 XendAPIStore.deregister(dpci_uuid, "DPCI")
1840 # create XenAPI DPCI objects.
1841 for pci_dev in pci_devs:
1842 dpci_uuid = pci_dev.get('uuid')
1843 ppci_uuid = XendPPCI.get_by_sbdf(pci_dev['domain'],
1844 pci_dev['bus'],
1845 pci_dev['slot'],
1846 pci_dev['func'])
1847 if ppci_uuid is None:
1848 continue
1849 dpci_record = {
1850 'VM': self['uuid'],
1851 'PPCI': ppci_uuid,
1852 'hotplug_slot': pci_dev.get('requested_vslot', 0)
1855 dpci_opts = pci_dev.get('opts')
1856 if dpci_opts and len(dpci_opts) > 0:
1857 dpci_record['options'] = dpci_opts
1859 XendDPCI(dpci_uuid, dpci_record)
1861 self['devices'][dev_uuid] = (dev_type,
1862 {'devs': pci_devs,
1863 'uuid': dev_uuid})
1864 return True
1866 if dev_type == 'vscsi': # Special case for vscsi
1867 vscsi_dict = self.vscsi_convert_sxp_to_dict(config)
1868 vscsi_devs = vscsi_dict['devs']
1869 vscsi_mode = vscsi_dict['feature-host']
1870 vscsi_be = vscsi_dict.get('backend', None)
1872 # destroy existing XenAPI DSCSI objects
1873 vscsi_devid = int(dev_info['devs'][0]['devid'])
1874 for dscsi_uuid in XendDSCSI.get_by_VM(self['uuid']):
1875 dscsi_inst = XendAPIStore.get(dscsi_uuid, 'DSCSI')
1876 if vscsi_devid == dscsi_inst.get_virtual_host():
1877 XendAPIStore.deregister(dscsi_uuid, "DSCSI")
1879 # create XenAPI DSCSI objects.
1880 for vscsi_dev in vscsi_devs:
1881 dscsi_uuid = vscsi_dev.get('uuid')
1882 pscsi_uuid = XendPSCSI.get_by_HCTL(vscsi_dev['p-dev'])
1883 if pscsi_uuid is None:
1884 continue
1885 dscsi_record = {
1886 'VM': self['uuid'],
1887 'PSCSI': pscsi_uuid,
1888 'virtual_HCTL': vscsi_dev.get('v-dev')
1890 XendDSCSI(dscsi_uuid, dscsi_record)
1892 vscsi_info = {
1893 'devs': vscsi_devs,
1894 'feature-host': vscsi_mode,
1895 'uuid': dev_uuid
1897 if vscsi_be is not None:
1898 vscsi_info['backend'] = vscsi_be
1899 self['devices'][dev_uuid] = (dev_type, vscsi_info)
1900 return True
1902 for opt_val in config[1:]:
1903 try:
1904 opt, val = opt_val
1905 dev_info[opt] = val
1906 except (TypeError, ValueError):
1907 pass # no value for this config option
1909 self['devices'][dev_uuid] = (dev_type, dev_info)
1910 return True
1912 elif dev_uuid in self['devices'] and cfg_xenapi:
1913 dev_type, dev_info = self['devices'][dev_uuid]
1914 for key, val in cfg_xenapi.items():
1915 dev_info[key] = val
1916 self['devices'][dev_uuid] = (dev_type, dev_info)
1917 return True
1919 return False
1922 def device_sxpr(self, dev_uuid = None, dev_type = None, dev_info = None, target = None):
1923 """Get Device SXPR by either giving the device UUID or (type, config).
1925 @rtype: list of lists
1926 @return: device config sxpr
1927 """
1928 sxpr = []
1930 if target == None:
1931 target = self
1933 if dev_uuid != None and dev_uuid in target['devices']:
1934 dev_type, dev_info = target['devices'][dev_uuid]
1936 if dev_type == None or dev_info == None:
1937 raise XendConfigError("Required either UUID or device type and "
1938 "configuration dictionary.")
1940 sxpr.append(dev_type)
1941 if dev_type in ('console', 'vfb'):
1942 config = [(opt, val) for opt, val in dev_info.items()
1943 if opt != 'other_config']
1944 else:
1945 config = [(opt, val) for opt, val in dev_info.items()]
1947 sxpr += config
1949 return sxpr
1951 def ordered_device_refs(self, target = None):
1952 result = []
1954 if target == None:
1955 target = self
1957 # vkbd devices *must* be before vfb devices, otherwise
1958 # there is a race condition when setting up devices
1959 # where the daemon spawned for the vfb may write stuff
1960 # into xenstore vkbd backend, before DevController has
1961 # setup permissions on the vkbd backend path. This race
1962 # results in domain creation failing with 'device already
1963 # connected' messages
1964 result.extend([u for u in target['devices'].keys() if target['devices'][u][0] == 'vkbd'])
1966 result.extend(target.get('console_refs', []) +
1967 target.get('vbd_refs', []) +
1968 target.get('vif_refs', []) +
1969 target.get('vtpm_refs', []))
1971 result.extend([u for u in target['devices'].keys() if u not in result])
1972 return result
1974 def all_devices_sxpr(self, target = None):
1975 """Returns the SXPR for all devices in the current configuration."""
1976 sxprs = []
1978 if target == None:
1979 target = self
1981 if 'devices' not in target:
1982 return sxprs
1984 ordered_refs = self.ordered_device_refs(target = target)
1985 for dev_uuid in ordered_refs:
1986 dev_type, dev_info = target['devices'][dev_uuid]
1987 if dev_type == 'pci' or dev_type == 'vscsi': # special case for pci devices
1988 if dev_type == 'pci':
1989 sxpr = ['pci', ['uuid', dev_info['uuid']]]
1990 elif dev_type == 'vscsi':
1991 sxpr = ['vscsi', ['uuid', dev_info['uuid']],
1992 ['feature-host', dev_info['feature-host']]]
1993 if dev_info.has_key('backend'):
1994 sxpr.append(['backend', dev_info['backend']])
1995 for pci_dev_info in dev_info['devs']:
1996 pci_dev_sxpr = ['dev']
1997 for opt, val in pci_dev_info.items():
1998 pci_dev_sxpr.append([opt, val])
1999 sxpr.append(pci_dev_sxpr)
2000 sxprs.append((dev_type, sxpr))
2001 else:
2002 sxpr = self.device_sxpr(dev_type = dev_type,
2003 dev_info = dev_info,
2004 target = target)
2005 sxprs.append((dev_type, sxpr))
2007 return sxprs
2009 def image_sxpr(self):
2010 """Returns a backwards compatible image SXP expression that is
2011 used in xenstore's /vm/<uuid>/image value and xm list."""
2012 image = [self.image_type()]
2013 if self.has_key('PV_kernel'):
2014 image.append(['kernel', self['PV_kernel']])
2015 if self.has_key('PV_ramdisk') and self['PV_ramdisk']:
2016 image.append(['ramdisk', self['PV_ramdisk']])
2017 if self.has_key('PV_args') and self['PV_args']:
2018 image.append(['args', self['PV_args']])
2019 if self.has_key('superpages'):
2020 image.append(['superpages', self['superpages']])
2022 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
2023 if key in self['platform']:
2024 image.append([key, self['platform'][key]])
2026 if 'notes' in self:
2027 image.append(self.notes_sxp(self['notes']))
2029 return image
2031 def update_with_image_sxp(self, image_sxp, bootloader = False):
2032 # Convert Legacy "image" config to Xen API PV_*
2033 # configuration
2034 log.debug("update_with_image_sxp(%s)" % scrub_password(image_sxp))
2036 # user-specified args must come last: previous releases did this and
2037 # some domU kernels rely upon the ordering.
2038 kernel_args = sxp.child_value(image_sxp, 'args', '')
2040 # attempt to extract extra arguments from SXP config
2041 arg_ip = sxp.child_value(image_sxp, 'ip')
2042 if arg_ip and not re.search(r'ip=[^ ]+', kernel_args):
2043 kernel_args = 'ip=%s ' % arg_ip + kernel_args
2044 arg_root = sxp.child_value(image_sxp, 'root')
2045 if arg_root and not re.search(r'root=', kernel_args):
2046 kernel_args = 'root=%s ' % arg_root + kernel_args
2048 if bootloader:
2049 self['_temp_using_bootloader'] = '1'
2050 self['_temp_kernel'] = sxp.child_value(image_sxp, 'kernel','')
2051 self['_temp_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
2052 self['_temp_args'] = kernel_args
2053 else:
2054 self['PV_kernel'] = sxp.child_value(image_sxp, 'kernel','')
2055 self['PV_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
2056 self['PV_args'] = kernel_args
2058 self['superpages'] = sxp.child_value(image_sxp, 'superpages',0)
2060 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
2061 val = sxp.child_value(image_sxp, key, None)
2062 if val is not None and val != '':
2063 self['platform'][key] = val
2065 notes = sxp.children(image_sxp, 'notes')
2066 if notes:
2067 self['notes'] = self.notes_from_sxp(notes[0])
2069 self._hvm_boot_params_from_sxp(image_sxp)
2071 def set_notes(self, notes):
2072 'Add parsed elfnotes to image'
2073 self['notes'] = notes
2075 def get_notes(self):
2076 try:
2077 return self['notes'] or {}
2078 except KeyError:
2079 return {}
2081 def notes_from_sxp(self, nsxp):
2082 notes = {}
2083 for note in sxp.children(nsxp):
2084 notes[note[0]] = note[1]
2085 return notes
2087 def notes_sxp(self, notes):
2088 nsxp = ['notes']
2089 for k, v in notes.iteritems():
2090 nsxp.append([k, str(v)])
2091 return nsxp
2093 def _hvm_boot_params_from_sxp(self, image_sxp):
2094 boot = sxp.child_value(image_sxp, 'boot', None)
2095 if boot is not None:
2096 self['HVM_boot_policy'] = 'BIOS order'
2097 self['HVM_boot_params'] = { 'order' : boot }
2099 def is_hvm(self):
2100 return self['HVM_boot_policy'] != ''
2102 def target(self):
2103 return self['target']
2105 def image_type(self):
2106 stored_type = self['platform'].get('image_type')
2107 return stored_type or (self.is_hvm() and 'hvm' or 'linux')
2109 def is_hap(self):
2110 return self['platform'].get('hap', 0)