ia64/xen-unstable

view tools/python/xen/xend/XendConfig.py @ 16639:ca461349620a

xend: Fix PCI Device Configuration

Xend doesn't correctly work after restart, when there is a domU which
owns a pci device (driver domain). This patch fixes the problem.

Signed-off-by: Yosuke Iwamatsu <y-iwamatsu@ab.jp.nec.com>
author Keir Fraser <keir.fraser@citrix.com>
date Wed Dec 19 14:50:37 2007 +0000 (2007-12-19)
parents 5255eac35270
children e08c4cab65c8
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.XendError import VmError
28 from xen.xend.XendDevices import XendDevices
29 from xen.xend.PrettyPrint import prettyprintstring
30 from xen.xend.XendConstants import DOM_STATE_HALTED
31 from xen.xend.xenstore.xstransact import xstransact
32 from xen.xend.server.BlktapController import blktap_disk_types
33 from xen.xend.server.netif import randomMAC
34 from xen.util.blkif import blkdev_name_to_number
35 from xen.util import xsconstants
36 import xen.util.auxbin
38 log = logging.getLogger("xend.XendConfig")
39 log.setLevel(logging.WARN)
42 """
43 XendConfig API
45 XendConfig will try to mirror as closely the Xen API VM Struct
46 with extra parameters for those options that are not supported.
48 """
50 def reverse_dict(adict):
51 """Return the reverse mapping of a dictionary."""
52 return dict([(v, k) for k, v in adict.items()])
54 def bool0(v):
55 return v != '0' and v != 'False' and bool(v)
57 # Recursively copy a data struct, scrubbing out VNC passwords.
58 # Will scrub any dict entry with a key of 'vncpasswd' or any
59 # 2-element list whose first member is 'vncpasswd'. It will
60 # also scrub a string matching '(vncpasswd XYZ)'. Everything
61 # else is no-op passthrough
62 def scrub_password(data):
63 if type(data) == dict or type(data) == XendConfig:
64 scrubbed = {}
65 for key in data.keys():
66 if key == "vncpasswd":
67 scrubbed[key] = "XXXXXXXX"
68 else:
69 scrubbed[key] = scrub_password(data[key])
70 return scrubbed
71 elif type(data) == list:
72 if len(data) == 2 and type(data[0]) == str and data[0] == 'vncpasswd':
73 return ['vncpasswd', 'XXXXXXXX']
74 else:
75 scrubbed = []
76 for entry in data:
77 scrubbed.append(scrub_password(entry))
78 return scrubbed
79 elif type(data) == tuple:
80 scrubbed = []
81 for entry in data:
82 scrubbed.append(scrub_password(entry))
83 return tuple(scrubbed)
84 elif type(data) == str:
85 return re.sub(r'\(vncpasswd\s+[^\)]+\)','(vncpasswd XXXXXX)', data)
86 else:
87 return data
89 #
90 # CPU fields:
91 #
92 # VCPUs_max -- the maximum number of vcpus that this domain may ever have.
93 # aka XendDomainInfo.getVCpuCount().
94 # vcpus -- the legacy configuration name for above.
95 # max_vcpu_id -- vcpus_number - 1. This is given to us by Xen.
96 #
97 # cpus -- the list of pCPUs available to each vCPU.
98 #
99 # vcpu_avail -- a bitmap telling the guest domain whether it may use each of
100 # its VCPUs. This is translated to
101 # <dompath>/cpu/<id>/availability = {online,offline} for use
102 # by the guest domain.
103 # VCPUs_live -- the number of VCPUs currently up, as reported by Xen. This
104 # is changed by changing vcpu_avail, and waiting for the
105 # domain to respond.
106 #
109 # Mapping from XendConfig configuration keys to the old
110 # legacy configuration keys that map directly.
112 XENAPI_CFG_TO_LEGACY_CFG = {
113 'uuid': 'uuid',
114 'VCPUs_max': 'vcpus',
115 'cpus': 'cpus',
116 'name_label': 'name',
117 'actions_after_shutdown': 'on_poweroff',
118 'actions_after_reboot': 'on_reboot',
119 'actions_after_crash': 'on_crash',
120 'PV_bootloader': 'bootloader',
121 'PV_bootloader_args': 'bootloader_args',
122 }
124 LEGACY_CFG_TO_XENAPI_CFG = reverse_dict(XENAPI_CFG_TO_LEGACY_CFG)
126 # Platform configuration keys.
127 XENAPI_PLATFORM_CFG = [ 'acpi', 'apic', 'boot', 'device_model', 'display',
128 'fda', 'fdb', 'keymap', 'isa', 'localtime', 'monitor',
129 'nographic', 'pae', 'rtc_timeoffset', 'serial', 'sdl',
130 'soundhw','stdvga', 'usb', 'usbdevice', 'vnc',
131 'vncconsole', 'vncdisplay', 'vnclisten', 'timer_mode',
132 'vncpasswd', 'vncunused', 'xauthority', 'pci', 'vhpt',
133 'guest_os_type' ]
135 # Xen API console 'other_config' keys.
136 XENAPI_CONSOLE_OTHER_CFG = ['vncunused', 'vncdisplay', 'vnclisten',
137 'vncpasswd', 'type', 'display', 'xauthority',
138 'keymap']
140 # List of XendConfig configuration keys that have no direct equivalent
141 # in the old world.
143 XENAPI_CFG_TYPES = {
144 'uuid': str,
145 'name_label': str,
146 'name_description': str,
147 'user_version': str,
148 'is_a_template': bool0,
149 'resident_on': str,
150 'memory_static_min': int, # note these are stored in bytes, not KB!
151 'memory_static_max': int,
152 'memory_dynamic_min': int,
153 'memory_dynamic_max': int,
154 'cpus': list,
155 'vcpus_params': dict,
156 'VCPUs_max': int,
157 'VCPUs_at_startup': int,
158 'VCPUs_live': int,
159 'actions_after_shutdown': str,
160 'actions_after_reboot': str,
161 'actions_after_crash': str,
162 'PV_bootloader': str,
163 'PV_kernel': str,
164 'PV_ramdisk': str,
165 'PV_args': str,
166 'PV_bootloader_args': str,
167 'HVM_boot_policy': str,
168 'HVM_boot_params': dict,
169 'PCI_bus': str,
170 'platform': dict,
171 'tools_version': dict,
172 'other_config': dict,
173 'security_label': str,
174 'pci': str,
175 }
177 # List of legacy configuration keys that have no equivalent in the
178 # Xen API, but are still stored in XendConfig.
180 LEGACY_UNSUPPORTED_BY_XENAPI_CFG = [
181 # roundtripped (dynamic, unmodified)
182 'shadow_memory',
183 'vcpu_avail',
184 'features',
185 # read/write
186 'on_xend_start',
187 'on_xend_stop',
188 # read-only
189 'domid',
190 'start_time',
191 'cpu_time',
192 'online_vcpus',
193 # write-once
194 'cpu',
195 'cpus',
196 ]
198 LEGACY_CFG_TYPES = {
199 'uuid': str,
200 'name': str,
201 'vcpus': int,
202 'vcpu_avail': long,
203 'memory': int,
204 'shadow_memory': int,
205 'maxmem': int,
206 'start_time': float,
207 'cpu_time': float,
208 'features': str,
209 'localtime': int,
210 'name': str,
211 'on_poweroff': str,
212 'on_reboot': str,
213 'on_crash': str,
214 'on_xend_stop': str,
215 'on_xend_start': str,
216 'online_vcpus': int,
217 'rtc/timeoffset': str,
218 }
220 # Values that should be stored in xenstore's /vm/<uuid> that is used
221 # by Xend. Used in XendDomainInfo to restore running VM state from
222 # xenstore.
223 LEGACY_XENSTORE_VM_PARAMS = [
224 'uuid',
225 'name',
226 'vcpus',
227 'vcpu_avail',
228 'memory',
229 'shadow_memory',
230 'maxmem',
231 'start_time',
232 'name',
233 'on_poweroff',
234 'on_crash',
235 'on_reboot',
236 'on_xend_start',
237 'on_xend_stop',
238 ]
240 ##
241 ## Config Choices
242 ##
244 CONFIG_RESTART_MODES = ('restart', 'destroy', 'preserve', 'rename-restart')
245 CONFIG_OLD_DOM_STATES = ('running', 'blocked', 'paused', 'shutdown',
246 'crashed', 'dying')
248 class XendConfigError(VmError):
249 def __str__(self):
250 return 'Invalid Configuration: %s' % str(self.value)
252 ##
253 ## XendConfig Class (an extended dictionary)
254 ##
256 class XendConfig(dict):
257 """ The new Xend VM Configuration.
259 Stores the configuration in xenapi compatible format but retains
260 import and export functions for SXP.
261 """
262 def __init__(self, filename = None, sxp_obj = None,
263 xapi = None, dominfo = None):
265 dict.__init__(self)
266 self.update(self._defaults())
268 if filename:
269 try:
270 sxp_obj = sxp.parse(open(filename,'r'))
271 sxp_obj = sxp_obj[0]
272 except IOError, e:
273 raise XendConfigError("Unable to read file: %s" % filename)
275 if sxp_obj:
276 self._sxp_to_xapi(sxp_obj)
277 self._sxp_to_xapi_unsupported(sxp_obj)
278 elif xapi:
279 self.update_with_xenapi_config(xapi)
280 elif dominfo:
281 # output from xc.domain_getinfo
282 self._dominfo_to_xapi(dominfo, update_mem = True)
284 log.debug('XendConfig.init: %s' % scrub_password(self))
286 # validators go here
287 self.validate()
289 """ In time, we should enable this type checking addition. It is great
290 also for tracking bugs and unintended writes to XendDomainInfo.info
291 def __setitem__(self, key, value):
292 type_conv = XENAPI_CFG_TYPES.get(key)
293 if callable(type_conv):
294 try:
295 dict.__setitem__(self, key, type_conv(value))
296 except (ValueError, TypeError):
297 raise XendConfigError("Wrong type for configuration value " +
298 "%s. Expected %s" %
299 (key, type_conv.__name__))
300 else:
301 dict.__setitem__(self, key, value)
302 """
304 def _defaults(self):
305 defaults = {
306 'name_label': 'Domain-Unnamed',
307 'actions_after_shutdown': 'destroy',
308 'actions_after_reboot': 'restart',
309 'actions_after_crash': 'restart',
310 'actions_after_suspend': '',
311 'is_a_template': False,
312 'is_control_domain': False,
313 'features': '',
314 'PV_bootloader': '',
315 'PV_kernel': '',
316 'PV_ramdisk': '',
317 'PV_args': '',
318 'PV_bootloader_args': '',
319 'HVM_boot_policy': '',
320 'HVM_boot_params': {},
321 'memory_static_min': 0,
322 'memory_dynamic_min': 0,
323 'shadow_memory': 0,
324 'memory_static_max': 0,
325 'memory_dynamic_max': 0,
326 'devices': {},
327 'on_xend_start': 'ignore',
328 'on_xend_stop': 'ignore',
329 'cpus': [],
330 'VCPUs_max': 1,
331 'VCPUs_live': 1,
332 'VCPUs_at_startup': 1,
333 'vcpus_params': {},
334 'console_refs': [],
335 'vif_refs': [],
336 'vbd_refs': [],
337 'vtpm_refs': [],
338 'other_config': {},
339 'platform': {}
340 }
342 return defaults
344 #
345 # Here we assume these values exist in the dict.
346 # If they don't we have a bigger problem, lets not
347 # try and 'fix it up' but acutually fix the cause ;-)
348 #
349 def _memory_sanity_check(self):
350 log.trace("_memory_sanity_check memory_static_min: %s, "
351 "memory_static_max: %i, "
352 "memory_dynamic_min: %i, "
353 "memory_dynamic_max: %i",
354 self["memory_static_min"],
355 self["memory_static_max"],
356 self["memory_dynamic_min"],
357 self["memory_dynamic_max"])
359 if not self["memory_static_min"] <= self["memory_static_max"]:
360 raise XendConfigError("memory_static_min must be less " \
361 "than or equal to memory_static_max")
362 if not self["memory_static_min"] <= self["memory_dynamic_min"]:
363 raise XendConfigError("memory_static_min must be less " \
364 "than or equal to memory_dynamic_min")
365 if not self["memory_dynamic_max"] <= self["memory_static_max"]:
366 raise XendConfigError("memory_dynamic_max must be less " \
367 "than or equal to memory_static_max")
368 if not self["memory_dynamic_max"] > 0:
369 raise XendConfigError("memory_dynamic_max must be greater " \
370 "than zero")
371 if not self["memory_static_max"] > 0:
372 raise XendConfigError("memory_static_max must be greater " \
373 "than zero")
375 def _actions_sanity_check(self):
376 for event in ['shutdown', 'reboot', 'crash']:
377 if self['actions_after_' + event] not in CONFIG_RESTART_MODES:
378 raise XendConfigError('Invalid event handling mode: ' +
379 event)
381 def _vcpus_sanity_check(self):
382 if 'VCPUs_max' in self and 'vcpu_avail' not in self:
383 self['vcpu_avail'] = (1 << self['VCPUs_max']) - 1
385 def _uuid_sanity_check(self):
386 """Make sure UUID is in proper string format with hyphens."""
387 if 'uuid' not in self or not self['uuid']:
388 self['uuid'] = uuid.createString()
389 else:
390 self['uuid'] = uuid.toString(uuid.fromString(self['uuid']))
392 def _name_sanity_check(self):
393 if 'name_label' not in self:
394 self['name_label'] = 'Domain-' + self['uuid']
396 def _platform_sanity_check(self):
397 if 'keymap' not in self['platform'] and XendOptions.instance().get_keymap():
398 self['platform']['keymap'] = XendOptions.instance().get_keymap()
400 if self.is_hvm() or self.has_rfb():
401 if 'device_model' not in self['platform']:
402 self['platform']['device_model'] = xen.util.auxbin.pathTo("qemu-dm")
404 if self.is_hvm():
405 # Compatibility hack, can go away soon.
406 if 'soundhw' not in self['platform'] and \
407 self['platform'].get('enable_audio'):
408 self['platform']['soundhw'] = 'sb16'
410 def validate(self):
411 self._uuid_sanity_check()
412 self._name_sanity_check()
413 self._memory_sanity_check()
414 self._actions_sanity_check()
415 self._vcpus_sanity_check()
416 self._platform_sanity_check()
418 def _dominfo_to_xapi(self, dominfo, update_mem = False):
419 self['domid'] = dominfo['domid']
420 self['online_vcpus'] = dominfo['online_vcpus']
421 self['VCPUs_max'] = dominfo['max_vcpu_id'] + 1
423 if update_mem:
424 self['memory_dynamic_min'] = dominfo['mem_kb'] * 1024
425 self['memory_dynamic_max'] = dominfo['mem_kb'] * 1024
426 self['memory_static_min'] = 0
427 self['memory_static_max'] = dominfo['maxmem_kb'] * 1024
428 self._memory_sanity_check()
430 self['cpu_time'] = dominfo['cpu_time']/1e9
431 if dominfo.get('ssidref'):
432 ssidref = int(dominfo.get('ssidref'))
433 import xen.util.xsm.xsm as security
434 self['security_label'] = security.ssidref2security_label(ssidref)
436 self['shutdown_reason'] = dominfo['shutdown_reason']
438 # parse state into Xen API states
439 self['running'] = dominfo['running']
440 self['crashed'] = dominfo['crashed']
441 self['dying'] = dominfo['dying']
442 self['shutdown'] = dominfo['shutdown']
443 self['paused'] = dominfo['paused']
444 self['blocked'] = dominfo['blocked']
446 if 'name' in dominfo:
447 self['name_label'] = dominfo['name']
449 if 'handle' in dominfo:
450 self['uuid'] = uuid.toString(dominfo['handle'])
452 def _parse_sxp(self, sxp_cfg):
453 """ Populate this XendConfig using the parsed SXP.
455 @param sxp_cfg: Parsed SXP Configuration
456 @type sxp_cfg: list of lists
457 @rtype: dictionary
458 @return: A dictionary containing the parsed options of the SXP.
459 """
460 cfg = {}
462 for key, typ in XENAPI_CFG_TYPES.items():
463 val = sxp.child_value(sxp_cfg, key)
464 if val is not None:
465 try:
466 cfg[key] = typ(val)
467 except (ValueError, TypeError), e:
468 log.warn('Unable to convert type value for key: %s' % key)
470 # Convert deprecated options to current equivalents.
472 restart = sxp.child_value(sxp_cfg, 'restart')
473 if restart:
474 if restart == 'onreboot':
475 cfg['on_poweroff'] = 'destroy'
476 cfg['on_reboot'] = 'restart'
477 cfg['on_crash'] = 'destroy'
478 elif restart == 'always':
479 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
480 cfg[opt] = 'restart'
481 elif restart == 'never':
482 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
483 cfg[opt] = 'never'
484 else:
485 log.warn('Ignoring unrecognised value for deprecated option:'
486 'restart = \'%s\'', restart)
488 # Handle memory, passed in as MiB
490 if sxp.child_value(sxp_cfg, "memory") != None:
491 cfg["memory"] = int(sxp.child_value(sxp_cfg, "memory"))
492 if sxp.child_value(sxp_cfg, "maxmem") != None:
493 cfg["maxmem"] = int(sxp.child_value(sxp_cfg, "maxmem"))
495 # Convert scheduling parameters to vcpus_params
496 if 'vcpus_params' not in cfg:
497 cfg['vcpus_params'] = {}
498 cfg["vcpus_params"]["weight"] = \
499 int(sxp.child_value(sxp_cfg, "cpu_weight", 256))
500 cfg["vcpus_params"]["cap"] = \
501 int(sxp.child_value(sxp_cfg, "cpu_cap", 0))
503 # Only extract options we know about.
504 extract_keys = LEGACY_UNSUPPORTED_BY_XENAPI_CFG
505 extract_keys += XENAPI_CFG_TO_LEGACY_CFG.values()
507 for key in extract_keys:
508 val = sxp.child_value(sxp_cfg, key)
509 if val != None:
510 try:
511 cfg[key] = LEGACY_CFG_TYPES[key](val)
512 except KeyError:
513 cfg[key] = val
514 except (TypeError, ValueError), e:
515 log.warn("Unable to parse key %s: %s: %s" %
516 (key, str(val), e))
518 if 'platform' not in cfg:
519 cfg['platform'] = {}
520 localtime = sxp.child_value(sxp_cfg, 'localtime')
521 if localtime is not None:
522 cfg['platform']['localtime'] = localtime
524 # Compatibility hack -- can go soon.
525 for key in XENAPI_PLATFORM_CFG:
526 val = sxp.child_value(sxp_cfg, "platform_" + key, None)
527 if val is not None:
528 self['platform'][key] = val
530 # Compatibility hack -- can go soon.
531 boot_order = sxp.child_value(sxp_cfg, 'HVM_boot')
532 if boot_order:
533 cfg['HVM_boot_policy'] = 'BIOS order'
534 cfg['HVM_boot_params'] = { 'order' : boot_order }
537 # Parsing the device SXP's.
538 cfg['devices'] = {}
539 for dev in sxp.children(sxp_cfg, 'device'):
540 config = sxp.child0(dev)
541 dev_type = sxp.name(config)
542 self.device_add(dev_type, cfg_sxp = config, target = cfg)
544 # Extract missing data from configuration entries
545 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
546 if image_sxp:
547 image_vcpus = sxp.child_value(image_sxp, 'vcpus')
548 if image_vcpus != None:
549 try:
550 if 'VCPUs_max' not in cfg:
551 cfg['VCPUs_max'] = int(image_vcpus)
552 elif cfg['VCPUs_max'] != int(image_vcpus):
553 cfg['VCPUs_max'] = int(image_vcpus)
554 log.warn('Overriding vcpus from %d to %d using image'
555 'vcpus value.', cfg['VCPUs_max'])
556 except ValueError, e:
557 raise XendConfigError('integer expeceted: %s: %s' %
558 image_sxp, e)
560 # Deprecated cpu configuration
561 if 'cpu' in cfg:
562 if 'cpus' in cfg:
563 cfg['cpus'] = "%s,%s" % (str(cfg['cpu']), cfg['cpus'])
564 else:
565 cfg['cpus'] = str(cfg['cpu'])
567 # Convert 'cpus' to list of ints
568 if 'cpus' in cfg:
569 cpus = []
570 if type(cfg['cpus']) == list:
571 # If sxp_cfg was created from config.sxp,
572 # the form of 'cpus' is list of string.
573 # Convert 'cpus' to list of ints.
574 # ['1'] -> [1]
575 # ['0','2','3'] -> [0,2,3]
576 try:
577 for c in cfg['cpus']:
578 cpus.append(int(c))
580 cfg['cpus'] = cpus
581 except ValueError, e:
582 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
583 else:
584 # Convert 'cpus' string to list of ints
585 # 'cpus' supports a list of ranges (0-3),
586 # seperated by commas, and negation, (^1).
587 # Precedence is settled by order of the
588 # string:
589 # "0-3,^1" -> [0,2,3]
590 # "0-3,^1,1" -> [0,1,2,3]
591 try:
592 for c in cfg['cpus'].split(','):
593 if c.find('-') != -1:
594 (x, y) = c.split('-')
595 for i in range(int(x), int(y)+1):
596 cpus.append(int(i))
597 else:
598 # remove this element from the list
599 if c[0] == '^':
600 cpus = [x for x in cpus if x != int(c[1:])]
601 else:
602 cpus.append(int(c))
604 cfg['cpus'] = cpus
605 except ValueError, e:
606 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
608 import xen.util.xsm.xsm as security
609 if security.on():
610 from xen.util.acmpolicy import ACM_LABEL_UNLABELED
611 if not 'security' in cfg and sxp.child_value(sxp_cfg, 'security'):
612 cfg['security'] = sxp.child_value(sxp_cfg, 'security')
613 elif not cfg.get('security_label'):
614 cfg['security'] = [['access_control',
615 ['policy', security.get_active_policy_name() ],
616 ['label', ACM_LABEL_UNLABELED ]]]
618 if 'security' in cfg and not cfg.get('security_label'):
619 secinfo = cfg['security']
620 # The xm command sends a list formatted like this:
621 # [['access_control', ['policy', 'xm-test'],['label', 'red']],
622 # ['ssidref', 196611]]
623 policy = ""
624 label = ""
625 for idx in range(0, len(secinfo)):
626 if secinfo[idx][0] == "access_control":
627 for aidx in range(1, len(secinfo[idx])):
628 if secinfo[idx][aidx][0] == "policy":
629 policy = secinfo[idx][aidx][1]
630 if secinfo[idx][aidx][0] == "label":
631 label = secinfo[idx][aidx][1]
632 cfg['security_label'] = \
633 security.set_security_label(policy, label)
634 if not sxp.child_value(sxp_cfg, 'security_label'):
635 del cfg['security']
637 sec_lab = cfg['security_label'].split(":")
638 if len(sec_lab) != 3:
639 raise XendConfigError("Badly formatted security label: %s"
640 % cfg['security_label'])
642 old_state = sxp.child_value(sxp_cfg, 'state')
643 if old_state:
644 for i in range(len(CONFIG_OLD_DOM_STATES)):
645 cfg[CONFIG_OLD_DOM_STATES[i]] = int(old_state[i] != '-')
647 return cfg
650 def _sxp_to_xapi(self, sxp_cfg):
651 """Read in an SXP Configuration object and
652 populate at much of the Xen API with valid values.
653 """
654 log.debug('_sxp_to_xapi(%s)' % scrub_password(sxp_cfg))
656 cfg = self._parse_sxp(sxp_cfg)
658 for key, typ in XENAPI_CFG_TYPES.items():
659 val = cfg.get(key)
660 if val is not None:
661 self[key] = typ(val)
663 # Convert parameters that can be directly mapped from
664 # the Legacy Config to Xen API Config
666 for apikey, cfgkey in XENAPI_CFG_TO_LEGACY_CFG.items():
667 try:
668 type_conv = XENAPI_CFG_TYPES.get(apikey)
669 if callable(type_conv):
670 self[apikey] = type_conv(cfg[cfgkey])
671 else:
672 log.warn("Unconverted key: " + apikey)
673 self[apikey] = cfg[cfgkey]
674 except KeyError:
675 pass
677 # Lets try and handle memory correctly
679 MiB = 1024 * 1024
681 if "memory" in cfg:
682 self["memory_static_min"] = 0
683 self["memory_static_max"] = int(cfg["memory"]) * MiB
684 self["memory_dynamic_min"] = int(cfg["memory"]) * MiB
685 self["memory_dynamic_max"] = int(cfg["memory"]) * MiB
687 if "maxmem" in cfg:
688 self["memory_static_max"] = int(cfg["maxmem"]) * MiB
690 self._memory_sanity_check()
692 def update_with(n, o):
693 if not self.get(n):
694 self[n] = cfg.get(o, '')
696 update_with('PV_bootloader', 'bootloader')
697 update_with('PV_bootloader_args', 'bootloader_args')
699 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
700 if image_sxp:
701 self.update_with_image_sxp(image_sxp)
703 # Convert Legacy HVM parameters to Xen API configuration
704 for key in XENAPI_PLATFORM_CFG:
705 if key in cfg:
706 self['platform'][key] = cfg[key]
708 # set device references in the configuration
709 self['devices'] = cfg.get('devices', {})
710 self['console_refs'] = cfg.get('console_refs', [])
711 self['vif_refs'] = cfg.get('vif_refs', [])
712 self['vbd_refs'] = cfg.get('vbd_refs', [])
713 self['vtpm_refs'] = cfg.get('vtpm_refs', [])
715 # coalesce hvm vnc frame buffer with vfb config
716 if self.is_hvm() and int(self['platform'].get('vnc', 0)) != 0:
717 # add vfb device if it isn't there already
718 if not self.has_rfb():
719 dev_config = ['vfb']
720 dev_config.append(['type', 'vnc'])
721 # copy VNC related params from platform config to vfb dev conf
722 for key in ['vncpasswd', 'vncunused', 'vncdisplay',
723 'vnclisten']:
724 if key in self['platform']:
725 dev_config.append([key, self['platform'][key]])
727 self.device_add('vfb', cfg_sxp = dev_config)
730 def has_rfb(self):
731 for console_uuid in self['console_refs']:
732 if self['devices'][console_uuid][1].get('protocol') == 'rfb':
733 return True
734 if self['devices'][console_uuid][0] == 'vfb':
735 return True
736 return False
738 def _sxp_to_xapi_unsupported(self, sxp_cfg):
739 """Read in an SXP configuration object and populate
740 values are that not related directly supported in
741 the Xen API.
742 """
744 log.debug('_sxp_to_xapi_unsupported(%s)' % scrub_password(sxp_cfg))
746 # Parse and convert parameters used to configure
747 # the image (as well as HVM images)
748 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
749 if image_sxp:
750 image_type = sxp.name(image_sxp)
751 if image_type != 'hvm' and image_type != 'linux':
752 self['platform']['image_type'] = image_type
754 for key in XENAPI_PLATFORM_CFG:
755 val = sxp.child_value(image_sxp, key, None)
756 if val is not None and val != '':
757 self['platform'][key] = val
759 notes = sxp.children(image_sxp, 'notes')
760 if notes:
761 self['notes'] = self.notes_from_sxp(notes[0])
763 self._hvm_boot_params_from_sxp(image_sxp)
765 # extract backend value
767 backend = []
768 for c in sxp.children(sxp_cfg, 'backend'):
769 backend.append(sxp.name(sxp.child0(c)))
770 if backend:
771 self['backend'] = backend
773 # Parse and convert other Non Xen API parameters.
774 def _set_cfg_if_exists(sxp_arg):
775 val = sxp.child_value(sxp_cfg, sxp_arg)
776 if val != None:
777 if LEGACY_CFG_TYPES.get(sxp_arg):
778 self[sxp_arg] = LEGACY_CFG_TYPES[sxp_arg](val)
779 else:
780 self[sxp_arg] = val
782 _set_cfg_if_exists('shadow_memory')
783 _set_cfg_if_exists('features')
784 _set_cfg_if_exists('on_xend_stop')
785 _set_cfg_if_exists('on_xend_start')
786 _set_cfg_if_exists('vcpu_avail')
788 # Parse and store runtime configuration
789 _set_cfg_if_exists('start_time')
790 _set_cfg_if_exists('cpu_time')
791 _set_cfg_if_exists('shutdown_reason')
792 _set_cfg_if_exists('up_time')
793 _set_cfg_if_exists('status') # TODO, deprecated
795 def _get_old_state_string(self):
796 """Returns the old xm state string.
797 @rtype: string
798 @return: old state string
799 """
800 state_string = ''
801 for state_name in CONFIG_OLD_DOM_STATES:
802 on_off = self.get(state_name, 0)
803 if on_off:
804 state_string += state_name[0]
805 else:
806 state_string += '-'
808 return state_string
811 def update_config(self, dominfo):
812 """Update configuration with the output from xc.domain_getinfo().
814 @param dominfo: Domain information via xc.domain_getinfo()
815 @type dominfo: dict
816 """
817 self._dominfo_to_xapi(dominfo)
818 self.validate()
820 def update_with_xenapi_config(self, xapi):
821 """Update configuration with a Xen API VM struct
823 @param xapi: Xen API VM Struct
824 @type xapi: dict
825 """
827 log.debug('update_with_xenapi_config: %s' % scrub_password(xapi))
829 for key, val in xapi.items():
830 type_conv = XENAPI_CFG_TYPES.get(key)
831 if type_conv is None:
832 key = key.lower()
833 type_conv = XENAPI_CFG_TYPES.get(key)
834 if callable(type_conv):
835 self[key] = type_conv(val)
836 else:
837 self[key] = val
839 self['vcpus_params']['weight'] = \
840 int(self['vcpus_params'].get('weight', 256))
841 self['vcpus_params']['cap'] = int(self['vcpus_params'].get('cap', 0))
843 def to_sxp(self, domain = None, ignore_devices = False, ignore = [],
844 legacy_only = True):
845 """ Get SXP representation of this config object.
847 Incompat: removed store_mfn, console_mfn
849 @keyword domain: (optional) XendDomainInfo to get extra information
850 from such as domid and running devices.
851 @type domain: XendDomainInfo
852 @keyword ignore: (optional) list of 'keys' that we do not want
853 to export.
854 @type ignore: list of strings
855 @rtype: list of list (SXP representation)
856 """
857 sxpr = ['domain']
859 # TODO: domid/dom is the same thing but called differently
860 # depending if it is from xenstore or sxpr.
862 if domain.getDomid() is not None:
863 sxpr.append(['domid', domain.getDomid()])
865 if not legacy_only:
866 for name, typ in XENAPI_CFG_TYPES.items():
867 if name in self and self[name] not in (None, []):
868 if typ == dict:
869 s = self[name].items()
870 elif typ == list:
871 s = self[name]
872 else:
873 s = str(self[name])
874 sxpr.append([name, s])
876 for xenapi, legacy in XENAPI_CFG_TO_LEGACY_CFG.items():
877 if legacy in ('cpus'): # skip this
878 continue
879 if self.has_key(xenapi) and self[xenapi] not in (None, []):
880 if type(self[xenapi]) == bool:
881 # convert booleans to ints before making an sxp item
882 sxpr.append([legacy, int(self[xenapi])])
883 else:
884 sxpr.append([legacy, self[xenapi]])
886 MiB = 1024*1024
888 sxpr.append(["maxmem", int(self["memory_static_max"])/MiB])
889 sxpr.append(["memory", int(self["memory_dynamic_max"])/MiB])
891 for legacy in LEGACY_UNSUPPORTED_BY_XENAPI_CFG:
892 if legacy in ('domid', 'uuid', 'cpus'): # skip these
893 continue
894 if self.has_key(legacy) and self[legacy] not in (None, []):
895 sxpr.append([legacy, self[legacy]])
897 if self.has_key('security_label'):
898 sxpr.append(['security_label', self['security_label']])
900 sxpr.append(['image', self.image_sxpr()])
901 sxpr.append(['status', domain._stateGet()])
903 if domain.getDomid() is not None:
904 sxpr.append(['state', self._get_old_state_string()])
906 if domain:
907 if domain.store_mfn:
908 sxpr.append(['store_mfn', domain.store_mfn])
909 if domain.console_mfn:
910 sxpr.append(['console_mfn', domain.console_mfn])
913 # Marshall devices (running or from configuration)
914 if not ignore_devices:
915 txn = xstransact()
916 try:
917 for cls in XendDevices.valid_devices():
918 found = False
920 # figure if there is a dev controller is valid and running
921 if domain and domain.getDomid() != None:
922 try:
923 controller = domain.getDeviceController(cls)
924 configs = controller.configurations(txn)
925 for config in configs:
926 if sxp.name(config) in ('vbd', 'tap'):
927 # The bootable flag is never written to the
928 # store as part of the device config.
929 dev_uuid = sxp.child_value(config, 'uuid')
930 dev_type, dev_cfg = self['devices'][dev_uuid]
931 is_bootable = dev_cfg.get('bootable', 0)
932 config.append(['bootable', int(is_bootable)])
934 sxpr.append(['device', config])
936 found = True
937 except:
938 log.exception("dumping sxp from device controllers")
939 pass
941 # if we didn't find that device, check the existing config
942 # for a device in the same class
943 if not found:
944 for dev_type, dev_info in self.all_devices_sxpr():
945 if dev_type == cls:
946 sxpr.append(['device', dev_info])
948 txn.commit()
949 except:
950 txn.abort()
951 raise
953 return sxpr
955 def _blkdev_name_to_number(self, dev):
956 if 'ioemu:' in dev:
957 _, dev = dev.split(':', 1)
958 try:
959 dev, _ = dev.split(':', 1)
960 except ValueError:
961 pass
963 try:
964 devid = int(dev)
965 except ValueError:
966 # devid is not a number but a string containing either device
967 # name (e.g. xvda) or device_type/device_id (e.g. vbd/51728)
968 dev2 = type(dev) is str and dev.split('/')[-1] or None
969 if dev2 == None:
970 log.debug("Could not check the device %s", dev)
971 return None
972 try:
973 devid = int(dev2)
974 except ValueError:
975 devid = blkdev_name_to_number(dev2)
976 if devid == None:
977 log.debug("The device %s is not device name", dev2)
978 return None
979 return devid
981 def device_duplicate_check(self, dev_type, dev_info, defined_config):
982 defined_devices_sxpr = self.all_devices_sxpr(target = defined_config)
984 if dev_type == 'vbd':
985 dev_uname = dev_info.get('uname')
986 blkdev_name = dev_info.get('dev')
987 devid = self._blkdev_name_to_number(blkdev_name)
988 if devid == None:
989 return
991 for o_dev_type, o_dev_info in defined_devices_sxpr:
992 if dev_type == o_dev_type:
993 if dev_uname == sxp.child_value(o_dev_info, 'uname'):
994 raise XendConfigError('The uname "%s" is already defined' %
995 dev_uname)
996 o_blkdev_name = sxp.child_value(o_dev_info, 'dev')
997 o_devid = self._blkdev_name_to_number(o_blkdev_name)
998 if o_devid != None and devid == o_devid:
999 raise XendConfigError('The device "%s" is already defined' %
1000 blkdev_name)
1002 elif dev_type == 'vif':
1003 dev_mac = dev_info.get('mac')
1005 for o_dev_type, o_dev_info in defined_devices_sxpr:
1006 if dev_type == o_dev_type:
1007 if dev_mac == sxp.child_value(o_dev_info, 'mac'):
1008 raise XendConfigError('The mac "%s" is already defined' %
1009 dev_mac)
1011 def device_add(self, dev_type, cfg_sxp = None, cfg_xenapi = None,
1012 target = None):
1013 """Add a device configuration in SXP format or XenAPI struct format.
1015 For SXP, it could be either:
1017 [device, [vbd, [uname ...]]
1019 or:
1021 [vbd, [uname ..]]
1023 @type cfg_sxp: list of lists (parsed sxp object)
1024 @param cfg_sxp: SXP configuration object
1025 @type cfg_xenapi: dict
1026 @param cfg_xenapi: A device configuration from Xen API (eg. vbd,vif)
1027 @param target: write device information to
1028 @type target: None or a dictionary
1029 @rtype: string
1030 @return: Assigned UUID of the device.
1031 """
1032 if target == None:
1033 target = self
1035 if dev_type not in XendDevices.valid_devices():
1036 raise XendConfigError("XendConfig: %s not a valid device type" %
1037 dev_type)
1039 if cfg_sxp == None and cfg_xenapi == None:
1040 raise XendConfigError("XendConfig: device_add requires some "
1041 "config.")
1043 #if cfg_sxp:
1044 # log.debug("XendConfig.device_add: %s" % str(cfg_sxp))
1045 #if cfg_xenapi:
1046 # log.debug("XendConfig.device_add: %s" % str(cfg_xenapi))
1048 if cfg_sxp:
1049 if sxp.child0(cfg_sxp) == 'device':
1050 config = sxp.child0(cfg_sxp)
1051 else:
1052 config = cfg_sxp
1054 dev_type = sxp.name(config)
1055 dev_info = {}
1057 # Parsing the device SXP's. In most cases, the SXP looks
1058 # like this:
1060 # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]]
1062 # However, for PCI devices it looks like this:
1064 # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1]]]]
1066 # It seems the reasoning for this difference is because
1067 # pciif.py needs all the PCI device configurations at
1068 # the same time when creating the devices.
1070 # To further complicate matters, Xen 2.0 configuration format
1071 # uses the following for pci device configuration:
1073 # [device, [pci, [domain, 0], [bus, 0], [dev, 1], [func, 2]]]
1075 if dev_type == 'pci':
1076 pci_devs_uuid = sxp.child_value(config, 'uuid',
1077 uuid.createString())
1078 pci_devs = []
1079 for pci_dev in sxp.children(config, 'dev'):
1080 pci_dev_info = {}
1081 for opt_val in pci_dev[1:]:
1082 try:
1083 opt, val = opt_val
1084 pci_dev_info[opt] = val
1085 except TypeError:
1086 pass
1087 pci_devs.append(pci_dev_info)
1088 target['devices'][pci_devs_uuid] = (dev_type,
1089 {'devs': pci_devs,
1090 'uuid': pci_devs_uuid})
1092 log.debug("XendConfig: reading device: %s" % pci_devs)
1093 return pci_devs_uuid
1095 for opt_val in config[1:]:
1096 try:
1097 opt, val = opt_val
1098 dev_info[opt] = val
1099 except (TypeError, ValueError): # unpack error
1100 pass
1102 if dev_type == 'vbd':
1103 dev_info['bootable'] = 0
1104 if dev_info.get('dev', '').startswith('ioemu:'):
1105 dev_info['driver'] = 'ioemu'
1106 else:
1107 dev_info['driver'] = 'paravirtualised'
1109 if dev_type == 'tap':
1110 if dev_info['uname'].split(':')[1] not in blktap_disk_types:
1111 raise XendConfigError("tap:%s not a valid disk type" %
1112 dev_info['uname'].split(':')[1])
1114 if dev_type == 'vif':
1115 if not dev_info.get('mac'):
1116 dev_info['mac'] = randomMAC()
1118 self.device_duplicate_check(dev_type, dev_info, target)
1120 if dev_type == 'vif':
1121 if dev_info.get('policy') and dev_info.get('label'):
1122 dev_info['security_label'] = "%s:%s:%s" % \
1123 (xsconstants.ACM_POLICY_ID,
1124 dev_info['policy'],dev_info['label'])
1126 # create uuid if it doesn't exist
1127 dev_uuid = dev_info.get('uuid', None)
1128 if not dev_uuid:
1129 dev_uuid = uuid.createString()
1130 dev_info['uuid'] = dev_uuid
1132 # store dev references by uuid for certain device types
1133 target['devices'][dev_uuid] = (dev_type, dev_info)
1134 if dev_type in ('vif', 'vbd', 'vtpm'):
1135 param = '%s_refs' % dev_type
1136 if param not in target:
1137 target[param] = []
1138 if dev_uuid not in target[param]:
1139 if dev_type == 'vbd':
1140 # Compat hack -- mark first disk bootable
1141 dev_info['bootable'] = int(not target[param])
1142 target[param].append(dev_uuid)
1143 elif dev_type == 'tap':
1144 if 'vbd_refs' not in target:
1145 target['vbd_refs'] = []
1146 if dev_uuid not in target['vbd_refs']:
1147 # Compat hack -- mark first disk bootable
1148 dev_info['bootable'] = int(not target['vbd_refs'])
1149 target['vbd_refs'].append(dev_uuid)
1151 elif dev_type == 'vfb':
1152 # Populate other config with aux data that is associated
1153 # with vfb
1155 other_config = {}
1156 for key in XENAPI_CONSOLE_OTHER_CFG:
1157 if key in dev_info:
1158 other_config[key] = dev_info[key]
1159 target['devices'][dev_uuid][1]['other_config'] = other_config
1162 if 'console_refs' not in target:
1163 target['console_refs'] = []
1165 # Treat VFB devices as console devices so they are found
1166 # through Xen API
1167 if dev_uuid not in target['console_refs']:
1168 target['console_refs'].append(dev_uuid)
1170 elif dev_type == 'console':
1171 if 'console_refs' not in target:
1172 target['console_refs'] = []
1173 if dev_uuid not in target['console_refs']:
1174 target['console_refs'].append(dev_uuid)
1176 log.debug("XendConfig: reading device: %s" % scrub_password(dev_info))
1177 return dev_uuid
1179 if cfg_xenapi:
1180 dev_info = {}
1181 dev_uuid = ''
1182 if dev_type == 'vif':
1183 dev_info['mac'] = cfg_xenapi.get('MAC')
1184 if not dev_info['mac']:
1185 dev_info['mac'] = randomMAC()
1186 # vifname is the name on the guest, not dom0
1187 # TODO: we don't have the ability to find that out or
1188 # change it from dom0
1189 #if cfg_xenapi.get('device'): # don't add if blank
1190 # dev_info['vifname'] = cfg_xenapi.get('device')
1191 if cfg_xenapi.get('type'):
1192 dev_info['type'] = cfg_xenapi.get('type')
1193 if cfg_xenapi.get('name'):
1194 dev_info['name'] = cfg_xenapi.get('name')
1195 if cfg_xenapi.get('network'):
1196 network = XendAPIStore.get(
1197 cfg_xenapi.get('network'), 'network')
1198 dev_info['bridge'] = network.get_name_label()
1200 if cfg_xenapi.get('security_label'):
1201 dev_info['security_label'] = \
1202 cfg_xenapi.get('security_label')
1204 dev_uuid = cfg_xenapi.get('uuid', None)
1205 if not dev_uuid:
1206 dev_uuid = uuid.createString()
1207 dev_info['uuid'] = dev_uuid
1208 target['devices'][dev_uuid] = (dev_type, dev_info)
1209 target['vif_refs'].append(dev_uuid)
1211 elif dev_type in ('vbd', 'tap'):
1212 dev_info['type'] = cfg_xenapi.get('type', 'Disk')
1213 if dev_info['type'] == 'CD':
1214 old_vbd_type = 'cdrom'
1215 else:
1216 old_vbd_type = 'disk'
1218 dev_info['uname'] = cfg_xenapi.get('image', '')
1219 dev_info['dev'] = '%s:%s' % (cfg_xenapi.get('device'),
1220 old_vbd_type)
1221 dev_info['bootable'] = int(cfg_xenapi.get('bootable', 0))
1222 dev_info['driver'] = cfg_xenapi.get('driver', '')
1223 dev_info['VDI'] = cfg_xenapi.get('VDI', '')
1225 if cfg_xenapi.get('mode') == 'RW':
1226 dev_info['mode'] = 'w'
1227 else:
1228 dev_info['mode'] = 'r'
1230 dev_uuid = cfg_xenapi.get('uuid', None)
1231 if not dev_uuid:
1232 dev_uuid = uuid.createString()
1233 dev_info['uuid'] = dev_uuid
1234 target['devices'][dev_uuid] = (dev_type, dev_info)
1235 target['vbd_refs'].append(dev_uuid)
1237 elif dev_type == 'vtpm':
1238 if cfg_xenapi.get('type'):
1239 dev_info['type'] = cfg_xenapi.get('type')
1241 dev_uuid = cfg_xenapi.get('uuid', None)
1242 if not dev_uuid:
1243 dev_uuid = uuid.createString()
1244 dev_info['uuid'] = dev_uuid
1245 dev_info['other_config'] = cfg_xenapi.get('other_config', {})
1246 target['devices'][dev_uuid] = (dev_type, dev_info)
1247 target['vtpm_refs'].append(dev_uuid)
1249 elif dev_type == 'console':
1250 dev_uuid = cfg_xenapi.get('uuid', None)
1251 if not dev_uuid:
1252 dev_uuid = uuid.createString()
1253 dev_info['uuid'] = dev_uuid
1254 dev_info['protocol'] = cfg_xenapi.get('protocol', 'rfb')
1255 dev_info['other_config'] = cfg_xenapi.get('other_config', {})
1256 if dev_info['protocol'] == 'rfb':
1257 # collapse other config into devinfo for things
1258 # such as vncpasswd, vncunused, etc.
1259 dev_info.update(cfg_xenapi.get('other_config', {}))
1260 dev_info['type'] = 'vnc'
1261 target['devices'][dev_uuid] = ('vfb', dev_info)
1262 target['console_refs'].append(dev_uuid)
1264 # Finally, if we are a pvfb, we need to make a vkbd
1265 # as well that is not really exposed to Xen API
1266 vkbd_uuid = uuid.createString()
1267 target['devices'][vkbd_uuid] = ('vkbd', {})
1269 elif dev_info['protocol'] == 'vt100':
1270 # if someone tries to create a VT100 console
1271 # via the Xen API, we'll have to ignore it
1272 # because we create one automatically in
1273 # XendDomainInfo._update_consoles
1274 raise XendConfigError('Creating vt100 consoles via '
1275 'Xen API is unsupported')
1277 return dev_uuid
1279 # no valid device to add
1280 return ''
1282 def phantom_device_add(self, dev_type, cfg_xenapi = None,
1283 target = None):
1284 """Add a phantom tap device configuration in XenAPI struct format.
1285 """
1287 if target == None:
1288 target = self
1290 if dev_type not in XendDevices.valid_devices() and \
1291 dev_type not in XendDevices.pseudo_devices():
1292 raise XendConfigError("XendConfig: %s not a valid device type" %
1293 dev_type)
1295 if cfg_xenapi == None:
1296 raise XendConfigError("XendConfig: device_add requires some "
1297 "config.")
1299 if cfg_xenapi:
1300 log.debug("XendConfig.phantom_device_add: %s" % str(cfg_xenapi))
1302 if cfg_xenapi:
1303 dev_info = {}
1304 if dev_type in ('vbd', 'tap'):
1305 if dev_type == 'vbd':
1306 dev_info['uname'] = cfg_xenapi.get('image', '')
1307 dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
1308 elif dev_type == 'tap':
1309 if cfg_xenapi.get('image').find('tap:') == -1:
1310 dev_info['uname'] = 'tap:qcow:%s' % cfg_xenapi.get('image')
1311 dev_info['dev'] = '/dev/%s' % cfg_xenapi.get('device')
1312 dev_info['uname'] = cfg_xenapi.get('image')
1313 dev_info['mode'] = cfg_xenapi.get('mode')
1314 dev_info['backend'] = '0'
1315 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
1316 dev_info['uuid'] = dev_uuid
1317 self['devices'][dev_uuid] = (dev_type, dev_info)
1318 self['vbd_refs'].append(dev_uuid)
1319 return dev_uuid
1321 return ''
1323 def console_add(self, protocol, location, other_config = {}):
1324 dev_uuid = uuid.createString()
1325 if protocol == 'vt100':
1326 dev_info = {
1327 'uuid': dev_uuid,
1328 'protocol': protocol,
1329 'location': location,
1330 'other_config': other_config,
1333 if 'devices' not in self:
1334 self['devices'] = {}
1336 self['devices'][dev_uuid] = ('console', dev_info)
1337 self['console_refs'].append(dev_uuid)
1338 return dev_info
1340 return {}
1342 def console_update(self, console_uuid, key, value):
1343 for dev_uuid, (dev_type, dev_info) in self['devices'].items():
1344 if dev_uuid == console_uuid:
1345 dev_info[key] = value
1346 # collapse other_config into dev_info for things
1347 # such as vncpasswd, vncunused, etc.
1348 if key == 'other_config':
1349 for k in XENAPI_CONSOLE_OTHER_CFG:
1350 if k in dev_info and k not in value:
1351 del dev_info[k]
1352 dev_info.update(value)
1353 break
1355 def console_get_all(self, protocol):
1356 if protocol == 'vt100':
1357 consoles = [dinfo for dtype, dinfo in self['devices'].values()
1358 if dtype == 'console']
1359 return [c for c in consoles if c.get('protocol') == protocol]
1361 elif protocol == 'rfb':
1362 vfbs = [dinfo for dtype, dinfo in self['devices'].values()
1363 if dtype == 'vfb']
1365 # move all non-console key values to other_config before
1366 # returning console config
1367 valid_keys = ['uuid', 'location']
1368 for vfb in vfbs:
1369 other_config = {}
1370 for key, val in vfb.items():
1371 if key not in valid_keys:
1372 other_config[key] = vfb[key]
1373 del vfb[key]
1374 vfb['other_config'] = other_config
1375 vfb['protocol'] = 'rfb'
1377 return vfbs
1379 else:
1380 return []
1382 def device_update(self, dev_uuid, cfg_sxp = [], cfg_xenapi = {}):
1383 """Update an existing device with the new configuration.
1385 @rtype: boolean
1386 @return: Returns True if succesfully found and updated a device conf
1387 """
1388 if dev_uuid in self['devices'] and cfg_sxp:
1389 if sxp.child0(cfg_sxp) == 'device':
1390 config = sxp.child0(cfg_sxp)
1391 else:
1392 config = cfg_sxp
1394 dev_type, dev_info = self['devices'][dev_uuid]
1395 for opt_val in config[1:]:
1396 try:
1397 opt, val = opt_val
1398 dev_info[opt] = val
1399 except (TypeError, ValueError):
1400 pass # no value for this config option
1402 self['devices'][dev_uuid] = (dev_type, dev_info)
1403 return True
1405 elif dev_uuid in self['devices'] and cfg_xenapi:
1406 dev_type, dev_info = self['devices'][dev_uuid]
1407 for key, val in cfg_xenapi.items():
1408 dev_info[key] = val
1409 self['devices'][dev_uuid] = (dev_type, dev_info)
1411 return False
1414 def device_sxpr(self, dev_uuid = None, dev_type = None, dev_info = None, target = None):
1415 """Get Device SXPR by either giving the device UUID or (type, config).
1417 @rtype: list of lists
1418 @return: device config sxpr
1419 """
1420 sxpr = []
1422 if target == None:
1423 target = self
1425 if dev_uuid != None and dev_uuid in target['devices']:
1426 dev_type, dev_info = target['devices'][dev_uuid]
1428 if dev_type == None or dev_info == None:
1429 raise XendConfigError("Required either UUID or device type and "
1430 "configuration dictionary.")
1432 sxpr.append(dev_type)
1433 if dev_type in ('console', 'vfb'):
1434 config = [(opt, val) for opt, val in dev_info.items()
1435 if opt != 'other_config']
1436 else:
1437 config = [(opt, val) for opt, val in dev_info.items()]
1439 sxpr += config
1441 return sxpr
1443 def ordered_device_refs(self, target = None):
1444 result = []
1446 if target == None:
1447 target = self
1449 # vkbd devices *must* be before vfb devices, otherwise
1450 # there is a race condition when setting up devices
1451 # where the daemon spawned for the vfb may write stuff
1452 # into xenstore vkbd backend, before DevController has
1453 # setup permissions on the vkbd backend path. This race
1454 # results in domain creation failing with 'device already
1455 # connected' messages
1456 result.extend([u for u in target['devices'].keys() if target['devices'][u][0] == 'vkbd'])
1458 result.extend(target.get('console_refs', []) +
1459 target.get('vbd_refs', []) +
1460 target.get('vif_refs', []) +
1461 target.get('vtpm_refs', []))
1463 result.extend([u for u in target['devices'].keys() if u not in result])
1464 return result
1466 def all_devices_sxpr(self, target = None):
1467 """Returns the SXPR for all devices in the current configuration."""
1468 sxprs = []
1469 pci_devs = []
1471 if target == None:
1472 target = self
1474 if 'devices' not in target:
1475 return sxprs
1477 ordered_refs = self.ordered_device_refs(target = target)
1478 for dev_uuid in ordered_refs:
1479 dev_type, dev_info = target['devices'][dev_uuid]
1480 if dev_type == 'pci': # special case for pci devices
1481 sxpr = ['pci', ['uuid', dev_info['uuid']]]
1482 for pci_dev_info in dev_info['devs']:
1483 pci_dev_sxpr = ['dev']
1484 for opt, val in pci_dev_info.items():
1485 pci_dev_sxpr.append([opt, val])
1486 sxpr.append(pci_dev_sxpr)
1487 sxprs.append((dev_type, sxpr))
1488 else:
1489 sxpr = self.device_sxpr(dev_type = dev_type,
1490 dev_info = dev_info,
1491 target = target)
1492 sxprs.append((dev_type, sxpr))
1494 return sxprs
1496 def image_sxpr(self):
1497 """Returns a backwards compatible image SXP expression that is
1498 used in xenstore's /vm/<uuid>/image value and xm list."""
1499 image = [self.image_type()]
1500 if self.has_key('PV_kernel'):
1501 image.append(['kernel', self['PV_kernel']])
1502 if self.has_key('PV_ramdisk') and self['PV_ramdisk']:
1503 image.append(['ramdisk', self['PV_ramdisk']])
1504 if self.has_key('PV_args') and self['PV_args']:
1505 image.append(['args', self['PV_args']])
1507 for key in XENAPI_PLATFORM_CFG:
1508 if key in self['platform']:
1509 image.append([key, self['platform'][key]])
1511 if 'notes' in self:
1512 image.append(self.notes_sxp(self['notes']))
1514 return image
1516 def update_with_image_sxp(self, image_sxp, bootloader = False):
1517 # Convert Legacy "image" config to Xen API PV_*
1518 # configuration
1519 log.debug("update_with_image_sxp(%s)" % scrub_password(image_sxp))
1521 # user-specified args must come last: previous releases did this and
1522 # some domU kernels rely upon the ordering.
1523 kernel_args = sxp.child_value(image_sxp, 'args', '')
1525 # attempt to extract extra arguments from SXP config
1526 arg_ip = sxp.child_value(image_sxp, 'ip')
1527 if arg_ip and not re.search(r'ip=[^ ]+', kernel_args):
1528 kernel_args = 'ip=%s ' % arg_ip + kernel_args
1529 arg_root = sxp.child_value(image_sxp, 'root')
1530 if arg_root and not re.search(r'root=', kernel_args):
1531 kernel_args = 'root=%s ' % arg_root + kernel_args
1533 if bootloader:
1534 self['_temp_using_bootloader'] = '1'
1535 self['_temp_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1536 self['_temp_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1537 self['_temp_args'] = kernel_args
1538 else:
1539 self['PV_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1540 self['PV_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1541 self['PV_args'] = kernel_args
1543 for key in XENAPI_PLATFORM_CFG:
1544 val = sxp.child_value(image_sxp, key, None)
1545 if val is not None and val != '':
1546 self['platform'][key] = val
1548 notes = sxp.children(image_sxp, 'notes')
1549 if notes:
1550 self['notes'] = self.notes_from_sxp(notes[0])
1552 self._hvm_boot_params_from_sxp(image_sxp)
1554 def set_notes(self, notes):
1555 'Add parsed elfnotes to image'
1556 self['notes'] = notes
1558 def get_notes(self):
1559 try:
1560 return self['notes'] or {}
1561 except KeyError:
1562 return {}
1564 def notes_from_sxp(self, nsxp):
1565 notes = {}
1566 for note in sxp.children(nsxp):
1567 notes[note[0]] = note[1]
1568 return notes
1570 def notes_sxp(self, notes):
1571 nsxp = ['notes']
1572 for k, v in notes.iteritems():
1573 nsxp.append([k, str(v)])
1574 return nsxp
1576 def _hvm_boot_params_from_sxp(self, image_sxp):
1577 boot = sxp.child_value(image_sxp, 'boot', None)
1578 if boot is not None:
1579 self['HVM_boot_policy'] = 'BIOS order'
1580 self['HVM_boot_params'] = { 'order' : boot }
1582 def is_hvm(self):
1583 return self['HVM_boot_policy'] != ''
1585 def image_type(self):
1586 stored_type = self['platform'].get('image_type')
1587 return stored_type or (self.is_hvm() and 'hvm' or 'linux')