debuggers.hg

view tools/python/xen/xend/XendConfig.py @ 20639:ab0d71f7f596

xend: Add keymap to vfb config for existing hvm guests

I submitted a patch a while back to add keymap to vfb config for hvm
guests. This patch works fine for new config (xm create|new) but not
existing, managed guests. To cover the latter case I've introduced a
validator method in XendConfig.

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