ia64/xen-unstable

view tools/python/xen/xend/XendConfig.py @ 16967:b1b93caa7d8e

xend: Fix grow of the LEGACY_UNSUPPORTED_BY_XENAPI_CFG list each time
we instantiate a new XendConfig.
Signed-off-by: Pascal Bouchareine <pascal@gandi.net>
author Keir Fraser <keir.fraser@citrix.com>
date Sat Feb 02 10:53:24 2008 +0000 (2008-02-02)
parents 396ab902b02d
children 58e5e9ae0f8d
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', 'loader', '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', 'hap']
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 'target': int,
174 'security_label': str,
175 'pci': str,
176 }
178 # List of legacy configuration keys that have no equivalent in the
179 # Xen API, but are still stored in XendConfig.
181 LEGACY_UNSUPPORTED_BY_XENAPI_CFG = [
182 # roundtripped (dynamic, unmodified)
183 'shadow_memory',
184 'vcpu_avail',
185 'features',
186 # read/write
187 'on_xend_start',
188 'on_xend_stop',
189 # read-only
190 'domid',
191 'start_time',
192 'cpu_time',
193 'online_vcpus',
194 # write-once
195 'cpu',
196 'cpus',
197 ]
199 LEGACY_CFG_TYPES = {
200 'uuid': str,
201 'name': str,
202 'vcpus': int,
203 'vcpu_avail': long,
204 'memory': int,
205 'shadow_memory': int,
206 'maxmem': int,
207 'start_time': float,
208 'cpu_time': float,
209 'features': str,
210 'localtime': int,
211 'name': str,
212 'on_poweroff': str,
213 'on_reboot': str,
214 'on_crash': str,
215 'on_xend_stop': str,
216 'on_xend_start': str,
217 'online_vcpus': int,
218 'rtc/timeoffset': str,
219 }
221 # Values that should be stored in xenstore's /vm/<uuid> that is used
222 # by Xend. Used in XendDomainInfo to restore running VM state from
223 # xenstore.
224 LEGACY_XENSTORE_VM_PARAMS = [
225 'uuid',
226 'name',
227 'vcpus',
228 'vcpu_avail',
229 'memory',
230 'shadow_memory',
231 'maxmem',
232 'start_time',
233 'name',
234 'on_poweroff',
235 'on_crash',
236 'on_reboot',
237 'on_xend_start',
238 'on_xend_stop',
239 ]
241 ##
242 ## Config Choices
243 ##
245 CONFIG_RESTART_MODES = ('restart', 'destroy', 'preserve', 'rename-restart')
246 CONFIG_OLD_DOM_STATES = ('running', 'blocked', 'paused', 'shutdown',
247 'crashed', 'dying')
249 class XendConfigError(VmError):
250 def __str__(self):
251 return 'Invalid Configuration: %s' % str(self.value)
253 ##
254 ## XendConfig Class (an extended dictionary)
255 ##
257 class XendConfig(dict):
258 """ The new Xend VM Configuration.
260 Stores the configuration in xenapi compatible format but retains
261 import and export functions for SXP.
262 """
263 def __init__(self, filename = None, sxp_obj = None,
264 xapi = None, dominfo = None):
266 dict.__init__(self)
267 self.update(self._defaults())
269 if filename:
270 try:
271 sxp_obj = sxp.parse(open(filename,'r'))
272 sxp_obj = sxp_obj[0]
273 except IOError, e:
274 raise XendConfigError("Unable to read file: %s" % filename)
276 if sxp_obj:
277 self._sxp_to_xapi(sxp_obj)
278 self._sxp_to_xapi_unsupported(sxp_obj)
279 elif xapi:
280 self.update_with_xenapi_config(xapi)
281 elif dominfo:
282 # output from xc.domain_getinfo
283 self._dominfo_to_xapi(dominfo, update_mem = True)
285 log.debug('XendConfig.init: %s' % scrub_password(self))
287 # validators go here
288 self.validate()
290 """ In time, we should enable this type checking addition. It is great
291 also for tracking bugs and unintended writes to XendDomainInfo.info
292 def __setitem__(self, key, value):
293 type_conv = XENAPI_CFG_TYPES.get(key)
294 if callable(type_conv):
295 try:
296 dict.__setitem__(self, key, type_conv(value))
297 except (ValueError, TypeError):
298 raise XendConfigError("Wrong type for configuration value " +
299 "%s. Expected %s" %
300 (key, type_conv.__name__))
301 else:
302 dict.__setitem__(self, key, value)
303 """
305 def _defaults(self):
306 defaults = {
307 'name_label': 'Domain-Unnamed',
308 'actions_after_shutdown': 'destroy',
309 'actions_after_reboot': 'restart',
310 'actions_after_crash': 'restart',
311 'actions_after_suspend': '',
312 'is_a_template': False,
313 'is_control_domain': False,
314 'features': '',
315 'PV_bootloader': '',
316 'PV_kernel': '',
317 'PV_ramdisk': '',
318 'PV_args': '',
319 'PV_bootloader_args': '',
320 'HVM_boot_policy': '',
321 'HVM_boot_params': {},
322 'memory_static_min': 0,
323 'memory_dynamic_min': 0,
324 'shadow_memory': 0,
325 'memory_static_max': 0,
326 'memory_dynamic_max': 0,
327 'devices': {},
328 'on_xend_start': 'ignore',
329 'on_xend_stop': 'ignore',
330 'cpus': [],
331 'VCPUs_max': 1,
332 'VCPUs_live': 1,
333 'VCPUs_at_startup': 1,
334 'vcpus_params': {},
335 'console_refs': [],
336 'vif_refs': [],
337 'vbd_refs': [],
338 'vtpm_refs': [],
339 'other_config': {},
340 'platform': {},
341 'target': 0,
342 }
344 return defaults
346 #
347 # Here we assume these values exist in the dict.
348 # If they don't we have a bigger problem, lets not
349 # try and 'fix it up' but acutually fix the cause ;-)
350 #
351 def _memory_sanity_check(self):
352 log.trace("_memory_sanity_check memory_static_min: %s, "
353 "memory_static_max: %i, "
354 "memory_dynamic_min: %i, "
355 "memory_dynamic_max: %i",
356 self["memory_static_min"],
357 self["memory_static_max"],
358 self["memory_dynamic_min"],
359 self["memory_dynamic_max"])
361 if not self["memory_static_min"] <= self["memory_static_max"]:
362 raise XendConfigError("memory_static_min must be less " \
363 "than or equal to memory_static_max")
364 if not self["memory_static_min"] <= self["memory_dynamic_min"]:
365 raise XendConfigError("memory_static_min must be less " \
366 "than or equal to memory_dynamic_min")
367 if not self["memory_dynamic_max"] <= self["memory_static_max"]:
368 raise XendConfigError("memory_dynamic_max must be less " \
369 "than or equal to memory_static_max")
370 if not self["memory_dynamic_max"] > 0:
371 raise XendConfigError("memory_dynamic_max must be greater " \
372 "than zero")
373 if not self["memory_static_max"] > 0:
374 raise XendConfigError("memory_static_max must be greater " \
375 "than zero")
377 def _actions_sanity_check(self):
378 for event in ['shutdown', 'reboot', 'crash']:
379 if self['actions_after_' + event] not in CONFIG_RESTART_MODES:
380 raise XendConfigError('Invalid event handling mode: ' +
381 event)
383 def _vcpus_sanity_check(self):
384 if 'VCPUs_max' in self and 'vcpu_avail' not in self:
385 self['vcpu_avail'] = (1 << self['VCPUs_max']) - 1
387 def _uuid_sanity_check(self):
388 """Make sure UUID is in proper string format with hyphens."""
389 if 'uuid' not in self or not self['uuid']:
390 self['uuid'] = uuid.createString()
391 else:
392 self['uuid'] = uuid.toString(uuid.fromString(self['uuid']))
394 def _name_sanity_check(self):
395 if 'name_label' not in self:
396 self['name_label'] = 'Domain-' + self['uuid']
398 def _platform_sanity_check(self):
399 if 'keymap' not in self['platform'] and XendOptions.instance().get_keymap():
400 self['platform']['keymap'] = XendOptions.instance().get_keymap()
402 if self.is_hvm() or self.has_rfb():
403 if 'device_model' not in self['platform']:
404 self['platform']['device_model'] = xen.util.auxbin.pathTo("qemu-dm")
406 if self.is_hvm():
407 if 'loader' not in self['platform']:
408 log.debug("No loader present")
409 # Old configs may have hvmloder set as PV_kernel param,
410 # so lets migrate them....
411 if self['PV_kernel'] == "/usr/lib/xen/boot/hvmloader":
412 self['platform']['loader'] = self['PV_kernel']
413 log.debug("Loader copied from kernel %s" % str(self['platform']['loader']))
414 else:
415 self['platform']['loader'] = "/usr/lib/xen/boot/hvmloader"
416 log.debug("Loader %s" % str(self['platform']['loader']))
418 # Compatibility hack, can go away soon.
419 if 'soundhw' not in self['platform'] and \
420 self['platform'].get('enable_audio'):
421 self['platform']['soundhw'] = 'sb16'
423 def validate(self):
424 self._uuid_sanity_check()
425 self._name_sanity_check()
426 self._memory_sanity_check()
427 self._actions_sanity_check()
428 self._vcpus_sanity_check()
429 self._platform_sanity_check()
431 def _dominfo_to_xapi(self, dominfo, update_mem = False):
432 self['domid'] = dominfo['domid']
433 self['online_vcpus'] = dominfo['online_vcpus']
434 self['VCPUs_max'] = dominfo['max_vcpu_id'] + 1
436 if update_mem:
437 self['memory_dynamic_min'] = dominfo['mem_kb'] * 1024
438 self['memory_dynamic_max'] = dominfo['mem_kb'] * 1024
439 self['memory_static_min'] = 0
440 self['memory_static_max'] = dominfo['maxmem_kb'] * 1024
441 self._memory_sanity_check()
443 self['cpu_time'] = dominfo['cpu_time']/1e9
444 if dominfo.get('ssidref'):
445 ssidref = int(dominfo.get('ssidref'))
446 import xen.util.xsm.xsm as security
447 self['security_label'] = security.ssidref2security_label(ssidref)
449 self['shutdown_reason'] = dominfo['shutdown_reason']
451 # parse state into Xen API states
452 self['running'] = dominfo['running']
453 self['crashed'] = dominfo['crashed']
454 self['dying'] = dominfo['dying']
455 self['shutdown'] = dominfo['shutdown']
456 self['paused'] = dominfo['paused']
457 self['blocked'] = dominfo['blocked']
459 if 'name' in dominfo:
460 self['name_label'] = dominfo['name']
462 if 'handle' in dominfo:
463 self['uuid'] = uuid.toString(dominfo['handle'])
465 def _parse_sxp(self, sxp_cfg):
466 """ Populate this XendConfig using the parsed SXP.
468 @param sxp_cfg: Parsed SXP Configuration
469 @type sxp_cfg: list of lists
470 @rtype: dictionary
471 @return: A dictionary containing the parsed options of the SXP.
472 """
473 cfg = {}
475 for key, typ in XENAPI_CFG_TYPES.items():
476 val = sxp.child_value(sxp_cfg, key)
477 if val is not None:
478 try:
479 cfg[key] = typ(val)
480 except (ValueError, TypeError), e:
481 log.warn('Unable to convert type value for key: %s' % key)
483 # Convert deprecated options to current equivalents.
485 restart = sxp.child_value(sxp_cfg, 'restart')
486 if restart:
487 if restart == 'onreboot':
488 cfg['on_poweroff'] = 'destroy'
489 cfg['on_reboot'] = 'restart'
490 cfg['on_crash'] = 'destroy'
491 elif restart == 'always':
492 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
493 cfg[opt] = 'restart'
494 elif restart == 'never':
495 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
496 cfg[opt] = 'never'
497 else:
498 log.warn('Ignoring unrecognised value for deprecated option:'
499 'restart = \'%s\'', restart)
501 # Handle memory, passed in as MiB
503 if sxp.child_value(sxp_cfg, "memory") != None:
504 cfg["memory"] = int(sxp.child_value(sxp_cfg, "memory"))
505 if sxp.child_value(sxp_cfg, "maxmem") != None:
506 cfg["maxmem"] = int(sxp.child_value(sxp_cfg, "maxmem"))
508 # Convert scheduling parameters to vcpus_params
509 if 'vcpus_params' not in cfg:
510 cfg['vcpus_params'] = {}
511 cfg["vcpus_params"]["weight"] = \
512 int(sxp.child_value(sxp_cfg, "cpu_weight", 256))
513 cfg["vcpus_params"]["cap"] = \
514 int(sxp.child_value(sxp_cfg, "cpu_cap", 0))
516 # Only extract options we know about.
517 extract_keys = LEGACY_UNSUPPORTED_BY_XENAPI_CFG + \
518 XENAPI_CFG_TO_LEGACY_CFG.values()
520 for key in extract_keys:
521 val = sxp.child_value(sxp_cfg, key)
522 if val != None:
523 try:
524 cfg[key] = LEGACY_CFG_TYPES[key](val)
525 except KeyError:
526 cfg[key] = val
527 except (TypeError, ValueError), e:
528 log.warn("Unable to parse key %s: %s: %s" %
529 (key, str(val), e))
531 if 'platform' not in cfg:
532 cfg['platform'] = {}
533 localtime = sxp.child_value(sxp_cfg, 'localtime')
534 if localtime is not None:
535 cfg['platform']['localtime'] = localtime
537 # Compatibility hack -- can go soon.
538 for key in XENAPI_PLATFORM_CFG:
539 val = sxp.child_value(sxp_cfg, "platform_" + key, None)
540 if val is not None:
541 self['platform'][key] = val
543 # Compatibility hack -- can go soon.
544 boot_order = sxp.child_value(sxp_cfg, 'HVM_boot')
545 if boot_order:
546 cfg['HVM_boot_policy'] = 'BIOS order'
547 cfg['HVM_boot_params'] = { 'order' : boot_order }
550 # Parsing the device SXP's.
551 cfg['devices'] = {}
552 for dev in sxp.children(sxp_cfg, 'device'):
553 config = sxp.child0(dev)
554 dev_type = sxp.name(config)
555 self.device_add(dev_type, cfg_sxp = config, target = cfg)
557 # Extract missing data from configuration entries
558 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
559 if image_sxp:
560 image_vcpus = sxp.child_value(image_sxp, 'vcpus')
561 if image_vcpus != None:
562 try:
563 if 'VCPUs_max' not in cfg:
564 cfg['VCPUs_max'] = int(image_vcpus)
565 elif cfg['VCPUs_max'] != int(image_vcpus):
566 cfg['VCPUs_max'] = int(image_vcpus)
567 log.warn('Overriding vcpus from %d to %d using image'
568 'vcpus value.', cfg['VCPUs_max'])
569 except ValueError, e:
570 raise XendConfigError('integer expeceted: %s: %s' %
571 image_sxp, e)
573 # Deprecated cpu configuration
574 if 'cpu' in cfg:
575 if 'cpus' in cfg:
576 cfg['cpus'] = "%s,%s" % (str(cfg['cpu']), cfg['cpus'])
577 else:
578 cfg['cpus'] = str(cfg['cpu'])
580 # Convert 'cpus' to list of ints
581 if 'cpus' in cfg:
582 cpus = []
583 if type(cfg['cpus']) == list:
584 # If sxp_cfg was created from config.sxp,
585 # the form of 'cpus' is list of string.
586 # Convert 'cpus' to list of ints.
587 # ['1'] -> [1]
588 # ['0','2','3'] -> [0,2,3]
589 try:
590 for c in cfg['cpus']:
591 cpus.append(int(c))
593 cfg['cpus'] = cpus
594 except ValueError, e:
595 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
596 else:
597 # Convert 'cpus' string to list of ints
598 # 'cpus' supports a list of ranges (0-3),
599 # seperated by commas, and negation, (^1).
600 # Precedence is settled by order of the
601 # string:
602 # "0-3,^1" -> [0,2,3]
603 # "0-3,^1,1" -> [0,1,2,3]
604 try:
605 for c in cfg['cpus'].split(','):
606 if c.find('-') != -1:
607 (x, y) = c.split('-')
608 for i in range(int(x), int(y)+1):
609 cpus.append(int(i))
610 else:
611 # remove this element from the list
612 if c[0] == '^':
613 cpus = [x for x in cpus if x != int(c[1:])]
614 else:
615 cpus.append(int(c))
617 cfg['cpus'] = cpus
618 except ValueError, e:
619 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
621 import xen.util.xsm.xsm as security
622 if security.on():
623 from xen.util.acmpolicy import ACM_LABEL_UNLABELED
624 if not 'security' in cfg and sxp.child_value(sxp_cfg, 'security'):
625 cfg['security'] = sxp.child_value(sxp_cfg, 'security')
626 elif not cfg.get('security_label'):
627 cfg['security'] = [['access_control',
628 ['policy', security.get_active_policy_name() ],
629 ['label', ACM_LABEL_UNLABELED ]]]
631 if 'security' in cfg and not cfg.get('security_label'):
632 secinfo = cfg['security']
633 # The xm command sends a list formatted like this:
634 # [['access_control', ['policy', 'xm-test'],['label', 'red']],
635 # ['ssidref', 196611]]
636 policy = ""
637 label = ""
638 for idx in range(0, len(secinfo)):
639 if secinfo[idx][0] == "access_control":
640 for aidx in range(1, len(secinfo[idx])):
641 if secinfo[idx][aidx][0] == "policy":
642 policy = secinfo[idx][aidx][1]
643 if secinfo[idx][aidx][0] == "label":
644 label = secinfo[idx][aidx][1]
645 cfg['security_label'] = \
646 security.set_security_label(policy, label)
647 if not sxp.child_value(sxp_cfg, 'security_label'):
648 del cfg['security']
650 sec_lab = cfg['security_label'].split(":")
651 if len(sec_lab) != 3:
652 raise XendConfigError("Badly formatted security label: %s"
653 % cfg['security_label'])
655 old_state = sxp.child_value(sxp_cfg, 'state')
656 if old_state:
657 for i in range(len(CONFIG_OLD_DOM_STATES)):
658 cfg[CONFIG_OLD_DOM_STATES[i]] = int(old_state[i] != '-')
660 return cfg
663 def _sxp_to_xapi(self, sxp_cfg):
664 """Read in an SXP Configuration object and
665 populate at much of the Xen API with valid values.
666 """
667 log.debug('_sxp_to_xapi(%s)' % scrub_password(sxp_cfg))
669 cfg = self._parse_sxp(sxp_cfg)
671 for key, typ in XENAPI_CFG_TYPES.items():
672 val = cfg.get(key)
673 if val is not None:
674 self[key] = typ(val)
676 # Convert parameters that can be directly mapped from
677 # the Legacy Config to Xen API Config
679 for apikey, cfgkey in XENAPI_CFG_TO_LEGACY_CFG.items():
680 try:
681 type_conv = XENAPI_CFG_TYPES.get(apikey)
682 if callable(type_conv):
683 self[apikey] = type_conv(cfg[cfgkey])
684 else:
685 log.warn("Unconverted key: " + apikey)
686 self[apikey] = cfg[cfgkey]
687 except KeyError:
688 pass
690 # Lets try and handle memory correctly
692 MiB = 1024 * 1024
694 if "memory" in cfg:
695 self["memory_static_min"] = 0
696 self["memory_static_max"] = int(cfg["memory"]) * MiB
697 self["memory_dynamic_min"] = int(cfg["memory"]) * MiB
698 self["memory_dynamic_max"] = int(cfg["memory"]) * MiB
700 if "maxmem" in cfg:
701 self["memory_static_max"] = int(cfg["maxmem"]) * MiB
703 self._memory_sanity_check()
705 def update_with(n, o):
706 if not self.get(n):
707 self[n] = cfg.get(o, '')
709 update_with('PV_bootloader', 'bootloader')
710 update_with('PV_bootloader_args', 'bootloader_args')
712 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
713 if image_sxp:
714 self.update_with_image_sxp(image_sxp)
716 # Convert Legacy HVM parameters to Xen API configuration
717 for key in XENAPI_PLATFORM_CFG:
718 if key in cfg:
719 self['platform'][key] = cfg[key]
721 # set device references in the configuration
722 self['devices'] = cfg.get('devices', {})
723 self['console_refs'] = cfg.get('console_refs', [])
724 self['vif_refs'] = cfg.get('vif_refs', [])
725 self['vbd_refs'] = cfg.get('vbd_refs', [])
726 self['vtpm_refs'] = cfg.get('vtpm_refs', [])
728 # coalesce hvm vnc frame buffer with vfb config
729 if self.is_hvm() and int(self['platform'].get('vnc', 0)) != 0:
730 # add vfb device if it isn't there already
731 if not self.has_rfb():
732 dev_config = ['vfb']
733 dev_config.append(['type', 'vnc'])
734 # copy VNC related params from platform config to vfb dev conf
735 for key in ['vncpasswd', 'vncunused', 'vncdisplay',
736 'vnclisten']:
737 if key in self['platform']:
738 dev_config.append([key, self['platform'][key]])
740 self.device_add('vfb', cfg_sxp = dev_config)
743 def has_rfb(self):
744 for console_uuid in self['console_refs']:
745 if self['devices'][console_uuid][1].get('protocol') == 'rfb':
746 return True
747 if self['devices'][console_uuid][0] == 'vfb':
748 return True
749 return False
751 def _sxp_to_xapi_unsupported(self, sxp_cfg):
752 """Read in an SXP configuration object and populate
753 values are that not related directly supported in
754 the Xen API.
755 """
757 log.debug('_sxp_to_xapi_unsupported(%s)' % scrub_password(sxp_cfg))
759 # Parse and convert parameters used to configure
760 # the image (as well as HVM images)
761 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
762 if image_sxp:
763 image_type = sxp.name(image_sxp)
764 if image_type != 'hvm' and image_type != 'linux':
765 self['platform']['image_type'] = image_type
767 for key in XENAPI_PLATFORM_CFG:
768 val = sxp.child_value(image_sxp, key, None)
769 if val is not None and val != '':
770 self['platform'][key] = val
772 notes = sxp.children(image_sxp, 'notes')
773 if notes:
774 self['notes'] = self.notes_from_sxp(notes[0])
776 self._hvm_boot_params_from_sxp(image_sxp)
778 # extract backend value
780 backend = []
781 for c in sxp.children(sxp_cfg, 'backend'):
782 backend.append(sxp.name(sxp.child0(c)))
783 if backend:
784 self['backend'] = backend
786 # Parse and convert other Non Xen API parameters.
787 def _set_cfg_if_exists(sxp_arg):
788 val = sxp.child_value(sxp_cfg, sxp_arg)
789 if val != None:
790 if LEGACY_CFG_TYPES.get(sxp_arg):
791 self[sxp_arg] = LEGACY_CFG_TYPES[sxp_arg](val)
792 else:
793 self[sxp_arg] = val
795 _set_cfg_if_exists('shadow_memory')
796 _set_cfg_if_exists('features')
797 _set_cfg_if_exists('on_xend_stop')
798 _set_cfg_if_exists('on_xend_start')
799 _set_cfg_if_exists('vcpu_avail')
801 # Parse and store runtime configuration
802 _set_cfg_if_exists('start_time')
803 _set_cfg_if_exists('cpu_time')
804 _set_cfg_if_exists('shutdown_reason')
805 _set_cfg_if_exists('up_time')
806 _set_cfg_if_exists('status') # TODO, deprecated
808 def _get_old_state_string(self):
809 """Returns the old xm state string.
810 @rtype: string
811 @return: old state string
812 """
813 state_string = ''
814 for state_name in CONFIG_OLD_DOM_STATES:
815 on_off = self.get(state_name, 0)
816 if on_off:
817 state_string += state_name[0]
818 else:
819 state_string += '-'
821 return state_string
824 def update_config(self, dominfo):
825 """Update configuration with the output from xc.domain_getinfo().
827 @param dominfo: Domain information via xc.domain_getinfo()
828 @type dominfo: dict
829 """
830 self._dominfo_to_xapi(dominfo)
831 self.validate()
833 def update_with_xenapi_config(self, xapi):
834 """Update configuration with a Xen API VM struct
836 @param xapi: Xen API VM Struct
837 @type xapi: dict
838 """
840 log.debug('update_with_xenapi_config: %s' % scrub_password(xapi))
842 for key, val in xapi.items():
843 type_conv = XENAPI_CFG_TYPES.get(key)
844 if type_conv is None:
845 key = key.lower()
846 type_conv = XENAPI_CFG_TYPES.get(key)
847 if callable(type_conv):
848 self[key] = type_conv(val)
849 else:
850 self[key] = val
852 self['vcpus_params']['weight'] = \
853 int(self['vcpus_params'].get('weight', 256))
854 self['vcpus_params']['cap'] = int(self['vcpus_params'].get('cap', 0))
856 def to_sxp(self, domain = None, ignore_devices = False, ignore = [],
857 legacy_only = True):
858 """ Get SXP representation of this config object.
860 Incompat: removed store_mfn, console_mfn
862 @keyword domain: (optional) XendDomainInfo to get extra information
863 from such as domid and running devices.
864 @type domain: XendDomainInfo
865 @keyword ignore: (optional) list of 'keys' that we do not want
866 to export.
867 @type ignore: list of strings
868 @rtype: list of list (SXP representation)
869 """
870 sxpr = ['domain']
872 # TODO: domid/dom is the same thing but called differently
873 # depending if it is from xenstore or sxpr.
875 if domain.getDomid() is not None:
876 sxpr.append(['domid', domain.getDomid()])
878 if not legacy_only:
879 for name, typ in XENAPI_CFG_TYPES.items():
880 if name in self and self[name] not in (None, []):
881 if typ == dict:
882 s = self[name].items()
883 elif typ == list:
884 s = self[name]
885 else:
886 s = str(self[name])
887 sxpr.append([name, s])
889 for xenapi, legacy in XENAPI_CFG_TO_LEGACY_CFG.items():
890 if legacy in ('cpus'): # skip this
891 continue
892 if self.has_key(xenapi) and self[xenapi] not in (None, []):
893 if type(self[xenapi]) == bool:
894 # convert booleans to ints before making an sxp item
895 sxpr.append([legacy, int(self[xenapi])])
896 else:
897 sxpr.append([legacy, self[xenapi]])
899 MiB = 1024*1024
901 sxpr.append(["maxmem", int(self["memory_static_max"])/MiB])
902 sxpr.append(["memory", int(self["memory_dynamic_max"])/MiB])
904 for legacy in LEGACY_UNSUPPORTED_BY_XENAPI_CFG:
905 if legacy in ('domid', 'uuid', 'cpus'): # skip these
906 continue
907 if self.has_key(legacy) and self[legacy] not in (None, []):
908 sxpr.append([legacy, self[legacy]])
910 if self.has_key('security_label'):
911 sxpr.append(['security_label', self['security_label']])
913 sxpr.append(['image', self.image_sxpr()])
914 sxpr.append(['status', domain._stateGet()])
916 if domain.getDomid() is not None:
917 sxpr.append(['state', self._get_old_state_string()])
919 if domain:
920 if domain.store_mfn:
921 sxpr.append(['store_mfn', domain.store_mfn])
922 if domain.console_mfn:
923 sxpr.append(['console_mfn', domain.console_mfn])
926 # Marshall devices (running or from configuration)
927 if not ignore_devices:
928 txn = xstransact()
929 try:
930 for cls in XendDevices.valid_devices():
931 found = False
933 # figure if there is a dev controller is valid and running
934 if domain and domain.getDomid() != None:
935 try:
936 controller = domain.getDeviceController(cls)
937 configs = controller.configurations(txn)
938 for config in configs:
939 if sxp.name(config) in ('vbd', 'tap'):
940 # The bootable flag is never written to the
941 # store as part of the device config.
942 dev_uuid = sxp.child_value(config, 'uuid')
943 dev_type, dev_cfg = self['devices'][dev_uuid]
944 is_bootable = dev_cfg.get('bootable', 0)
945 config.append(['bootable', int(is_bootable)])
947 sxpr.append(['device', config])
949 found = True
950 except:
951 log.exception("dumping sxp from device controllers")
952 pass
954 # if we didn't find that device, check the existing config
955 # for a device in the same class
956 if not found:
957 for dev_type, dev_info in self.all_devices_sxpr():
958 if dev_type == cls:
959 sxpr.append(['device', dev_info])
961 txn.commit()
962 except:
963 txn.abort()
964 raise
966 return sxpr
968 def _blkdev_name_to_number(self, dev):
969 if 'ioemu:' in dev:
970 _, dev = dev.split(':', 1)
971 try:
972 dev, _ = dev.split(':', 1)
973 except ValueError:
974 pass
976 try:
977 devid = int(dev)
978 except ValueError:
979 # devid is not a number but a string containing either device
980 # name (e.g. xvda) or device_type/device_id (e.g. vbd/51728)
981 dev2 = type(dev) is str and dev.split('/')[-1] or None
982 if dev2 == None:
983 log.debug("Could not check the device %s", dev)
984 return None
985 try:
986 devid = int(dev2)
987 except ValueError:
988 devid = blkdev_name_to_number(dev2)
989 if devid == None:
990 log.debug("The device %s is not device name", dev2)
991 return None
992 return devid
994 def device_duplicate_check(self, dev_type, dev_info, defined_config):
995 defined_devices_sxpr = self.all_devices_sxpr(target = defined_config)
997 if dev_type == 'vbd' or dev_type == 'tap':
998 dev_uname = dev_info.get('uname')
999 blkdev_name = dev_info.get('dev')
1000 devid = self._blkdev_name_to_number(blkdev_name)
1001 if devid == None or dev_uname == None:
1002 return
1004 for o_dev_type, o_dev_info in defined_devices_sxpr:
1005 if o_dev_type == 'vbd' or o_dev_type == 'tap':
1006 blkdev_file = blkdev_uname_to_file(dev_uname)
1007 o_dev_uname = sxp.child_value(o_dev_info, 'uname')
1008 o_blkdev_file = blkdev_uname_to_file(o_dev_uname)
1009 if blkdev_file == o_blkdev_file:
1010 raise XendConfigError('The file "%s" is already used' %
1011 blkdev_file)
1012 o_blkdev_name = sxp.child_value(o_dev_info, 'dev')
1013 o_devid = self._blkdev_name_to_number(o_blkdev_name)
1014 if o_devid != None and devid == o_devid:
1015 raise XendConfigError('The device "%s" is already defined' %
1016 blkdev_name)
1018 elif dev_type == 'vif':
1019 dev_mac = dev_info.get('mac')
1021 for o_dev_type, o_dev_info in defined_devices_sxpr:
1022 if dev_type == o_dev_type:
1023 if dev_mac.lower() == sxp.child_value(o_dev_info, 'mac').lower():
1024 raise XendConfigError('The mac "%s" is already defined' %
1025 dev_mac)
1027 def device_add(self, dev_type, cfg_sxp = None, cfg_xenapi = None,
1028 target = None):
1029 """Add a device configuration in SXP format or XenAPI struct format.
1031 For SXP, it could be either:
1033 [device, [vbd, [uname ...]]
1035 or:
1037 [vbd, [uname ..]]
1039 @type cfg_sxp: list of lists (parsed sxp object)
1040 @param cfg_sxp: SXP configuration object
1041 @type cfg_xenapi: dict
1042 @param cfg_xenapi: A device configuration from Xen API (eg. vbd,vif)
1043 @param target: write device information to
1044 @type target: None or a dictionary
1045 @rtype: string
1046 @return: Assigned UUID of the device.
1047 """
1048 if target == None:
1049 target = self
1051 if dev_type not in XendDevices.valid_devices():
1052 raise XendConfigError("XendConfig: %s not a valid device type" %
1053 dev_type)
1055 if cfg_sxp == None and cfg_xenapi == None:
1056 raise XendConfigError("XendConfig: device_add requires some "
1057 "config.")
1059 #if cfg_sxp:
1060 # log.debug("XendConfig.device_add: %s" % str(cfg_sxp))
1061 #if cfg_xenapi:
1062 # log.debug("XendConfig.device_add: %s" % str(cfg_xenapi))
1064 if cfg_sxp:
1065 if sxp.child0(cfg_sxp) == 'device':
1066 config = sxp.child0(cfg_sxp)
1067 else:
1068 config = cfg_sxp
1070 dev_type = sxp.name(config)
1071 dev_info = {}
1073 # Parsing the device SXP's. In most cases, the SXP looks
1074 # like this:
1076 # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]]
1078 # However, for PCI devices it looks like this:
1080 # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1]]]]
1082 # It seems the reasoning for this difference is because
1083 # pciif.py needs all the PCI device configurations at
1084 # the same time when creating the devices.
1086 # To further complicate matters, Xen 2.0 configuration format
1087 # uses the following for pci device configuration:
1089 # [device, [pci, [domain, 0], [bus, 0], [dev, 1], [func, 2]]]
1091 if dev_type == 'pci':
1092 pci_devs_uuid = sxp.child_value(config, 'uuid',
1093 uuid.createString())
1094 pci_devs = []
1095 for pci_dev in sxp.children(config, 'dev'):
1096 pci_dev_info = {}
1097 for opt_val in pci_dev[1:]:
1098 try:
1099 opt, val = opt_val
1100 pci_dev_info[opt] = val
1101 except TypeError:
1102 pass
1103 pci_devs.append(pci_dev_info)
1104 target['devices'][pci_devs_uuid] = (dev_type,
1105 {'devs': pci_devs,
1106 'uuid': pci_devs_uuid})
1108 log.debug("XendConfig: reading device: %s" % pci_devs)
1109 return pci_devs_uuid
1111 for opt_val in config[1:]:
1112 try:
1113 opt, val = opt_val
1114 dev_info[opt] = val
1115 except (TypeError, ValueError): # unpack error
1116 pass
1118 if dev_type == 'vbd':
1119 dev_info['bootable'] = 0
1120 if dev_info.get('dev', '').startswith('ioemu:'):
1121 dev_info['driver'] = 'ioemu'
1122 else:
1123 dev_info['driver'] = 'paravirtualised'
1125 if dev_type == 'tap':
1126 if dev_info['uname'].split(':')[1] not in blktap_disk_types:
1127 raise XendConfigError("tap:%s not a valid disk type" %
1128 dev_info['uname'].split(':')[1])
1130 if dev_type == 'vif':
1131 if not dev_info.get('mac'):
1132 dev_info['mac'] = randomMAC()
1134 self.device_duplicate_check(dev_type, dev_info, target)
1136 if dev_type == 'vif':
1137 if dev_info.get('policy') and dev_info.get('label'):
1138 dev_info['security_label'] = "%s:%s:%s" % \
1139 (xsconstants.ACM_POLICY_ID,
1140 dev_info['policy'],dev_info['label'])
1142 # create uuid if it doesn't exist
1143 dev_uuid = dev_info.get('uuid', None)
1144 if not dev_uuid:
1145 dev_uuid = uuid.createString()
1146 dev_info['uuid'] = dev_uuid
1148 # store dev references by uuid for certain device types
1149 target['devices'][dev_uuid] = (dev_type, dev_info)
1150 if dev_type in ('vif', 'vbd', 'vtpm'):
1151 param = '%s_refs' % dev_type
1152 if param not in target:
1153 target[param] = []
1154 if dev_uuid not in target[param]:
1155 if dev_type == 'vbd':
1156 # Compat hack -- mark first disk bootable
1157 dev_info['bootable'] = int(not target[param])
1158 target[param].append(dev_uuid)
1159 elif dev_type == 'tap':
1160 if 'vbd_refs' not in target:
1161 target['vbd_refs'] = []
1162 if dev_uuid not in target['vbd_refs']:
1163 # Compat hack -- mark first disk bootable
1164 dev_info['bootable'] = int(not target['vbd_refs'])
1165 target['vbd_refs'].append(dev_uuid)
1167 elif dev_type == 'vfb':
1168 # Populate other config with aux data that is associated
1169 # with vfb
1171 other_config = {}
1172 for key in XENAPI_CONSOLE_OTHER_CFG:
1173 if key in dev_info:
1174 other_config[key] = dev_info[key]
1175 target['devices'][dev_uuid][1]['other_config'] = other_config
1178 if 'console_refs' not in target:
1179 target['console_refs'] = []
1181 # Treat VFB devices as console devices so they are found
1182 # through Xen API
1183 if dev_uuid not in target['console_refs']:
1184 target['console_refs'].append(dev_uuid)
1186 elif dev_type == 'console':
1187 if 'console_refs' not in target:
1188 target['console_refs'] = []
1189 if dev_uuid not in target['console_refs']:
1190 target['console_refs'].append(dev_uuid)
1192 log.debug("XendConfig: reading device: %s" % scrub_password(dev_info))
1193 return dev_uuid
1195 if cfg_xenapi:
1196 dev_info = {}
1197 dev_uuid = ''
1198 if dev_type == 'vif':
1199 dev_info['mac'] = cfg_xenapi.get('MAC')
1200 if not dev_info['mac']:
1201 dev_info['mac'] = randomMAC()
1202 # vifname is the name on the guest, not dom0
1203 # TODO: we don't have the ability to find that out or
1204 # change it from dom0
1205 #if cfg_xenapi.get('device'): # don't add if blank
1206 # dev_info['vifname'] = cfg_xenapi.get('device')
1207 if cfg_xenapi.get('type'):
1208 dev_info['type'] = cfg_xenapi.get('type')
1209 if cfg_xenapi.get('name'):
1210 dev_info['name'] = cfg_xenapi.get('name')
1211 if cfg_xenapi.get('network'):
1212 network = XendAPIStore.get(
1213 cfg_xenapi.get('network'), 'network')
1214 dev_info['bridge'] = network.get_name_label()
1216 if cfg_xenapi.get('security_label'):
1217 dev_info['security_label'] = \
1218 cfg_xenapi.get('security_label')
1220 dev_uuid = cfg_xenapi.get('uuid', None)
1221 if not dev_uuid:
1222 dev_uuid = uuid.createString()
1223 dev_info['uuid'] = dev_uuid
1224 target['devices'][dev_uuid] = (dev_type, dev_info)
1225 target['vif_refs'].append(dev_uuid)
1227 elif dev_type in ('vbd', 'tap'):
1228 dev_info['type'] = cfg_xenapi.get('type', 'Disk')
1229 if dev_info['type'] == 'CD':
1230 old_vbd_type = 'cdrom'
1231 else:
1232 old_vbd_type = 'disk'
1234 dev_info['uname'] = cfg_xenapi.get('image', '')
1235 dev_info['dev'] = '%s:%s' % (cfg_xenapi.get('device'),
1236 old_vbd_type)
1237 dev_info['bootable'] = int(cfg_xenapi.get('bootable', 0))
1238 dev_info['driver'] = cfg_xenapi.get('driver', '')
1239 dev_info['VDI'] = cfg_xenapi.get('VDI', '')
1241 if cfg_xenapi.get('mode') == 'RW':
1242 dev_info['mode'] = 'w'
1243 else:
1244 dev_info['mode'] = 'r'
1246 dev_uuid = cfg_xenapi.get('uuid', None)
1247 if not dev_uuid:
1248 dev_uuid = uuid.createString()
1249 dev_info['uuid'] = dev_uuid
1250 target['devices'][dev_uuid] = (dev_type, dev_info)
1251 target['vbd_refs'].append(dev_uuid)
1253 elif dev_type == 'vtpm':
1254 if cfg_xenapi.get('type'):
1255 dev_info['type'] = cfg_xenapi.get('type')
1257 dev_uuid = cfg_xenapi.get('uuid', None)
1258 if not dev_uuid:
1259 dev_uuid = uuid.createString()
1260 dev_info['uuid'] = dev_uuid
1261 dev_info['other_config'] = cfg_xenapi.get('other_config', {})
1262 target['devices'][dev_uuid] = (dev_type, dev_info)
1263 target['vtpm_refs'].append(dev_uuid)
1265 elif dev_type == 'console':
1266 dev_uuid = cfg_xenapi.get('uuid', None)
1267 if not dev_uuid:
1268 dev_uuid = uuid.createString()
1269 dev_info['uuid'] = dev_uuid
1270 dev_info['protocol'] = cfg_xenapi.get('protocol', 'rfb')
1271 dev_info['other_config'] = cfg_xenapi.get('other_config', {})
1272 if dev_info['protocol'] == 'rfb':
1273 # collapse other config into devinfo for things
1274 # such as vncpasswd, vncunused, etc.
1275 dev_info.update(cfg_xenapi.get('other_config', {}))
1276 dev_info['type'] = 'vnc'
1277 target['devices'][dev_uuid] = ('vfb', dev_info)
1278 target['console_refs'].append(dev_uuid)
1280 # Finally, if we are a pvfb, we need to make a vkbd
1281 # as well that is not really exposed to Xen API
1282 vkbd_uuid = uuid.createString()
1283 target['devices'][vkbd_uuid] = ('vkbd', {})
1285 elif dev_info['protocol'] == 'vt100':
1286 # if someone tries to create a VT100 console
1287 # via the Xen API, we'll have to ignore it
1288 # because we create one automatically in
1289 # XendDomainInfo._update_consoles
1290 raise XendConfigError('Creating vt100 consoles via '
1291 'Xen API is unsupported')
1293 return dev_uuid
1295 # no valid device to add
1296 return ''
1298 def phantom_device_add(self, dev_type, cfg_xenapi = None,
1299 target = None):
1300 """Add a phantom tap device configuration in XenAPI struct format.
1301 """
1303 if target == None:
1304 target = self
1306 if dev_type not in XendDevices.valid_devices() and \
1307 dev_type not in XendDevices.pseudo_devices():
1308 raise XendConfigError("XendConfig: %s not a valid device type" %
1309 dev_type)
1311 if cfg_xenapi == None:
1312 raise XendConfigError("XendConfig: device_add requires some "
1313 "config.")
1315 if cfg_xenapi:
1316 log.debug("XendConfig.phantom_device_add: %s" % str(cfg_xenapi))
1318 if cfg_xenapi:
1319 dev_info = {}
1320 if dev_type in ('vbd', 'tap'):
1321 if dev_type == 'vbd':
1322 dev_info['uname'] = cfg_xenapi.get('image', '')
1323 dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
1324 elif dev_type == 'tap':
1325 if cfg_xenapi.get('image').find('tap:') == -1:
1326 dev_info['uname'] = 'tap:qcow:%s' % cfg_xenapi.get('image')
1327 dev_info['dev'] = '/dev/%s' % cfg_xenapi.get('device')
1328 dev_info['uname'] = cfg_xenapi.get('image')
1329 dev_info['mode'] = cfg_xenapi.get('mode')
1330 dev_info['backend'] = '0'
1331 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
1332 dev_info['uuid'] = dev_uuid
1333 self['devices'][dev_uuid] = (dev_type, dev_info)
1334 self['vbd_refs'].append(dev_uuid)
1335 return dev_uuid
1337 return ''
1339 def console_add(self, protocol, location, other_config = {}):
1340 dev_uuid = uuid.createString()
1341 if protocol == 'vt100':
1342 dev_info = {
1343 'uuid': dev_uuid,
1344 'protocol': protocol,
1345 'location': location,
1346 'other_config': other_config,
1349 if 'devices' not in self:
1350 self['devices'] = {}
1352 self['devices'][dev_uuid] = ('console', dev_info)
1353 self['console_refs'].append(dev_uuid)
1354 return dev_info
1356 return {}
1358 def console_update(self, console_uuid, key, value):
1359 for dev_uuid, (dev_type, dev_info) in self['devices'].items():
1360 if dev_uuid == console_uuid:
1361 dev_info[key] = value
1362 # collapse other_config into dev_info for things
1363 # such as vncpasswd, vncunused, etc.
1364 if key == 'other_config':
1365 for k in XENAPI_CONSOLE_OTHER_CFG:
1366 if k in dev_info and k not in value:
1367 del dev_info[k]
1368 dev_info.update(value)
1369 break
1371 def console_get_all(self, protocol):
1372 if protocol == 'vt100':
1373 consoles = [dinfo for dtype, dinfo in self['devices'].values()
1374 if dtype == 'console']
1375 return [c for c in consoles if c.get('protocol') == protocol]
1377 elif protocol == 'rfb':
1378 vfbs = [dinfo for dtype, dinfo in self['devices'].values()
1379 if dtype == 'vfb']
1381 # move all non-console key values to other_config before
1382 # returning console config
1383 valid_keys = ['uuid', 'location']
1384 for vfb in vfbs:
1385 other_config = {}
1386 for key, val in vfb.items():
1387 if key not in valid_keys:
1388 other_config[key] = vfb[key]
1389 del vfb[key]
1390 vfb['other_config'] = other_config
1391 vfb['protocol'] = 'rfb'
1393 return vfbs
1395 else:
1396 return []
1398 def device_update(self, dev_uuid, cfg_sxp = [], cfg_xenapi = {}):
1399 """Update an existing device with the new configuration.
1401 @rtype: boolean
1402 @return: Returns True if succesfully found and updated a device conf
1403 """
1404 if dev_uuid in self['devices'] and cfg_sxp:
1405 if sxp.child0(cfg_sxp) == 'device':
1406 config = sxp.child0(cfg_sxp)
1407 else:
1408 config = cfg_sxp
1410 dev_type, dev_info = self['devices'][dev_uuid]
1411 for opt_val in config[1:]:
1412 try:
1413 opt, val = opt_val
1414 dev_info[opt] = val
1415 except (TypeError, ValueError):
1416 pass # no value for this config option
1418 self['devices'][dev_uuid] = (dev_type, dev_info)
1419 return True
1421 elif dev_uuid in self['devices'] and cfg_xenapi:
1422 dev_type, dev_info = self['devices'][dev_uuid]
1423 for key, val in cfg_xenapi.items():
1424 dev_info[key] = val
1425 self['devices'][dev_uuid] = (dev_type, dev_info)
1427 return False
1430 def device_sxpr(self, dev_uuid = None, dev_type = None, dev_info = None, target = None):
1431 """Get Device SXPR by either giving the device UUID or (type, config).
1433 @rtype: list of lists
1434 @return: device config sxpr
1435 """
1436 sxpr = []
1438 if target == None:
1439 target = self
1441 if dev_uuid != None and dev_uuid in target['devices']:
1442 dev_type, dev_info = target['devices'][dev_uuid]
1444 if dev_type == None or dev_info == None:
1445 raise XendConfigError("Required either UUID or device type and "
1446 "configuration dictionary.")
1448 sxpr.append(dev_type)
1449 if dev_type in ('console', 'vfb'):
1450 config = [(opt, val) for opt, val in dev_info.items()
1451 if opt != 'other_config']
1452 else:
1453 config = [(opt, val) for opt, val in dev_info.items()]
1455 sxpr += config
1457 return sxpr
1459 def ordered_device_refs(self, target = None):
1460 result = []
1462 if target == None:
1463 target = self
1465 # vkbd devices *must* be before vfb devices, otherwise
1466 # there is a race condition when setting up devices
1467 # where the daemon spawned for the vfb may write stuff
1468 # into xenstore vkbd backend, before DevController has
1469 # setup permissions on the vkbd backend path. This race
1470 # results in domain creation failing with 'device already
1471 # connected' messages
1472 result.extend([u for u in target['devices'].keys() if target['devices'][u][0] == 'vkbd'])
1474 result.extend(target.get('console_refs', []) +
1475 target.get('vbd_refs', []) +
1476 target.get('vif_refs', []) +
1477 target.get('vtpm_refs', []))
1479 result.extend([u for u in target['devices'].keys() if u not in result])
1480 return result
1482 def all_devices_sxpr(self, target = None):
1483 """Returns the SXPR for all devices in the current configuration."""
1484 sxprs = []
1485 pci_devs = []
1487 if target == None:
1488 target = self
1490 if 'devices' not in target:
1491 return sxprs
1493 ordered_refs = self.ordered_device_refs(target = target)
1494 for dev_uuid in ordered_refs:
1495 dev_type, dev_info = target['devices'][dev_uuid]
1496 if dev_type == 'pci': # special case for pci devices
1497 sxpr = ['pci', ['uuid', dev_info['uuid']]]
1498 for pci_dev_info in dev_info['devs']:
1499 pci_dev_sxpr = ['dev']
1500 for opt, val in pci_dev_info.items():
1501 pci_dev_sxpr.append([opt, val])
1502 sxpr.append(pci_dev_sxpr)
1503 sxprs.append((dev_type, sxpr))
1504 else:
1505 sxpr = self.device_sxpr(dev_type = dev_type,
1506 dev_info = dev_info,
1507 target = target)
1508 sxprs.append((dev_type, sxpr))
1510 return sxprs
1512 def image_sxpr(self):
1513 """Returns a backwards compatible image SXP expression that is
1514 used in xenstore's /vm/<uuid>/image value and xm list."""
1515 image = [self.image_type()]
1516 if self.has_key('PV_kernel'):
1517 image.append(['kernel', self['PV_kernel']])
1518 if self.has_key('PV_ramdisk') and self['PV_ramdisk']:
1519 image.append(['ramdisk', self['PV_ramdisk']])
1520 if self.has_key('PV_args') and self['PV_args']:
1521 image.append(['args', self['PV_args']])
1523 for key in XENAPI_PLATFORM_CFG:
1524 if key in self['platform']:
1525 image.append([key, self['platform'][key]])
1527 if 'notes' in self:
1528 image.append(self.notes_sxp(self['notes']))
1530 return image
1532 def update_with_image_sxp(self, image_sxp, bootloader = False):
1533 # Convert Legacy "image" config to Xen API PV_*
1534 # configuration
1535 log.debug("update_with_image_sxp(%s)" % scrub_password(image_sxp))
1537 # user-specified args must come last: previous releases did this and
1538 # some domU kernels rely upon the ordering.
1539 kernel_args = sxp.child_value(image_sxp, 'args', '')
1541 # attempt to extract extra arguments from SXP config
1542 arg_ip = sxp.child_value(image_sxp, 'ip')
1543 if arg_ip and not re.search(r'ip=[^ ]+', kernel_args):
1544 kernel_args = 'ip=%s ' % arg_ip + kernel_args
1545 arg_root = sxp.child_value(image_sxp, 'root')
1546 if arg_root and not re.search(r'root=', kernel_args):
1547 kernel_args = 'root=%s ' % arg_root + kernel_args
1549 if bootloader:
1550 self['_temp_using_bootloader'] = '1'
1551 self['_temp_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1552 self['_temp_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1553 self['_temp_args'] = kernel_args
1554 else:
1555 self['PV_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1556 self['PV_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1557 self['PV_args'] = kernel_args
1559 for key in XENAPI_PLATFORM_CFG:
1560 val = sxp.child_value(image_sxp, key, None)
1561 if val is not None and val != '':
1562 self['platform'][key] = val
1564 notes = sxp.children(image_sxp, 'notes')
1565 if notes:
1566 self['notes'] = self.notes_from_sxp(notes[0])
1568 self._hvm_boot_params_from_sxp(image_sxp)
1570 def set_notes(self, notes):
1571 'Add parsed elfnotes to image'
1572 self['notes'] = notes
1574 def get_notes(self):
1575 try:
1576 return self['notes'] or {}
1577 except KeyError:
1578 return {}
1580 def notes_from_sxp(self, nsxp):
1581 notes = {}
1582 for note in sxp.children(nsxp):
1583 notes[note[0]] = note[1]
1584 return notes
1586 def notes_sxp(self, notes):
1587 nsxp = ['notes']
1588 for k, v in notes.iteritems():
1589 nsxp.append([k, str(v)])
1590 return nsxp
1592 def _hvm_boot_params_from_sxp(self, image_sxp):
1593 boot = sxp.child_value(image_sxp, 'boot', None)
1594 if boot is not None:
1595 self['HVM_boot_policy'] = 'BIOS order'
1596 self['HVM_boot_params'] = { 'order' : boot }
1598 def is_hvm(self):
1599 return self['HVM_boot_policy'] != ''
1601 def target(self):
1602 return self['target']
1604 def image_type(self):
1605 stored_type = self['platform'].get('image_type')
1606 return stored_type or (self.is_hvm() and 'hvm' or 'linux')
1608 def is_hap(self):
1609 return self['platform'].get('hap', 0)