ia64/xen-unstable

view tools/python/xen/xend/XendConfig.py @ 17227:939ceafa5ef9

Initialization of new domU config options via XenAPI

Many of the new domU config options related to hvm guests (e.g. hpet,
rtc_timeoffset, etc.) are not initialized with default values via
XenAPI, which prevents starting an hvm domU created through XenAPI.
This patch ensures the new options are set with appropriate default
values in XendConfig platform sanity check.

Signed-off-by: Jim Fehlig <jfehlig@novell.com>
author Keir Fraser <keir.fraser@citrix.com>
date Tue Mar 18 11:05:53 2008 +0000 (2008-03-18)
parents 209512f6d89c
children 06dbce4a60f9
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', 'hpet', '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 'coredump-destroy', 'coredump-restart')
247 CONFIG_OLD_DOM_STATES = ('running', 'blocked', 'paused', 'shutdown',
248 'crashed', 'dying')
250 class XendConfigError(VmError):
251 def __str__(self):
252 return 'Invalid Configuration: %s' % str(self.value)
254 ##
255 ## XendConfig Class (an extended dictionary)
256 ##
258 class XendConfig(dict):
259 """ The new Xend VM Configuration.
261 Stores the configuration in xenapi compatible format but retains
262 import and export functions for SXP.
263 """
264 def __init__(self, filename = None, sxp_obj = None,
265 xapi = None, dominfo = None):
267 dict.__init__(self)
268 self.update(self._defaults())
270 if filename:
271 try:
272 sxp_obj = sxp.parse(open(filename,'r'))
273 sxp_obj = sxp_obj[0]
274 except IOError, e:
275 raise XendConfigError("Unable to read file: %s" % filename)
277 if sxp_obj:
278 self._sxp_to_xapi(sxp_obj)
279 self._sxp_to_xapi_unsupported(sxp_obj)
280 elif xapi:
281 self.update_with_xenapi_config(xapi)
282 elif dominfo:
283 # output from xc.domain_getinfo
284 self._dominfo_to_xapi(dominfo, update_mem = True)
286 log.debug('XendConfig.init: %s' % scrub_password(self))
288 # validators go here
289 self.validate()
291 """ In time, we should enable this type checking addition. It is great
292 also for tracking bugs and unintended writes to XendDomainInfo.info
293 def __setitem__(self, key, value):
294 type_conv = XENAPI_CFG_TYPES.get(key)
295 if callable(type_conv):
296 try:
297 dict.__setitem__(self, key, type_conv(value))
298 except (ValueError, TypeError):
299 raise XendConfigError("Wrong type for configuration value " +
300 "%s. Expected %s" %
301 (key, type_conv.__name__))
302 else:
303 dict.__setitem__(self, key, value)
304 """
306 def _defaults(self):
307 defaults = {
308 'name_label': 'Domain-Unnamed',
309 'actions_after_shutdown': 'destroy',
310 'actions_after_reboot': 'restart',
311 'actions_after_crash': 'restart',
312 'actions_after_suspend': '',
313 'is_a_template': False,
314 'is_control_domain': False,
315 'features': '',
316 'PV_bootloader': '',
317 'PV_kernel': '',
318 'PV_ramdisk': '',
319 'PV_args': '',
320 'PV_bootloader_args': '',
321 'HVM_boot_policy': '',
322 'HVM_boot_params': {},
323 'memory_static_min': 0,
324 'memory_dynamic_min': 0,
325 'shadow_memory': 0,
326 'memory_static_max': 0,
327 'memory_dynamic_max': 0,
328 'devices': {},
329 'on_xend_start': 'ignore',
330 'on_xend_stop': 'ignore',
331 'cpus': [],
332 'VCPUs_max': 1,
333 'VCPUs_live': 1,
334 'VCPUs_at_startup': 1,
335 'vcpus_params': {},
336 'console_refs': [],
337 'vif_refs': [],
338 'vbd_refs': [],
339 'vtpm_refs': [],
340 'other_config': {},
341 'platform': {},
342 'target': 0,
343 }
345 return defaults
347 #
348 # Here we assume these values exist in the dict.
349 # If they don't we have a bigger problem, lets not
350 # try and 'fix it up' but acutually fix the cause ;-)
351 #
352 def _memory_sanity_check(self):
353 log.trace("_memory_sanity_check memory_static_min: %s, "
354 "memory_static_max: %i, "
355 "memory_dynamic_min: %i, "
356 "memory_dynamic_max: %i",
357 self["memory_static_min"],
358 self["memory_static_max"],
359 self["memory_dynamic_min"],
360 self["memory_dynamic_max"])
362 if not self["memory_static_min"] <= self["memory_static_max"]:
363 raise XendConfigError("memory_static_min must be less " \
364 "than or equal to memory_static_max")
365 if not self["memory_static_min"] <= self["memory_dynamic_min"]:
366 raise XendConfigError("memory_static_min must be less " \
367 "than or equal to memory_dynamic_min")
368 if not self["memory_dynamic_max"] <= self["memory_static_max"]:
369 raise XendConfigError("memory_dynamic_max must be less " \
370 "than or equal to memory_static_max")
371 if not self["memory_dynamic_max"] > 0:
372 raise XendConfigError("memory_dynamic_max must be greater " \
373 "than zero")
374 if not self["memory_static_max"] > 0:
375 raise XendConfigError("memory_static_max must be greater " \
376 "than zero")
378 def _actions_sanity_check(self):
379 for event in ['shutdown', 'reboot', 'crash']:
380 if self['actions_after_' + event] not in CONFIG_RESTART_MODES:
381 raise XendConfigError('Invalid event handling mode: ' +
382 event)
384 def _vcpus_sanity_check(self):
385 if 'VCPUs_max' in self and 'vcpu_avail' not in self:
386 self['vcpu_avail'] = (1 << self['VCPUs_max']) - 1
388 def _uuid_sanity_check(self):
389 """Make sure UUID is in proper string format with hyphens."""
390 if 'uuid' not in self or not self['uuid']:
391 self['uuid'] = uuid.createString()
392 else:
393 self['uuid'] = uuid.toString(uuid.fromString(self['uuid']))
395 def _name_sanity_check(self):
396 if 'name_label' not in self:
397 self['name_label'] = 'Domain-' + self['uuid']
399 def _platform_sanity_check(self):
400 if 'keymap' not in self['platform'] and XendOptions.instance().get_keymap():
401 self['platform']['keymap'] = XendOptions.instance().get_keymap()
403 if self.is_hvm() or self.has_rfb():
404 if 'device_model' not in self['platform']:
405 self['platform']['device_model'] = xen.util.auxbin.pathTo("qemu-dm")
407 if self.is_hvm():
408 if 'timer_mode' not in self['platform']:
409 self['platform']['timer_mode'] = 0
410 if 'rtc_timeoffset' not in self['platform']:
411 self['platform']['rtc_timeoffset'] = 0
412 if 'hpet' not in self['platform']:
413 self['platform']['hpet'] = 0
414 if 'loader' not in self['platform']:
415 # Old configs may have hvmloader set as PV_kernel param
416 if self.has_key('PV_kernel') and re.search('hvmloader', self['PV_kernel']):
417 self['platform']['loader'] = self['PV_kernel']
418 self['PV_kernel'] = ''
419 else:
420 self['platform']['loader'] = "/usr/lib/xen/boot/hvmloader"
421 log.debug("Loader is %s" % str(self['platform']['loader']))
423 # Compatibility hack, can go away soon.
424 if 'soundhw' not in self['platform'] and \
425 self['platform'].get('enable_audio'):
426 self['platform']['soundhw'] = 'sb16'
428 def validate(self):
429 self._uuid_sanity_check()
430 self._name_sanity_check()
431 self._memory_sanity_check()
432 self._actions_sanity_check()
433 self._vcpus_sanity_check()
434 self._platform_sanity_check()
436 def _dominfo_to_xapi(self, dominfo, update_mem = False):
437 self['domid'] = dominfo['domid']
438 self['online_vcpus'] = dominfo['online_vcpus']
439 self['VCPUs_max'] = dominfo['max_vcpu_id'] + 1
441 if update_mem:
442 self['memory_dynamic_min'] = dominfo['mem_kb'] * 1024
443 self['memory_dynamic_max'] = dominfo['mem_kb'] * 1024
444 self['memory_static_min'] = 0
445 self['memory_static_max'] = dominfo['maxmem_kb'] * 1024
446 self._memory_sanity_check()
448 self['cpu_time'] = dominfo['cpu_time']/1e9
449 if dominfo.get('ssidref'):
450 ssidref = int(dominfo.get('ssidref'))
451 import xen.util.xsm.xsm as security
452 self['security_label'] = security.ssidref2security_label(ssidref)
454 self['shutdown_reason'] = dominfo['shutdown_reason']
456 # parse state into Xen API states
457 self['running'] = dominfo['running']
458 self['crashed'] = dominfo['crashed']
459 self['dying'] = dominfo['dying']
460 self['shutdown'] = dominfo['shutdown']
461 self['paused'] = dominfo['paused']
462 self['blocked'] = dominfo['blocked']
464 if 'name' in dominfo:
465 self['name_label'] = dominfo['name']
467 if 'handle' in dominfo:
468 self['uuid'] = uuid.toString(dominfo['handle'])
470 def _parse_sxp(self, sxp_cfg):
471 """ Populate this XendConfig using the parsed SXP.
473 @param sxp_cfg: Parsed SXP Configuration
474 @type sxp_cfg: list of lists
475 @rtype: dictionary
476 @return: A dictionary containing the parsed options of the SXP.
477 """
478 cfg = {}
480 for key, typ in XENAPI_CFG_TYPES.items():
481 val = sxp.child_value(sxp_cfg, key)
482 if val is not None:
483 try:
484 cfg[key] = typ(val)
485 except (ValueError, TypeError), e:
486 log.warn('Unable to convert type value for key: %s' % key)
488 # Convert deprecated options to current equivalents.
490 restart = sxp.child_value(sxp_cfg, 'restart')
491 if restart:
492 if restart == 'onreboot':
493 cfg['on_poweroff'] = 'destroy'
494 cfg['on_reboot'] = 'restart'
495 cfg['on_crash'] = 'destroy'
496 elif restart == 'always':
497 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
498 cfg[opt] = 'restart'
499 elif restart == 'never':
500 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
501 cfg[opt] = 'never'
502 else:
503 log.warn('Ignoring unrecognised value for deprecated option:'
504 'restart = \'%s\'', restart)
506 # Handle memory, passed in as MiB
508 if sxp.child_value(sxp_cfg, "memory") != None:
509 cfg["memory"] = int(sxp.child_value(sxp_cfg, "memory"))
510 if sxp.child_value(sxp_cfg, "maxmem") != None:
511 cfg["maxmem"] = int(sxp.child_value(sxp_cfg, "maxmem"))
513 # Convert scheduling parameters to vcpus_params
514 if 'vcpus_params' not in cfg:
515 cfg['vcpus_params'] = {}
516 cfg["vcpus_params"]["weight"] = \
517 int(sxp.child_value(sxp_cfg, "cpu_weight", 256))
518 cfg["vcpus_params"]["cap"] = \
519 int(sxp.child_value(sxp_cfg, "cpu_cap", 0))
521 # Only extract options we know about.
522 extract_keys = LEGACY_UNSUPPORTED_BY_XENAPI_CFG + \
523 XENAPI_CFG_TO_LEGACY_CFG.values()
525 for key in extract_keys:
526 val = sxp.child_value(sxp_cfg, key)
527 if val != None:
528 try:
529 cfg[key] = LEGACY_CFG_TYPES[key](val)
530 except KeyError:
531 cfg[key] = val
532 except (TypeError, ValueError), e:
533 log.warn("Unable to parse key %s: %s: %s" %
534 (key, str(val), e))
536 if 'platform' not in cfg:
537 cfg['platform'] = {}
538 localtime = sxp.child_value(sxp_cfg, 'localtime')
539 if localtime is not None:
540 cfg['platform']['localtime'] = localtime
542 # Compatibility hack -- can go soon.
543 for key in XENAPI_PLATFORM_CFG:
544 val = sxp.child_value(sxp_cfg, "platform_" + key, None)
545 if val is not None:
546 self['platform'][key] = val
548 # Compatibility hack -- can go soon.
549 boot_order = sxp.child_value(sxp_cfg, 'HVM_boot')
550 if boot_order:
551 cfg['HVM_boot_policy'] = 'BIOS order'
552 cfg['HVM_boot_params'] = { 'order' : boot_order }
555 # Parsing the device SXP's.
556 cfg['devices'] = {}
557 for dev in sxp.children(sxp_cfg, 'device'):
558 config = sxp.child0(dev)
559 dev_type = sxp.name(config)
560 self.device_add(dev_type, cfg_sxp = config, target = cfg)
562 # Extract missing data from configuration entries
563 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
564 if image_sxp:
565 image_vcpus = sxp.child_value(image_sxp, 'vcpus')
566 if image_vcpus != None:
567 try:
568 if 'VCPUs_max' not in cfg:
569 cfg['VCPUs_max'] = int(image_vcpus)
570 elif cfg['VCPUs_max'] != int(image_vcpus):
571 cfg['VCPUs_max'] = int(image_vcpus)
572 log.warn('Overriding vcpus from %d to %d using image'
573 'vcpus value.', cfg['VCPUs_max'])
574 except ValueError, e:
575 raise XendConfigError('integer expeceted: %s: %s' %
576 image_sxp, e)
578 # Deprecated cpu configuration
579 if 'cpu' in cfg:
580 if 'cpus' in cfg:
581 cfg['cpus'] = "%s,%s" % (str(cfg['cpu']), cfg['cpus'])
582 else:
583 cfg['cpus'] = str(cfg['cpu'])
585 # Convert 'cpus' to list of ints
586 if 'cpus' in cfg:
587 cpus = []
588 if type(cfg['cpus']) == list:
589 # If sxp_cfg was created from config.sxp,
590 # the form of 'cpus' is list of string.
591 # Convert 'cpus' to list of ints.
592 # ['1'] -> [1]
593 # ['0','2','3'] -> [0,2,3]
594 try:
595 for c in cfg['cpus']:
596 cpus.append(int(c))
598 cfg['cpus'] = cpus
599 except ValueError, e:
600 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
601 else:
602 # Convert 'cpus' string to list of ints
603 # 'cpus' supports a list of ranges (0-3),
604 # seperated by commas, and negation, (^1).
605 # Precedence is settled by order of the
606 # string:
607 # "0-3,^1" -> [0,2,3]
608 # "0-3,^1,1" -> [0,1,2,3]
609 try:
610 for c in cfg['cpus'].split(','):
611 if c.find('-') != -1:
612 (x, y) = c.split('-')
613 for i in range(int(x), int(y)+1):
614 cpus.append(int(i))
615 else:
616 # remove this element from the list
617 if c[0] == '^':
618 cpus = [x for x in cpus if x != int(c[1:])]
619 else:
620 cpus.append(int(c))
622 cfg['cpus'] = cpus
623 except ValueError, e:
624 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
626 import xen.util.xsm.xsm as security
627 if security.on():
628 from xen.util.acmpolicy import ACM_LABEL_UNLABELED
629 if not 'security' in cfg and sxp.child_value(sxp_cfg, 'security'):
630 cfg['security'] = sxp.child_value(sxp_cfg, 'security')
631 elif not cfg.get('security_label'):
632 cfg['security'] = [['access_control',
633 ['policy', security.get_active_policy_name() ],
634 ['label', ACM_LABEL_UNLABELED ]]]
636 if 'security' in cfg and not cfg.get('security_label'):
637 secinfo = cfg['security']
638 # The xm command sends a list formatted like this:
639 # [['access_control', ['policy', 'xm-test'],['label', 'red']],
640 # ['ssidref', 196611]]
641 policy = ""
642 label = ""
643 for idx in range(0, len(secinfo)):
644 if secinfo[idx][0] == "access_control":
645 for aidx in range(1, len(secinfo[idx])):
646 if secinfo[idx][aidx][0] == "policy":
647 policy = secinfo[idx][aidx][1]
648 if secinfo[idx][aidx][0] == "label":
649 label = secinfo[idx][aidx][1]
650 cfg['security_label'] = \
651 security.set_security_label(policy, label)
652 if not sxp.child_value(sxp_cfg, 'security_label'):
653 del cfg['security']
655 sec_lab = cfg['security_label'].split(":")
656 if len(sec_lab) != 3:
657 raise XendConfigError("Badly formatted security label: %s"
658 % cfg['security_label'])
660 old_state = sxp.child_value(sxp_cfg, 'state')
661 if old_state:
662 for i in range(len(CONFIG_OLD_DOM_STATES)):
663 cfg[CONFIG_OLD_DOM_STATES[i]] = int(old_state[i] != '-')
665 return cfg
668 def _sxp_to_xapi(self, sxp_cfg):
669 """Read in an SXP Configuration object and
670 populate at much of the Xen API with valid values.
671 """
672 log.debug('_sxp_to_xapi(%s)' % scrub_password(sxp_cfg))
674 cfg = self._parse_sxp(sxp_cfg)
676 for key, typ in XENAPI_CFG_TYPES.items():
677 val = cfg.get(key)
678 if val is not None:
679 self[key] = typ(val)
681 # Convert parameters that can be directly mapped from
682 # the Legacy Config to Xen API Config
684 for apikey, cfgkey in XENAPI_CFG_TO_LEGACY_CFG.items():
685 try:
686 type_conv = XENAPI_CFG_TYPES.get(apikey)
687 if callable(type_conv):
688 self[apikey] = type_conv(cfg[cfgkey])
689 else:
690 log.warn("Unconverted key: " + apikey)
691 self[apikey] = cfg[cfgkey]
692 except KeyError:
693 pass
695 # Lets try and handle memory correctly
697 MiB = 1024 * 1024
699 if "memory" in cfg:
700 self["memory_static_min"] = 0
701 self["memory_static_max"] = int(cfg["memory"]) * MiB
702 self["memory_dynamic_min"] = int(cfg["memory"]) * MiB
703 self["memory_dynamic_max"] = int(cfg["memory"]) * MiB
705 if "maxmem" in cfg:
706 self["memory_static_max"] = int(cfg["maxmem"]) * MiB
708 self._memory_sanity_check()
710 def update_with(n, o):
711 if not self.get(n):
712 self[n] = cfg.get(o, '')
714 update_with('PV_bootloader', 'bootloader')
715 update_with('PV_bootloader_args', 'bootloader_args')
717 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
718 if image_sxp:
719 self.update_with_image_sxp(image_sxp)
721 # Convert Legacy HVM parameters to Xen API configuration
722 for key in XENAPI_PLATFORM_CFG:
723 if key in cfg:
724 self['platform'][key] = cfg[key]
726 # set device references in the configuration
727 self['devices'] = cfg.get('devices', {})
728 self['console_refs'] = cfg.get('console_refs', [])
729 self['vif_refs'] = cfg.get('vif_refs', [])
730 self['vbd_refs'] = cfg.get('vbd_refs', [])
731 self['vtpm_refs'] = cfg.get('vtpm_refs', [])
733 # coalesce hvm vnc frame buffer with vfb config
734 if self.is_hvm() and int(self['platform'].get('vnc', 0)) != 0:
735 # add vfb device if it isn't there already
736 if not self.has_rfb():
737 dev_config = ['vfb']
738 dev_config.append(['type', 'vnc'])
739 # copy VNC related params from platform config to vfb dev conf
740 for key in ['vncpasswd', 'vncunused', 'vncdisplay',
741 'vnclisten']:
742 if key in self['platform']:
743 dev_config.append([key, self['platform'][key]])
745 self.device_add('vfb', cfg_sxp = dev_config)
748 def has_rfb(self):
749 for console_uuid in self['console_refs']:
750 if self['devices'][console_uuid][1].get('protocol') == 'rfb':
751 return True
752 if self['devices'][console_uuid][0] == 'vfb':
753 return True
754 return False
756 def _sxp_to_xapi_unsupported(self, sxp_cfg):
757 """Read in an SXP configuration object and populate
758 values are that not related directly supported in
759 the Xen API.
760 """
762 log.debug('_sxp_to_xapi_unsupported(%s)' % scrub_password(sxp_cfg))
764 # Parse and convert parameters used to configure
765 # the image (as well as HVM images)
766 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
767 if image_sxp:
768 image_type = sxp.name(image_sxp)
769 if image_type != 'hvm' and image_type != 'linux':
770 self['platform']['image_type'] = image_type
772 for key in XENAPI_PLATFORM_CFG:
773 val = sxp.child_value(image_sxp, key, None)
774 if val is not None and val != '':
775 self['platform'][key] = val
777 notes = sxp.children(image_sxp, 'notes')
778 if notes:
779 self['notes'] = self.notes_from_sxp(notes[0])
781 self._hvm_boot_params_from_sxp(image_sxp)
783 # extract backend value
785 backend = []
786 for c in sxp.children(sxp_cfg, 'backend'):
787 backend.append(sxp.name(sxp.child0(c)))
788 if backend:
789 self['backend'] = backend
791 # Parse and convert other Non Xen API parameters.
792 def _set_cfg_if_exists(sxp_arg):
793 val = sxp.child_value(sxp_cfg, sxp_arg)
794 if val != None:
795 if LEGACY_CFG_TYPES.get(sxp_arg):
796 self[sxp_arg] = LEGACY_CFG_TYPES[sxp_arg](val)
797 else:
798 self[sxp_arg] = val
800 _set_cfg_if_exists('shadow_memory')
801 _set_cfg_if_exists('features')
802 _set_cfg_if_exists('on_xend_stop')
803 _set_cfg_if_exists('on_xend_start')
804 _set_cfg_if_exists('vcpu_avail')
806 # Parse and store runtime configuration
807 _set_cfg_if_exists('start_time')
808 _set_cfg_if_exists('cpu_time')
809 _set_cfg_if_exists('shutdown_reason')
810 _set_cfg_if_exists('up_time')
811 _set_cfg_if_exists('status') # TODO, deprecated
813 def _get_old_state_string(self):
814 """Returns the old xm state string.
815 @rtype: string
816 @return: old state string
817 """
818 state_string = ''
819 for state_name in CONFIG_OLD_DOM_STATES:
820 on_off = self.get(state_name, 0)
821 if on_off:
822 state_string += state_name[0]
823 else:
824 state_string += '-'
826 return state_string
829 def update_config(self, dominfo):
830 """Update configuration with the output from xc.domain_getinfo().
832 @param dominfo: Domain information via xc.domain_getinfo()
833 @type dominfo: dict
834 """
835 self._dominfo_to_xapi(dominfo)
836 self.validate()
838 def update_with_xenapi_config(self, xapi):
839 """Update configuration with a Xen API VM struct
841 @param xapi: Xen API VM Struct
842 @type xapi: dict
843 """
845 log.debug('update_with_xenapi_config: %s' % scrub_password(xapi))
847 for key, val in xapi.items():
848 type_conv = XENAPI_CFG_TYPES.get(key)
849 if type_conv is None:
850 key = key.lower()
851 type_conv = XENAPI_CFG_TYPES.get(key)
852 if callable(type_conv):
853 self[key] = type_conv(val)
854 else:
855 self[key] = val
857 self['vcpus_params']['weight'] = \
858 int(self['vcpus_params'].get('weight', 256))
859 self['vcpus_params']['cap'] = int(self['vcpus_params'].get('cap', 0))
861 def to_sxp(self, domain = None, ignore_devices = False, ignore = [],
862 legacy_only = True):
863 """ Get SXP representation of this config object.
865 Incompat: removed store_mfn, console_mfn
867 @keyword domain: (optional) XendDomainInfo to get extra information
868 from such as domid and running devices.
869 @type domain: XendDomainInfo
870 @keyword ignore: (optional) list of 'keys' that we do not want
871 to export.
872 @type ignore: list of strings
873 @rtype: list of list (SXP representation)
874 """
875 sxpr = ['domain']
877 # TODO: domid/dom is the same thing but called differently
878 # depending if it is from xenstore or sxpr.
880 if domain.getDomid() is not None:
881 sxpr.append(['domid', domain.getDomid()])
883 if not legacy_only:
884 for name, typ in XENAPI_CFG_TYPES.items():
885 if name in self and self[name] not in (None, []):
886 if typ == dict:
887 s = self[name].items()
888 elif typ == list:
889 s = self[name]
890 else:
891 s = str(self[name])
892 sxpr.append([name, s])
894 for xenapi, legacy in XENAPI_CFG_TO_LEGACY_CFG.items():
895 if legacy in ('cpus'): # skip this
896 continue
897 if self.has_key(xenapi) and self[xenapi] not in (None, []):
898 if type(self[xenapi]) == bool:
899 # convert booleans to ints before making an sxp item
900 sxpr.append([legacy, int(self[xenapi])])
901 else:
902 sxpr.append([legacy, self[xenapi]])
904 MiB = 1024*1024
906 sxpr.append(["maxmem", int(self["memory_static_max"])/MiB])
907 sxpr.append(["memory", int(self["memory_dynamic_max"])/MiB])
909 for legacy in LEGACY_UNSUPPORTED_BY_XENAPI_CFG:
910 if legacy in ('domid', 'uuid', 'cpus'): # skip these
911 continue
912 if self.has_key(legacy) and self[legacy] not in (None, []):
913 sxpr.append([legacy, self[legacy]])
915 if self.has_key('security_label'):
916 sxpr.append(['security_label', self['security_label']])
918 sxpr.append(['image', self.image_sxpr()])
919 sxpr.append(['status', domain._stateGet()])
921 if domain.getDomid() is not None:
922 sxpr.append(['state', self._get_old_state_string()])
924 if domain:
925 if domain.store_mfn:
926 sxpr.append(['store_mfn', domain.store_mfn])
927 if domain.console_mfn:
928 sxpr.append(['console_mfn', domain.console_mfn])
931 # Marshall devices (running or from configuration)
932 if not ignore_devices:
933 txn = xstransact()
934 try:
935 for cls in XendDevices.valid_devices():
936 found = False
938 # figure if there is a dev controller is valid and running
939 if domain and domain.getDomid() != None:
940 try:
941 controller = domain.getDeviceController(cls)
942 configs = controller.configurations(txn)
943 for config in configs:
944 if sxp.name(config) in ('vbd', 'tap'):
945 # The bootable flag is never written to the
946 # store as part of the device config.
947 dev_uuid = sxp.child_value(config, 'uuid')
948 dev_type, dev_cfg = self['devices'][dev_uuid]
949 is_bootable = dev_cfg.get('bootable', 0)
950 config.append(['bootable', int(is_bootable)])
952 sxpr.append(['device', config])
954 found = True
955 except:
956 log.exception("dumping sxp from device controllers")
957 pass
959 # if we didn't find that device, check the existing config
960 # for a device in the same class
961 if not found:
962 for dev_type, dev_info in self.all_devices_sxpr():
963 if dev_type == cls:
964 sxpr.append(['device', dev_info])
966 txn.commit()
967 except:
968 txn.abort()
969 raise
971 return sxpr
973 def _blkdev_name_to_number(self, dev):
974 if 'ioemu:' in dev:
975 _, dev = dev.split(':', 1)
976 try:
977 dev, _ = dev.split(':', 1)
978 except ValueError:
979 pass
981 try:
982 devid = int(dev)
983 except ValueError:
984 # devid is not a number but a string containing either device
985 # name (e.g. xvda) or device_type/device_id (e.g. vbd/51728)
986 dev2 = type(dev) is str and dev.split('/')[-1] or None
987 if dev2 == None:
988 log.debug("Could not check the device %s", dev)
989 return None
990 try:
991 devid = int(dev2)
992 except ValueError:
993 devid = blkdev_name_to_number(dev2)
994 if devid == None:
995 log.debug("The device %s is not device name", dev2)
996 return None
997 return devid
999 def device_duplicate_check(self, dev_type, dev_info, defined_config):
1000 defined_devices_sxpr = self.all_devices_sxpr(target = defined_config)
1002 if dev_type == 'vbd' or dev_type == 'tap':
1003 dev_uname = dev_info.get('uname')
1004 blkdev_name = dev_info.get('dev')
1005 devid = self._blkdev_name_to_number(blkdev_name)
1006 if devid == None or dev_uname == None:
1007 return
1009 for o_dev_type, o_dev_info in defined_devices_sxpr:
1010 if o_dev_type == 'vbd' or o_dev_type == 'tap':
1011 blkdev_file = blkdev_uname_to_file(dev_uname)
1012 o_dev_uname = sxp.child_value(o_dev_info, 'uname')
1013 o_blkdev_file = blkdev_uname_to_file(o_dev_uname)
1014 if blkdev_file == o_blkdev_file:
1015 raise XendConfigError('The file "%s" is already used' %
1016 blkdev_file)
1017 o_blkdev_name = sxp.child_value(o_dev_info, 'dev')
1018 o_devid = self._blkdev_name_to_number(o_blkdev_name)
1019 if o_devid != None and devid == o_devid:
1020 raise XendConfigError('The device "%s" is already defined' %
1021 blkdev_name)
1023 elif dev_type == 'vif':
1024 dev_mac = dev_info.get('mac')
1026 for o_dev_type, o_dev_info in defined_devices_sxpr:
1027 if dev_type == o_dev_type:
1028 if dev_mac.lower() == sxp.child_value(o_dev_info, 'mac').lower():
1029 raise XendConfigError('The mac "%s" is already defined' %
1030 dev_mac)
1032 def device_add(self, dev_type, cfg_sxp = None, cfg_xenapi = None,
1033 target = None):
1034 """Add a device configuration in SXP format or XenAPI struct format.
1036 For SXP, it could be either:
1038 [device, [vbd, [uname ...]]
1040 or:
1042 [vbd, [uname ..]]
1044 @type cfg_sxp: list of lists (parsed sxp object)
1045 @param cfg_sxp: SXP configuration object
1046 @type cfg_xenapi: dict
1047 @param cfg_xenapi: A device configuration from Xen API (eg. vbd,vif)
1048 @param target: write device information to
1049 @type target: None or a dictionary
1050 @rtype: string
1051 @return: Assigned UUID of the device.
1052 """
1053 if target == None:
1054 target = self
1056 if dev_type not in XendDevices.valid_devices():
1057 raise XendConfigError("XendConfig: %s not a valid device type" %
1058 dev_type)
1060 if cfg_sxp == None and cfg_xenapi == None:
1061 raise XendConfigError("XendConfig: device_add requires some "
1062 "config.")
1064 #if cfg_sxp:
1065 # log.debug("XendConfig.device_add: %s" % str(cfg_sxp))
1066 #if cfg_xenapi:
1067 # log.debug("XendConfig.device_add: %s" % str(cfg_xenapi))
1069 if cfg_sxp:
1070 if sxp.child0(cfg_sxp) == 'device':
1071 config = sxp.child0(cfg_sxp)
1072 else:
1073 config = cfg_sxp
1075 dev_type = sxp.name(config)
1076 dev_info = {}
1078 # Parsing the device SXP's. In most cases, the SXP looks
1079 # like this:
1081 # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]]
1083 # However, for PCI devices it looks like this:
1085 # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1]]]]
1087 # It seems the reasoning for this difference is because
1088 # pciif.py needs all the PCI device configurations at
1089 # the same time when creating the devices.
1091 # To further complicate matters, Xen 2.0 configuration format
1092 # uses the following for pci device configuration:
1094 # [device, [pci, [domain, 0], [bus, 0], [dev, 1], [func, 2]]]
1096 if dev_type == 'pci':
1097 pci_devs_uuid = sxp.child_value(config, 'uuid',
1098 uuid.createString())
1099 pci_devs = []
1100 for pci_dev in sxp.children(config, 'dev'):
1101 pci_dev_info = {}
1102 for opt_val in pci_dev[1:]:
1103 try:
1104 opt, val = opt_val
1105 pci_dev_info[opt] = val
1106 except TypeError:
1107 pass
1108 pci_devs.append(pci_dev_info)
1109 target['devices'][pci_devs_uuid] = (dev_type,
1110 {'devs': pci_devs,
1111 'uuid': pci_devs_uuid})
1113 log.debug("XendConfig: reading device: %s" % pci_devs)
1114 return pci_devs_uuid
1116 for opt_val in config[1:]:
1117 try:
1118 opt, val = opt_val
1119 dev_info[opt] = val
1120 except (TypeError, ValueError): # unpack error
1121 pass
1123 if dev_type == 'vbd':
1124 dev_info['bootable'] = 0
1125 if dev_info.get('dev', '').startswith('ioemu:'):
1126 dev_info['driver'] = 'ioemu'
1127 else:
1128 dev_info['driver'] = 'paravirtualised'
1130 if dev_type == 'tap':
1131 if dev_info['uname'].split(':')[1] not in blktap_disk_types:
1132 raise XendConfigError("tap:%s not a valid disk type" %
1133 dev_info['uname'].split(':')[1])
1135 if dev_type == 'vif':
1136 if not dev_info.get('mac'):
1137 dev_info['mac'] = randomMAC()
1139 self.device_duplicate_check(dev_type, dev_info, target)
1141 if dev_type == 'vif':
1142 if dev_info.get('policy') and dev_info.get('label'):
1143 dev_info['security_label'] = "%s:%s:%s" % \
1144 (xsconstants.ACM_POLICY_ID,
1145 dev_info['policy'],dev_info['label'])
1147 # create uuid if it doesn't exist
1148 dev_uuid = dev_info.get('uuid', None)
1149 if not dev_uuid:
1150 dev_uuid = uuid.createString()
1151 dev_info['uuid'] = dev_uuid
1153 # store dev references by uuid for certain device types
1154 target['devices'][dev_uuid] = (dev_type, dev_info)
1155 if dev_type in ('vif', 'vbd', 'vtpm'):
1156 param = '%s_refs' % dev_type
1157 if param not in target:
1158 target[param] = []
1159 if dev_uuid not in target[param]:
1160 if dev_type == 'vbd':
1161 # Compat hack -- mark first disk bootable
1162 dev_info['bootable'] = int(not target[param])
1163 target[param].append(dev_uuid)
1164 elif dev_type == 'tap':
1165 if 'vbd_refs' not in target:
1166 target['vbd_refs'] = []
1167 if dev_uuid not in target['vbd_refs']:
1168 # Compat hack -- mark first disk bootable
1169 dev_info['bootable'] = int(not target['vbd_refs'])
1170 target['vbd_refs'].append(dev_uuid)
1172 elif dev_type == 'vfb':
1173 # Populate other config with aux data that is associated
1174 # with vfb
1176 other_config = {}
1177 for key in XENAPI_CONSOLE_OTHER_CFG:
1178 if key in dev_info:
1179 other_config[key] = dev_info[key]
1180 target['devices'][dev_uuid][1]['other_config'] = other_config
1183 if 'console_refs' not in target:
1184 target['console_refs'] = []
1186 # Treat VFB devices as console devices so they are found
1187 # through Xen API
1188 if dev_uuid not in target['console_refs']:
1189 target['console_refs'].append(dev_uuid)
1191 elif dev_type == 'console':
1192 if 'console_refs' not in target:
1193 target['console_refs'] = []
1194 if dev_uuid not in target['console_refs']:
1195 target['console_refs'].append(dev_uuid)
1197 log.debug("XendConfig: reading device: %s" % scrub_password(dev_info))
1198 return dev_uuid
1200 if cfg_xenapi:
1201 dev_info = {}
1202 dev_uuid = ''
1203 if dev_type == 'vif':
1204 dev_info['mac'] = cfg_xenapi.get('MAC')
1205 if not dev_info['mac']:
1206 dev_info['mac'] = randomMAC()
1207 # vifname is the name on the guest, not dom0
1208 # TODO: we don't have the ability to find that out or
1209 # change it from dom0
1210 #if cfg_xenapi.get('device'): # don't add if blank
1211 # dev_info['vifname'] = cfg_xenapi.get('device')
1212 if cfg_xenapi.get('type'):
1213 dev_info['type'] = cfg_xenapi.get('type')
1214 if cfg_xenapi.get('name'):
1215 dev_info['name'] = cfg_xenapi.get('name')
1216 if cfg_xenapi.get('network'):
1217 network = XendAPIStore.get(
1218 cfg_xenapi.get('network'), 'network')
1219 dev_info['bridge'] = network.get_name_label()
1221 if cfg_xenapi.get('security_label'):
1222 dev_info['security_label'] = \
1223 cfg_xenapi.get('security_label')
1225 dev_uuid = cfg_xenapi.get('uuid', None)
1226 if not dev_uuid:
1227 dev_uuid = uuid.createString()
1228 dev_info['uuid'] = dev_uuid
1229 target['devices'][dev_uuid] = (dev_type, dev_info)
1230 target['vif_refs'].append(dev_uuid)
1232 elif dev_type in ('vbd', 'tap'):
1233 dev_info['type'] = cfg_xenapi.get('type', 'Disk')
1234 if dev_info['type'] == 'CD':
1235 old_vbd_type = 'cdrom'
1236 else:
1237 old_vbd_type = 'disk'
1239 dev_info['uname'] = cfg_xenapi.get('image', '')
1240 dev_info['dev'] = '%s:%s' % (cfg_xenapi.get('device'),
1241 old_vbd_type)
1242 dev_info['bootable'] = int(cfg_xenapi.get('bootable', 0))
1243 dev_info['driver'] = cfg_xenapi.get('driver', '')
1244 dev_info['VDI'] = cfg_xenapi.get('VDI', '')
1246 if cfg_xenapi.get('mode') == 'RW':
1247 dev_info['mode'] = 'w'
1248 else:
1249 dev_info['mode'] = 'r'
1251 dev_uuid = cfg_xenapi.get('uuid', None)
1252 if not dev_uuid:
1253 dev_uuid = uuid.createString()
1254 dev_info['uuid'] = dev_uuid
1255 target['devices'][dev_uuid] = (dev_type, dev_info)
1256 target['vbd_refs'].append(dev_uuid)
1258 elif dev_type == 'vtpm':
1259 if cfg_xenapi.get('type'):
1260 dev_info['type'] = cfg_xenapi.get('type')
1262 dev_uuid = cfg_xenapi.get('uuid', None)
1263 if not dev_uuid:
1264 dev_uuid = uuid.createString()
1265 dev_info['uuid'] = dev_uuid
1266 dev_info['other_config'] = cfg_xenapi.get('other_config', {})
1267 target['devices'][dev_uuid] = (dev_type, dev_info)
1268 target['vtpm_refs'].append(dev_uuid)
1270 elif dev_type == 'console':
1271 dev_uuid = cfg_xenapi.get('uuid', None)
1272 if not dev_uuid:
1273 dev_uuid = uuid.createString()
1274 dev_info['uuid'] = dev_uuid
1275 dev_info['protocol'] = cfg_xenapi.get('protocol', 'rfb')
1276 dev_info['other_config'] = cfg_xenapi.get('other_config', {})
1277 if dev_info['protocol'] == 'rfb':
1278 # collapse other config into devinfo for things
1279 # such as vncpasswd, vncunused, etc.
1280 dev_info.update(cfg_xenapi.get('other_config', {}))
1281 dev_info['type'] = 'vnc'
1282 target['devices'][dev_uuid] = ('vfb', dev_info)
1283 target['console_refs'].append(dev_uuid)
1285 # Finally, if we are a pvfb, we need to make a vkbd
1286 # as well that is not really exposed to Xen API
1287 vkbd_uuid = uuid.createString()
1288 target['devices'][vkbd_uuid] = ('vkbd', {})
1290 elif dev_info['protocol'] == 'vt100':
1291 # if someone tries to create a VT100 console
1292 # via the Xen API, we'll have to ignore it
1293 # because we create one automatically in
1294 # XendDomainInfo._update_consoles
1295 raise XendConfigError('Creating vt100 consoles via '
1296 'Xen API is unsupported')
1298 return dev_uuid
1300 # no valid device to add
1301 return ''
1303 def phantom_device_add(self, dev_type, cfg_xenapi = None,
1304 target = None):
1305 """Add a phantom tap device configuration in XenAPI struct format.
1306 """
1308 if target == None:
1309 target = self
1311 if dev_type not in XendDevices.valid_devices() and \
1312 dev_type not in XendDevices.pseudo_devices():
1313 raise XendConfigError("XendConfig: %s not a valid device type" %
1314 dev_type)
1316 if cfg_xenapi == None:
1317 raise XendConfigError("XendConfig: device_add requires some "
1318 "config.")
1320 if cfg_xenapi:
1321 log.debug("XendConfig.phantom_device_add: %s" % str(cfg_xenapi))
1323 if cfg_xenapi:
1324 dev_info = {}
1325 if dev_type in ('vbd', 'tap'):
1326 if dev_type == 'vbd':
1327 dev_info['uname'] = cfg_xenapi.get('image', '')
1328 dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
1329 elif dev_type == 'tap':
1330 if cfg_xenapi.get('image').find('tap:') == -1:
1331 dev_info['uname'] = 'tap:qcow:%s' % cfg_xenapi.get('image')
1332 dev_info['dev'] = '/dev/%s' % cfg_xenapi.get('device')
1333 dev_info['uname'] = cfg_xenapi.get('image')
1334 dev_info['mode'] = cfg_xenapi.get('mode')
1335 dev_info['backend'] = '0'
1336 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
1337 dev_info['uuid'] = dev_uuid
1338 self['devices'][dev_uuid] = (dev_type, dev_info)
1339 self['vbd_refs'].append(dev_uuid)
1340 return dev_uuid
1342 return ''
1344 def console_add(self, protocol, location, other_config = {}):
1345 dev_uuid = uuid.createString()
1346 if protocol == 'vt100':
1347 dev_info = {
1348 'uuid': dev_uuid,
1349 'protocol': protocol,
1350 'location': location,
1351 'other_config': other_config,
1354 if 'devices' not in self:
1355 self['devices'] = {}
1357 self['devices'][dev_uuid] = ('console', dev_info)
1358 self['console_refs'].append(dev_uuid)
1359 return dev_info
1361 return {}
1363 def console_update(self, console_uuid, key, value):
1364 for dev_uuid, (dev_type, dev_info) in self['devices'].items():
1365 if dev_uuid == console_uuid:
1366 dev_info[key] = value
1367 # collapse other_config into dev_info for things
1368 # such as vncpasswd, vncunused, etc.
1369 if key == 'other_config':
1370 for k in XENAPI_CONSOLE_OTHER_CFG:
1371 if k in dev_info and k not in value:
1372 del dev_info[k]
1373 dev_info.update(value)
1374 break
1376 def console_get_all(self, protocol):
1377 if protocol == 'vt100':
1378 consoles = [dinfo for dtype, dinfo in self['devices'].values()
1379 if dtype == 'console']
1380 return [c for c in consoles if c.get('protocol') == protocol]
1382 elif protocol == 'rfb':
1383 vfbs = [dinfo for dtype, dinfo in self['devices'].values()
1384 if dtype == 'vfb']
1386 # move all non-console key values to other_config before
1387 # returning console config
1388 valid_keys = ['uuid', 'location']
1389 for vfb in vfbs:
1390 other_config = {}
1391 for key, val in vfb.items():
1392 if key not in valid_keys:
1393 other_config[key] = vfb[key]
1394 del vfb[key]
1395 vfb['other_config'] = other_config
1396 vfb['protocol'] = 'rfb'
1398 return vfbs
1400 else:
1401 return []
1403 def device_update(self, dev_uuid, cfg_sxp = [], cfg_xenapi = {}):
1404 """Update an existing device with the new configuration.
1406 @rtype: boolean
1407 @return: Returns True if succesfully found and updated a device conf
1408 """
1409 if dev_uuid in self['devices'] and cfg_sxp:
1410 if sxp.child0(cfg_sxp) == 'device':
1411 config = sxp.child0(cfg_sxp)
1412 else:
1413 config = cfg_sxp
1415 dev_type, dev_info = self['devices'][dev_uuid]
1416 for opt_val in config[1:]:
1417 try:
1418 opt, val = opt_val
1419 dev_info[opt] = val
1420 except (TypeError, ValueError):
1421 pass # no value for this config option
1423 self['devices'][dev_uuid] = (dev_type, dev_info)
1424 return True
1426 elif dev_uuid in self['devices'] and cfg_xenapi:
1427 dev_type, dev_info = self['devices'][dev_uuid]
1428 for key, val in cfg_xenapi.items():
1429 dev_info[key] = val
1430 self['devices'][dev_uuid] = (dev_type, dev_info)
1432 return False
1435 def device_sxpr(self, dev_uuid = None, dev_type = None, dev_info = None, target = None):
1436 """Get Device SXPR by either giving the device UUID or (type, config).
1438 @rtype: list of lists
1439 @return: device config sxpr
1440 """
1441 sxpr = []
1443 if target == None:
1444 target = self
1446 if dev_uuid != None and dev_uuid in target['devices']:
1447 dev_type, dev_info = target['devices'][dev_uuid]
1449 if dev_type == None or dev_info == None:
1450 raise XendConfigError("Required either UUID or device type and "
1451 "configuration dictionary.")
1453 sxpr.append(dev_type)
1454 if dev_type in ('console', 'vfb'):
1455 config = [(opt, val) for opt, val in dev_info.items()
1456 if opt != 'other_config']
1457 else:
1458 config = [(opt, val) for opt, val in dev_info.items()]
1460 sxpr += config
1462 return sxpr
1464 def ordered_device_refs(self, target = None):
1465 result = []
1467 if target == None:
1468 target = self
1470 # vkbd devices *must* be before vfb devices, otherwise
1471 # there is a race condition when setting up devices
1472 # where the daemon spawned for the vfb may write stuff
1473 # into xenstore vkbd backend, before DevController has
1474 # setup permissions on the vkbd backend path. This race
1475 # results in domain creation failing with 'device already
1476 # connected' messages
1477 result.extend([u for u in target['devices'].keys() if target['devices'][u][0] == 'vkbd'])
1479 result.extend(target.get('console_refs', []) +
1480 target.get('vbd_refs', []) +
1481 target.get('vif_refs', []) +
1482 target.get('vtpm_refs', []))
1484 result.extend([u for u in target['devices'].keys() if u not in result])
1485 return result
1487 def all_devices_sxpr(self, target = None):
1488 """Returns the SXPR for all devices in the current configuration."""
1489 sxprs = []
1490 pci_devs = []
1492 if target == None:
1493 target = self
1495 if 'devices' not in target:
1496 return sxprs
1498 ordered_refs = self.ordered_device_refs(target = target)
1499 for dev_uuid in ordered_refs:
1500 dev_type, dev_info = target['devices'][dev_uuid]
1501 if dev_type == 'pci': # special case for pci devices
1502 sxpr = ['pci', ['uuid', dev_info['uuid']]]
1503 for pci_dev_info in dev_info['devs']:
1504 pci_dev_sxpr = ['dev']
1505 for opt, val in pci_dev_info.items():
1506 pci_dev_sxpr.append([opt, val])
1507 sxpr.append(pci_dev_sxpr)
1508 sxprs.append((dev_type, sxpr))
1509 else:
1510 sxpr = self.device_sxpr(dev_type = dev_type,
1511 dev_info = dev_info,
1512 target = target)
1513 sxprs.append((dev_type, sxpr))
1515 return sxprs
1517 def image_sxpr(self):
1518 """Returns a backwards compatible image SXP expression that is
1519 used in xenstore's /vm/<uuid>/image value and xm list."""
1520 image = [self.image_type()]
1521 if self.has_key('PV_kernel'):
1522 image.append(['kernel', self['PV_kernel']])
1523 if self.has_key('PV_ramdisk') and self['PV_ramdisk']:
1524 image.append(['ramdisk', self['PV_ramdisk']])
1525 if self.has_key('PV_args') and self['PV_args']:
1526 image.append(['args', self['PV_args']])
1528 for key in XENAPI_PLATFORM_CFG:
1529 if key in self['platform']:
1530 image.append([key, self['platform'][key]])
1532 if 'notes' in self:
1533 image.append(self.notes_sxp(self['notes']))
1535 return image
1537 def update_with_image_sxp(self, image_sxp, bootloader = False):
1538 # Convert Legacy "image" config to Xen API PV_*
1539 # configuration
1540 log.debug("update_with_image_sxp(%s)" % scrub_password(image_sxp))
1542 # user-specified args must come last: previous releases did this and
1543 # some domU kernels rely upon the ordering.
1544 kernel_args = sxp.child_value(image_sxp, 'args', '')
1546 # attempt to extract extra arguments from SXP config
1547 arg_ip = sxp.child_value(image_sxp, 'ip')
1548 if arg_ip and not re.search(r'ip=[^ ]+', kernel_args):
1549 kernel_args = 'ip=%s ' % arg_ip + kernel_args
1550 arg_root = sxp.child_value(image_sxp, 'root')
1551 if arg_root and not re.search(r'root=', kernel_args):
1552 kernel_args = 'root=%s ' % arg_root + kernel_args
1554 if bootloader:
1555 self['_temp_using_bootloader'] = '1'
1556 self['_temp_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1557 self['_temp_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1558 self['_temp_args'] = kernel_args
1559 else:
1560 self['PV_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1561 self['PV_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1562 self['PV_args'] = kernel_args
1564 for key in XENAPI_PLATFORM_CFG:
1565 val = sxp.child_value(image_sxp, key, None)
1566 if val is not None and val != '':
1567 self['platform'][key] = val
1569 notes = sxp.children(image_sxp, 'notes')
1570 if notes:
1571 self['notes'] = self.notes_from_sxp(notes[0])
1573 self._hvm_boot_params_from_sxp(image_sxp)
1575 def set_notes(self, notes):
1576 'Add parsed elfnotes to image'
1577 self['notes'] = notes
1579 def get_notes(self):
1580 try:
1581 return self['notes'] or {}
1582 except KeyError:
1583 return {}
1585 def notes_from_sxp(self, nsxp):
1586 notes = {}
1587 for note in sxp.children(nsxp):
1588 notes[note[0]] = note[1]
1589 return notes
1591 def notes_sxp(self, notes):
1592 nsxp = ['notes']
1593 for k, v in notes.iteritems():
1594 nsxp.append([k, str(v)])
1595 return nsxp
1597 def _hvm_boot_params_from_sxp(self, image_sxp):
1598 boot = sxp.child_value(image_sxp, 'boot', None)
1599 if boot is not None:
1600 self['HVM_boot_policy'] = 'BIOS order'
1601 self['HVM_boot_params'] = { 'order' : boot }
1603 def is_hvm(self):
1604 return self['HVM_boot_policy'] != ''
1606 def target(self):
1607 return self['target']
1609 def image_type(self):
1610 stored_type = self['platform'].get('image_type')
1611 return stored_type or (self.is_hvm() and 'hvm' or 'linux')
1613 def is_hap(self):
1614 return self['platform'].get('hap', 0)