ia64/xen-unstable

view tools/python/xen/xend/XendConfig.py @ 16640:e08c4cab65c8

xend: Fix device duplicate check.

Signed-off-by: Yosuke Iwamatsu <y-iwamatsu@ab.jp.nec.com>
author Keir Fraser <keir.fraser@citrix.com>
date Wed Dec 19 14:51:02 2007 +0000 (2007-12-19)
parents ca461349620a
children 741ecee8ccc0
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, blkdev_uname_to_file
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' or dev_type == 'tap':
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 o_dev_type == 'vbd' or o_dev_type == 'tap':
993 blkdev_file = blkdev_uname_to_file(dev_uname)
994 o_dev_uname = sxp.child_value(o_dev_info, 'uname')
995 o_blkdev_file = blkdev_uname_to_file(o_dev_uname)
996 if blkdev_file == o_blkdev_file:
997 raise XendConfigError('The file "%s" is already used' %
998 blkdev_file)
999 o_blkdev_name = sxp.child_value(o_dev_info, 'dev')
1000 o_devid = self._blkdev_name_to_number(o_blkdev_name)
1001 if o_devid != None and devid == o_devid:
1002 raise XendConfigError('The device "%s" is already defined' %
1003 blkdev_name)
1005 elif dev_type == 'vif':
1006 dev_mac = dev_info.get('mac')
1008 for o_dev_type, o_dev_info in defined_devices_sxpr:
1009 if dev_type == o_dev_type:
1010 if dev_mac.lower() == sxp.child_value(o_dev_info, 'mac').lower():
1011 raise XendConfigError('The mac "%s" is already defined' %
1012 dev_mac)
1014 def device_add(self, dev_type, cfg_sxp = None, cfg_xenapi = None,
1015 target = None):
1016 """Add a device configuration in SXP format or XenAPI struct format.
1018 For SXP, it could be either:
1020 [device, [vbd, [uname ...]]
1022 or:
1024 [vbd, [uname ..]]
1026 @type cfg_sxp: list of lists (parsed sxp object)
1027 @param cfg_sxp: SXP configuration object
1028 @type cfg_xenapi: dict
1029 @param cfg_xenapi: A device configuration from Xen API (eg. vbd,vif)
1030 @param target: write device information to
1031 @type target: None or a dictionary
1032 @rtype: string
1033 @return: Assigned UUID of the device.
1034 """
1035 if target == None:
1036 target = self
1038 if dev_type not in XendDevices.valid_devices():
1039 raise XendConfigError("XendConfig: %s not a valid device type" %
1040 dev_type)
1042 if cfg_sxp == None and cfg_xenapi == None:
1043 raise XendConfigError("XendConfig: device_add requires some "
1044 "config.")
1046 #if cfg_sxp:
1047 # log.debug("XendConfig.device_add: %s" % str(cfg_sxp))
1048 #if cfg_xenapi:
1049 # log.debug("XendConfig.device_add: %s" % str(cfg_xenapi))
1051 if cfg_sxp:
1052 if sxp.child0(cfg_sxp) == 'device':
1053 config = sxp.child0(cfg_sxp)
1054 else:
1055 config = cfg_sxp
1057 dev_type = sxp.name(config)
1058 dev_info = {}
1060 # Parsing the device SXP's. In most cases, the SXP looks
1061 # like this:
1063 # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]]
1065 # However, for PCI devices it looks like this:
1067 # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1]]]]
1069 # It seems the reasoning for this difference is because
1070 # pciif.py needs all the PCI device configurations at
1071 # the same time when creating the devices.
1073 # To further complicate matters, Xen 2.0 configuration format
1074 # uses the following for pci device configuration:
1076 # [device, [pci, [domain, 0], [bus, 0], [dev, 1], [func, 2]]]
1078 if dev_type == 'pci':
1079 pci_devs_uuid = sxp.child_value(config, 'uuid',
1080 uuid.createString())
1081 pci_devs = []
1082 for pci_dev in sxp.children(config, 'dev'):
1083 pci_dev_info = {}
1084 for opt_val in pci_dev[1:]:
1085 try:
1086 opt, val = opt_val
1087 pci_dev_info[opt] = val
1088 except TypeError:
1089 pass
1090 pci_devs.append(pci_dev_info)
1091 target['devices'][pci_devs_uuid] = (dev_type,
1092 {'devs': pci_devs,
1093 'uuid': pci_devs_uuid})
1095 log.debug("XendConfig: reading device: %s" % pci_devs)
1096 return pci_devs_uuid
1098 for opt_val in config[1:]:
1099 try:
1100 opt, val = opt_val
1101 dev_info[opt] = val
1102 except (TypeError, ValueError): # unpack error
1103 pass
1105 if dev_type == 'vbd':
1106 dev_info['bootable'] = 0
1107 if dev_info.get('dev', '').startswith('ioemu:'):
1108 dev_info['driver'] = 'ioemu'
1109 else:
1110 dev_info['driver'] = 'paravirtualised'
1112 if dev_type == 'tap':
1113 if dev_info['uname'].split(':')[1] not in blktap_disk_types:
1114 raise XendConfigError("tap:%s not a valid disk type" %
1115 dev_info['uname'].split(':')[1])
1117 if dev_type == 'vif':
1118 if not dev_info.get('mac'):
1119 dev_info['mac'] = randomMAC()
1121 self.device_duplicate_check(dev_type, dev_info, target)
1123 if dev_type == 'vif':
1124 if dev_info.get('policy') and dev_info.get('label'):
1125 dev_info['security_label'] = "%s:%s:%s" % \
1126 (xsconstants.ACM_POLICY_ID,
1127 dev_info['policy'],dev_info['label'])
1129 # create uuid if it doesn't exist
1130 dev_uuid = dev_info.get('uuid', None)
1131 if not dev_uuid:
1132 dev_uuid = uuid.createString()
1133 dev_info['uuid'] = dev_uuid
1135 # store dev references by uuid for certain device types
1136 target['devices'][dev_uuid] = (dev_type, dev_info)
1137 if dev_type in ('vif', 'vbd', 'vtpm'):
1138 param = '%s_refs' % dev_type
1139 if param not in target:
1140 target[param] = []
1141 if dev_uuid not in target[param]:
1142 if dev_type == 'vbd':
1143 # Compat hack -- mark first disk bootable
1144 dev_info['bootable'] = int(not target[param])
1145 target[param].append(dev_uuid)
1146 elif dev_type == 'tap':
1147 if 'vbd_refs' not in target:
1148 target['vbd_refs'] = []
1149 if dev_uuid not in target['vbd_refs']:
1150 # Compat hack -- mark first disk bootable
1151 dev_info['bootable'] = int(not target['vbd_refs'])
1152 target['vbd_refs'].append(dev_uuid)
1154 elif dev_type == 'vfb':
1155 # Populate other config with aux data that is associated
1156 # with vfb
1158 other_config = {}
1159 for key in XENAPI_CONSOLE_OTHER_CFG:
1160 if key in dev_info:
1161 other_config[key] = dev_info[key]
1162 target['devices'][dev_uuid][1]['other_config'] = other_config
1165 if 'console_refs' not in target:
1166 target['console_refs'] = []
1168 # Treat VFB devices as console devices so they are found
1169 # through Xen API
1170 if dev_uuid not in target['console_refs']:
1171 target['console_refs'].append(dev_uuid)
1173 elif dev_type == 'console':
1174 if 'console_refs' not in target:
1175 target['console_refs'] = []
1176 if dev_uuid not in target['console_refs']:
1177 target['console_refs'].append(dev_uuid)
1179 log.debug("XendConfig: reading device: %s" % scrub_password(dev_info))
1180 return dev_uuid
1182 if cfg_xenapi:
1183 dev_info = {}
1184 dev_uuid = ''
1185 if dev_type == 'vif':
1186 dev_info['mac'] = cfg_xenapi.get('MAC')
1187 if not dev_info['mac']:
1188 dev_info['mac'] = randomMAC()
1189 # vifname is the name on the guest, not dom0
1190 # TODO: we don't have the ability to find that out or
1191 # change it from dom0
1192 #if cfg_xenapi.get('device'): # don't add if blank
1193 # dev_info['vifname'] = cfg_xenapi.get('device')
1194 if cfg_xenapi.get('type'):
1195 dev_info['type'] = cfg_xenapi.get('type')
1196 if cfg_xenapi.get('name'):
1197 dev_info['name'] = cfg_xenapi.get('name')
1198 if cfg_xenapi.get('network'):
1199 network = XendAPIStore.get(
1200 cfg_xenapi.get('network'), 'network')
1201 dev_info['bridge'] = network.get_name_label()
1203 if cfg_xenapi.get('security_label'):
1204 dev_info['security_label'] = \
1205 cfg_xenapi.get('security_label')
1207 dev_uuid = cfg_xenapi.get('uuid', None)
1208 if not dev_uuid:
1209 dev_uuid = uuid.createString()
1210 dev_info['uuid'] = dev_uuid
1211 target['devices'][dev_uuid] = (dev_type, dev_info)
1212 target['vif_refs'].append(dev_uuid)
1214 elif dev_type in ('vbd', 'tap'):
1215 dev_info['type'] = cfg_xenapi.get('type', 'Disk')
1216 if dev_info['type'] == 'CD':
1217 old_vbd_type = 'cdrom'
1218 else:
1219 old_vbd_type = 'disk'
1221 dev_info['uname'] = cfg_xenapi.get('image', '')
1222 dev_info['dev'] = '%s:%s' % (cfg_xenapi.get('device'),
1223 old_vbd_type)
1224 dev_info['bootable'] = int(cfg_xenapi.get('bootable', 0))
1225 dev_info['driver'] = cfg_xenapi.get('driver', '')
1226 dev_info['VDI'] = cfg_xenapi.get('VDI', '')
1228 if cfg_xenapi.get('mode') == 'RW':
1229 dev_info['mode'] = 'w'
1230 else:
1231 dev_info['mode'] = 'r'
1233 dev_uuid = cfg_xenapi.get('uuid', None)
1234 if not dev_uuid:
1235 dev_uuid = uuid.createString()
1236 dev_info['uuid'] = dev_uuid
1237 target['devices'][dev_uuid] = (dev_type, dev_info)
1238 target['vbd_refs'].append(dev_uuid)
1240 elif dev_type == 'vtpm':
1241 if cfg_xenapi.get('type'):
1242 dev_info['type'] = cfg_xenapi.get('type')
1244 dev_uuid = cfg_xenapi.get('uuid', None)
1245 if not dev_uuid:
1246 dev_uuid = uuid.createString()
1247 dev_info['uuid'] = dev_uuid
1248 dev_info['other_config'] = cfg_xenapi.get('other_config', {})
1249 target['devices'][dev_uuid] = (dev_type, dev_info)
1250 target['vtpm_refs'].append(dev_uuid)
1252 elif dev_type == 'console':
1253 dev_uuid = cfg_xenapi.get('uuid', None)
1254 if not dev_uuid:
1255 dev_uuid = uuid.createString()
1256 dev_info['uuid'] = dev_uuid
1257 dev_info['protocol'] = cfg_xenapi.get('protocol', 'rfb')
1258 dev_info['other_config'] = cfg_xenapi.get('other_config', {})
1259 if dev_info['protocol'] == 'rfb':
1260 # collapse other config into devinfo for things
1261 # such as vncpasswd, vncunused, etc.
1262 dev_info.update(cfg_xenapi.get('other_config', {}))
1263 dev_info['type'] = 'vnc'
1264 target['devices'][dev_uuid] = ('vfb', dev_info)
1265 target['console_refs'].append(dev_uuid)
1267 # Finally, if we are a pvfb, we need to make a vkbd
1268 # as well that is not really exposed to Xen API
1269 vkbd_uuid = uuid.createString()
1270 target['devices'][vkbd_uuid] = ('vkbd', {})
1272 elif dev_info['protocol'] == 'vt100':
1273 # if someone tries to create a VT100 console
1274 # via the Xen API, we'll have to ignore it
1275 # because we create one automatically in
1276 # XendDomainInfo._update_consoles
1277 raise XendConfigError('Creating vt100 consoles via '
1278 'Xen API is unsupported')
1280 return dev_uuid
1282 # no valid device to add
1283 return ''
1285 def phantom_device_add(self, dev_type, cfg_xenapi = None,
1286 target = None):
1287 """Add a phantom tap device configuration in XenAPI struct format.
1288 """
1290 if target == None:
1291 target = self
1293 if dev_type not in XendDevices.valid_devices() and \
1294 dev_type not in XendDevices.pseudo_devices():
1295 raise XendConfigError("XendConfig: %s not a valid device type" %
1296 dev_type)
1298 if cfg_xenapi == None:
1299 raise XendConfigError("XendConfig: device_add requires some "
1300 "config.")
1302 if cfg_xenapi:
1303 log.debug("XendConfig.phantom_device_add: %s" % str(cfg_xenapi))
1305 if cfg_xenapi:
1306 dev_info = {}
1307 if dev_type in ('vbd', 'tap'):
1308 if dev_type == 'vbd':
1309 dev_info['uname'] = cfg_xenapi.get('image', '')
1310 dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
1311 elif dev_type == 'tap':
1312 if cfg_xenapi.get('image').find('tap:') == -1:
1313 dev_info['uname'] = 'tap:qcow:%s' % cfg_xenapi.get('image')
1314 dev_info['dev'] = '/dev/%s' % cfg_xenapi.get('device')
1315 dev_info['uname'] = cfg_xenapi.get('image')
1316 dev_info['mode'] = cfg_xenapi.get('mode')
1317 dev_info['backend'] = '0'
1318 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
1319 dev_info['uuid'] = dev_uuid
1320 self['devices'][dev_uuid] = (dev_type, dev_info)
1321 self['vbd_refs'].append(dev_uuid)
1322 return dev_uuid
1324 return ''
1326 def console_add(self, protocol, location, other_config = {}):
1327 dev_uuid = uuid.createString()
1328 if protocol == 'vt100':
1329 dev_info = {
1330 'uuid': dev_uuid,
1331 'protocol': protocol,
1332 'location': location,
1333 'other_config': other_config,
1336 if 'devices' not in self:
1337 self['devices'] = {}
1339 self['devices'][dev_uuid] = ('console', dev_info)
1340 self['console_refs'].append(dev_uuid)
1341 return dev_info
1343 return {}
1345 def console_update(self, console_uuid, key, value):
1346 for dev_uuid, (dev_type, dev_info) in self['devices'].items():
1347 if dev_uuid == console_uuid:
1348 dev_info[key] = value
1349 # collapse other_config into dev_info for things
1350 # such as vncpasswd, vncunused, etc.
1351 if key == 'other_config':
1352 for k in XENAPI_CONSOLE_OTHER_CFG:
1353 if k in dev_info and k not in value:
1354 del dev_info[k]
1355 dev_info.update(value)
1356 break
1358 def console_get_all(self, protocol):
1359 if protocol == 'vt100':
1360 consoles = [dinfo for dtype, dinfo in self['devices'].values()
1361 if dtype == 'console']
1362 return [c for c in consoles if c.get('protocol') == protocol]
1364 elif protocol == 'rfb':
1365 vfbs = [dinfo for dtype, dinfo in self['devices'].values()
1366 if dtype == 'vfb']
1368 # move all non-console key values to other_config before
1369 # returning console config
1370 valid_keys = ['uuid', 'location']
1371 for vfb in vfbs:
1372 other_config = {}
1373 for key, val in vfb.items():
1374 if key not in valid_keys:
1375 other_config[key] = vfb[key]
1376 del vfb[key]
1377 vfb['other_config'] = other_config
1378 vfb['protocol'] = 'rfb'
1380 return vfbs
1382 else:
1383 return []
1385 def device_update(self, dev_uuid, cfg_sxp = [], cfg_xenapi = {}):
1386 """Update an existing device with the new configuration.
1388 @rtype: boolean
1389 @return: Returns True if succesfully found and updated a device conf
1390 """
1391 if dev_uuid in self['devices'] and cfg_sxp:
1392 if sxp.child0(cfg_sxp) == 'device':
1393 config = sxp.child0(cfg_sxp)
1394 else:
1395 config = cfg_sxp
1397 dev_type, dev_info = self['devices'][dev_uuid]
1398 for opt_val in config[1:]:
1399 try:
1400 opt, val = opt_val
1401 dev_info[opt] = val
1402 except (TypeError, ValueError):
1403 pass # no value for this config option
1405 self['devices'][dev_uuid] = (dev_type, dev_info)
1406 return True
1408 elif dev_uuid in self['devices'] and cfg_xenapi:
1409 dev_type, dev_info = self['devices'][dev_uuid]
1410 for key, val in cfg_xenapi.items():
1411 dev_info[key] = val
1412 self['devices'][dev_uuid] = (dev_type, dev_info)
1414 return False
1417 def device_sxpr(self, dev_uuid = None, dev_type = None, dev_info = None, target = None):
1418 """Get Device SXPR by either giving the device UUID or (type, config).
1420 @rtype: list of lists
1421 @return: device config sxpr
1422 """
1423 sxpr = []
1425 if target == None:
1426 target = self
1428 if dev_uuid != None and dev_uuid in target['devices']:
1429 dev_type, dev_info = target['devices'][dev_uuid]
1431 if dev_type == None or dev_info == None:
1432 raise XendConfigError("Required either UUID or device type and "
1433 "configuration dictionary.")
1435 sxpr.append(dev_type)
1436 if dev_type in ('console', 'vfb'):
1437 config = [(opt, val) for opt, val in dev_info.items()
1438 if opt != 'other_config']
1439 else:
1440 config = [(opt, val) for opt, val in dev_info.items()]
1442 sxpr += config
1444 return sxpr
1446 def ordered_device_refs(self, target = None):
1447 result = []
1449 if target == None:
1450 target = self
1452 # vkbd devices *must* be before vfb devices, otherwise
1453 # there is a race condition when setting up devices
1454 # where the daemon spawned for the vfb may write stuff
1455 # into xenstore vkbd backend, before DevController has
1456 # setup permissions on the vkbd backend path. This race
1457 # results in domain creation failing with 'device already
1458 # connected' messages
1459 result.extend([u for u in target['devices'].keys() if target['devices'][u][0] == 'vkbd'])
1461 result.extend(target.get('console_refs', []) +
1462 target.get('vbd_refs', []) +
1463 target.get('vif_refs', []) +
1464 target.get('vtpm_refs', []))
1466 result.extend([u for u in target['devices'].keys() if u not in result])
1467 return result
1469 def all_devices_sxpr(self, target = None):
1470 """Returns the SXPR for all devices in the current configuration."""
1471 sxprs = []
1472 pci_devs = []
1474 if target == None:
1475 target = self
1477 if 'devices' not in target:
1478 return sxprs
1480 ordered_refs = self.ordered_device_refs(target = target)
1481 for dev_uuid in ordered_refs:
1482 dev_type, dev_info = target['devices'][dev_uuid]
1483 if dev_type == 'pci': # special case for pci devices
1484 sxpr = ['pci', ['uuid', dev_info['uuid']]]
1485 for pci_dev_info in dev_info['devs']:
1486 pci_dev_sxpr = ['dev']
1487 for opt, val in pci_dev_info.items():
1488 pci_dev_sxpr.append([opt, val])
1489 sxpr.append(pci_dev_sxpr)
1490 sxprs.append((dev_type, sxpr))
1491 else:
1492 sxpr = self.device_sxpr(dev_type = dev_type,
1493 dev_info = dev_info,
1494 target = target)
1495 sxprs.append((dev_type, sxpr))
1497 return sxprs
1499 def image_sxpr(self):
1500 """Returns a backwards compatible image SXP expression that is
1501 used in xenstore's /vm/<uuid>/image value and xm list."""
1502 image = [self.image_type()]
1503 if self.has_key('PV_kernel'):
1504 image.append(['kernel', self['PV_kernel']])
1505 if self.has_key('PV_ramdisk') and self['PV_ramdisk']:
1506 image.append(['ramdisk', self['PV_ramdisk']])
1507 if self.has_key('PV_args') and self['PV_args']:
1508 image.append(['args', self['PV_args']])
1510 for key in XENAPI_PLATFORM_CFG:
1511 if key in self['platform']:
1512 image.append([key, self['platform'][key]])
1514 if 'notes' in self:
1515 image.append(self.notes_sxp(self['notes']))
1517 return image
1519 def update_with_image_sxp(self, image_sxp, bootloader = False):
1520 # Convert Legacy "image" config to Xen API PV_*
1521 # configuration
1522 log.debug("update_with_image_sxp(%s)" % scrub_password(image_sxp))
1524 # user-specified args must come last: previous releases did this and
1525 # some domU kernels rely upon the ordering.
1526 kernel_args = sxp.child_value(image_sxp, 'args', '')
1528 # attempt to extract extra arguments from SXP config
1529 arg_ip = sxp.child_value(image_sxp, 'ip')
1530 if arg_ip and not re.search(r'ip=[^ ]+', kernel_args):
1531 kernel_args = 'ip=%s ' % arg_ip + kernel_args
1532 arg_root = sxp.child_value(image_sxp, 'root')
1533 if arg_root and not re.search(r'root=', kernel_args):
1534 kernel_args = 'root=%s ' % arg_root + kernel_args
1536 if bootloader:
1537 self['_temp_using_bootloader'] = '1'
1538 self['_temp_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1539 self['_temp_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1540 self['_temp_args'] = kernel_args
1541 else:
1542 self['PV_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1543 self['PV_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1544 self['PV_args'] = kernel_args
1546 for key in XENAPI_PLATFORM_CFG:
1547 val = sxp.child_value(image_sxp, key, None)
1548 if val is not None and val != '':
1549 self['platform'][key] = val
1551 notes = sxp.children(image_sxp, 'notes')
1552 if notes:
1553 self['notes'] = self.notes_from_sxp(notes[0])
1555 self._hvm_boot_params_from_sxp(image_sxp)
1557 def set_notes(self, notes):
1558 'Add parsed elfnotes to image'
1559 self['notes'] = notes
1561 def get_notes(self):
1562 try:
1563 return self['notes'] or {}
1564 except KeyError:
1565 return {}
1567 def notes_from_sxp(self, nsxp):
1568 notes = {}
1569 for note in sxp.children(nsxp):
1570 notes[note[0]] = note[1]
1571 return notes
1573 def notes_sxp(self, notes):
1574 nsxp = ['notes']
1575 for k, v in notes.iteritems():
1576 nsxp.append([k, str(v)])
1577 return nsxp
1579 def _hvm_boot_params_from_sxp(self, image_sxp):
1580 boot = sxp.child_value(image_sxp, 'boot', None)
1581 if boot is not None:
1582 self['HVM_boot_policy'] = 'BIOS order'
1583 self['HVM_boot_params'] = { 'order' : boot }
1585 def is_hvm(self):
1586 return self['HVM_boot_policy'] != ''
1588 def image_type(self):
1589 stored_type = self['platform'].get('image_type')
1590 return stored_type or (self.is_hvm() and 'hvm' or 'linux')