ia64/xen-unstable

view tools/python/xen/xend/XendConfig.py @ 17578:94c6501c4ffe

xend: Refactor security.on() call

I am refactoring the security.on() call to return the actual type of
the security module that is found to be enabled rather than just
returning True or False.

Signed-off-by: Stefan Berger <stefanb@us.ibm.com>
author Keir Fraser <keir.fraser@citrix.com>
date Tue May 06 10:05:52 2008 +0100 (2008-05-06)
parents 18727843db60
children 5c3df1bded82
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 and their types.
127 XENAPI_PLATFORM_CFG_TYPES = {
128 'acpi': int,
129 'apic': int,
130 'boot': str,
131 'device_model': str,
132 'loader': str,
133 'display' : str,
134 'fda': str,
135 'fdb': str,
136 'keymap': str,
137 'isa' : int,
138 'localtime': int,
139 'monitor': int,
140 'nographic': int,
141 'pae' : int,
142 'rtc_timeoffset': int,
143 'serial': str,
144 'sdl': int,
145 'opengl': int,
146 'soundhw': str,
147 'stdvga': int,
148 'usb': int,
149 'usbdevice': str,
150 'hpet': int,
151 'vnc': int,
152 'vncconsole': int,
153 'vncdisplay': int,
154 'vnclisten': str,
155 'timer_mode': int,
156 'vncpasswd': str,
157 'vncunused': int,
158 'xauthority': str,
159 'pci': str,
160 'vhpt': int,
161 'guest_os_type': str,
162 'hap': int,
163 }
165 # Xen API console 'other_config' keys.
166 XENAPI_CONSOLE_OTHER_CFG = ['vncunused', 'vncdisplay', 'vnclisten',
167 'vncpasswd', 'type', 'display', 'xauthority',
168 'keymap', 'opengl']
170 # List of XendConfig configuration keys that have no direct equivalent
171 # in the old world.
173 XENAPI_CFG_TYPES = {
174 'uuid': str,
175 'name_label': str,
176 'name_description': str,
177 'user_version': str,
178 'is_a_template': bool0,
179 'resident_on': str,
180 'memory_static_min': int, # note these are stored in bytes, not KB!
181 'memory_static_max': int,
182 'memory_dynamic_min': int,
183 'memory_dynamic_max': int,
184 'cpus': list,
185 'vcpus_params': dict,
186 'VCPUs_max': int,
187 'VCPUs_at_startup': int,
188 'VCPUs_live': int,
189 'actions_after_shutdown': str,
190 'actions_after_reboot': str,
191 'actions_after_crash': str,
192 'PV_bootloader': str,
193 'PV_kernel': str,
194 'PV_ramdisk': str,
195 'PV_args': str,
196 'PV_bootloader_args': str,
197 'HVM_boot_policy': str,
198 'HVM_boot_params': dict,
199 'PCI_bus': str,
200 'platform': dict,
201 'tools_version': dict,
202 'other_config': dict,
203 'target': int,
204 'security_label': str,
205 'pci': str,
206 'cpuid' : dict,
207 'cpuid_check' : dict,
208 }
210 # List of legacy configuration keys that have no equivalent in the
211 # Xen API, but are still stored in XendConfig.
213 LEGACY_UNSUPPORTED_BY_XENAPI_CFG = [
214 # roundtripped (dynamic, unmodified)
215 'shadow_memory',
216 'vcpu_avail',
217 'features',
218 # read/write
219 'on_xend_start',
220 'on_xend_stop',
221 # read-only
222 'domid',
223 'start_time',
224 'cpu_time',
225 'online_vcpus',
226 # write-once
227 'cpu',
228 'cpus',
229 ]
231 LEGACY_CFG_TYPES = {
232 'uuid': str,
233 'name': str,
234 'vcpus': int,
235 'vcpu_avail': long,
236 'memory': int,
237 'shadow_memory': int,
238 'maxmem': int,
239 'start_time': float,
240 'cpu_time': float,
241 'features': str,
242 'localtime': int,
243 'name': str,
244 'on_poweroff': str,
245 'on_reboot': str,
246 'on_crash': str,
247 'on_xend_stop': str,
248 'on_xend_start': str,
249 'online_vcpus': int,
250 'rtc/timeoffset': str,
251 }
253 # Values that should be stored in xenstore's /vm/<uuid> that is used
254 # by Xend. Used in XendDomainInfo to restore running VM state from
255 # xenstore.
256 LEGACY_XENSTORE_VM_PARAMS = [
257 'uuid',
258 'name',
259 'vcpus',
260 'vcpu_avail',
261 'memory',
262 'shadow_memory',
263 'maxmem',
264 'start_time',
265 'name',
266 'on_poweroff',
267 'on_crash',
268 'on_reboot',
269 'on_xend_start',
270 'on_xend_stop',
271 ]
273 ##
274 ## Config Choices
275 ##
277 CONFIG_RESTART_MODES = ('restart', 'destroy', 'preserve', 'rename-restart',
278 'coredump-destroy', 'coredump-restart')
279 CONFIG_OLD_DOM_STATES = ('running', 'blocked', 'paused', 'shutdown',
280 'crashed', 'dying')
282 class XendConfigError(VmError):
283 def __str__(self):
284 return 'Invalid Configuration: %s' % str(self.value)
286 ##
287 ## XendConfig Class (an extended dictionary)
288 ##
290 class XendConfig(dict):
291 """ The new Xend VM Configuration.
293 Stores the configuration in xenapi compatible format but retains
294 import and export functions for SXP.
295 """
296 def __init__(self, filename = None, sxp_obj = None,
297 xapi = None, dominfo = None):
299 dict.__init__(self)
300 self.update(self._defaults())
302 if filename:
303 try:
304 sxp_obj = sxp.parse(open(filename,'r'))
305 sxp_obj = sxp_obj[0]
306 except IOError, e:
307 raise XendConfigError("Unable to read file: %s" % filename)
309 if sxp_obj:
310 self._sxp_to_xapi(sxp_obj)
311 self._sxp_to_xapi_unsupported(sxp_obj)
312 elif xapi:
313 self.update_with_xenapi_config(xapi)
314 elif dominfo:
315 # output from xc.domain_getinfo
316 self._dominfo_to_xapi(dominfo, update_mem = True)
318 log.debug('XendConfig.init: %s' % scrub_password(self))
320 # validators go here
321 self.validate()
323 """ In time, we should enable this type checking addition. It is great
324 also for tracking bugs and unintended writes to XendDomainInfo.info
325 def __setitem__(self, key, value):
326 type_conv = XENAPI_CFG_TYPES.get(key)
327 if callable(type_conv):
328 try:
329 dict.__setitem__(self, key, type_conv(value))
330 except (ValueError, TypeError):
331 raise XendConfigError("Wrong type for configuration value " +
332 "%s. Expected %s" %
333 (key, type_conv.__name__))
334 else:
335 dict.__setitem__(self, key, value)
336 """
338 def _defaults(self):
339 defaults = {
340 'name_label': 'Domain-Unnamed',
341 'actions_after_shutdown': 'destroy',
342 'actions_after_reboot': 'restart',
343 'actions_after_crash': 'restart',
344 'actions_after_suspend': '',
345 'is_a_template': False,
346 'is_control_domain': False,
347 'features': '',
348 'PV_bootloader': '',
349 'PV_kernel': '',
350 'PV_ramdisk': '',
351 'PV_args': '',
352 'PV_bootloader_args': '',
353 'HVM_boot_policy': '',
354 'HVM_boot_params': {},
355 'memory_static_min': 0,
356 'memory_dynamic_min': 0,
357 'shadow_memory': 0,
358 'memory_static_max': 0,
359 'memory_dynamic_max': 0,
360 'devices': {},
361 'on_xend_start': 'ignore',
362 'on_xend_stop': 'ignore',
363 'cpus': [],
364 'VCPUs_max': 1,
365 'VCPUs_live': 1,
366 'VCPUs_at_startup': 1,
367 'vcpus_params': {},
368 'console_refs': [],
369 'vif_refs': [],
370 'vbd_refs': [],
371 'vtpm_refs': [],
372 'other_config': {},
373 'platform': {},
374 'target': 0,
375 }
377 return defaults
379 #
380 # Here we assume these values exist in the dict.
381 # If they don't we have a bigger problem, lets not
382 # try and 'fix it up' but acutually fix the cause ;-)
383 #
384 def _memory_sanity_check(self):
385 log.trace("_memory_sanity_check memory_static_min: %s, "
386 "memory_static_max: %i, "
387 "memory_dynamic_min: %i, "
388 "memory_dynamic_max: %i",
389 self["memory_static_min"],
390 self["memory_static_max"],
391 self["memory_dynamic_min"],
392 self["memory_dynamic_max"])
394 if not self["memory_static_min"] <= self["memory_static_max"]:
395 raise XendConfigError("memory_static_min must be less " \
396 "than or equal to memory_static_max")
397 if not self["memory_static_min"] <= self["memory_dynamic_min"]:
398 raise XendConfigError("memory_static_min must be less " \
399 "than or equal to memory_dynamic_min")
400 if not self["memory_dynamic_max"] <= self["memory_static_max"]:
401 raise XendConfigError("memory_dynamic_max must be less " \
402 "than or equal to memory_static_max")
403 if not self["memory_dynamic_max"] > 0:
404 raise XendConfigError("memory_dynamic_max must be greater " \
405 "than zero")
406 if not self["memory_static_max"] > 0:
407 raise XendConfigError("memory_static_max must be greater " \
408 "than zero")
410 def _actions_sanity_check(self):
411 for event in ['shutdown', 'reboot', 'crash']:
412 if self['actions_after_' + event] not in CONFIG_RESTART_MODES:
413 raise XendConfigError('Invalid event handling mode: ' +
414 event)
416 def _vcpus_sanity_check(self):
417 if 'VCPUs_max' in self and 'vcpu_avail' not in self:
418 self['vcpu_avail'] = (1 << self['VCPUs_max']) - 1
420 def _uuid_sanity_check(self):
421 """Make sure UUID is in proper string format with hyphens."""
422 if 'uuid' not in self or not self['uuid']:
423 self['uuid'] = uuid.createString()
424 else:
425 self['uuid'] = uuid.toString(uuid.fromString(self['uuid']))
427 def _name_sanity_check(self):
428 if 'name_label' not in self:
429 self['name_label'] = 'Domain-' + self['uuid']
431 def _platform_sanity_check(self):
432 if 'keymap' not in self['platform'] and XendOptions.instance().get_keymap():
433 self['platform']['keymap'] = XendOptions.instance().get_keymap()
435 if self.is_hvm() or self.has_rfb():
436 if 'device_model' not in self['platform']:
437 self['platform']['device_model'] = xen.util.auxbin.pathTo("qemu-dm")
439 if self.is_hvm():
440 if 'timer_mode' not in self['platform']:
441 self['platform']['timer_mode'] = 0
442 if 'rtc_timeoffset' not in self['platform']:
443 self['platform']['rtc_timeoffset'] = 0
444 if 'hpet' not in self['platform']:
445 self['platform']['hpet'] = 0
446 if 'loader' not in self['platform']:
447 # Old configs may have hvmloader set as PV_kernel param
448 if self.has_key('PV_kernel') and re.search('hvmloader', self['PV_kernel']):
449 self['platform']['loader'] = self['PV_kernel']
450 self['PV_kernel'] = ''
451 else:
452 self['platform']['loader'] = "/usr/lib/xen/boot/hvmloader"
453 log.debug("Loader is %s" % str(self['platform']['loader']))
455 # Compatibility hack, can go away soon.
456 if 'soundhw' not in self['platform'] and \
457 self['platform'].get('enable_audio'):
458 self['platform']['soundhw'] = 'sb16'
460 def validate(self):
461 self._uuid_sanity_check()
462 self._name_sanity_check()
463 self._memory_sanity_check()
464 self._actions_sanity_check()
465 self._vcpus_sanity_check()
466 self._platform_sanity_check()
468 def _dominfo_to_xapi(self, dominfo, update_mem = False):
469 self['domid'] = dominfo['domid']
470 self['online_vcpus'] = dominfo['online_vcpus']
471 self['VCPUs_max'] = dominfo['max_vcpu_id'] + 1
473 if update_mem:
474 self['memory_dynamic_min'] = dominfo['mem_kb'] * 1024
475 self['memory_dynamic_max'] = dominfo['mem_kb'] * 1024
476 self['memory_static_min'] = 0
477 self['memory_static_max'] = dominfo['maxmem_kb'] * 1024
478 self._memory_sanity_check()
480 self['cpu_time'] = dominfo['cpu_time']/1e9
481 if dominfo.get('ssidref'):
482 ssidref = int(dominfo.get('ssidref'))
483 import xen.util.xsm.xsm as security
484 self['security_label'] = security.ssidref2security_label(ssidref)
486 self['shutdown_reason'] = dominfo['shutdown_reason']
488 # parse state into Xen API states
489 self['running'] = dominfo['running']
490 self['crashed'] = dominfo['crashed']
491 self['dying'] = dominfo['dying']
492 self['shutdown'] = dominfo['shutdown']
493 self['paused'] = dominfo['paused']
494 self['blocked'] = dominfo['blocked']
496 if 'name' in dominfo:
497 self['name_label'] = dominfo['name']
499 if 'handle' in dominfo:
500 self['uuid'] = uuid.toString(dominfo['handle'])
502 def parse_cpuid(self, cfg, field):
503 def int2bin(n, count=32):
504 return "".join([str((n >> y) & 1) for y in range(count-1, -1, -1)])
506 for input, regs in cfg[field].iteritems():
507 if not regs is dict:
508 cfg[field][input] = dict(regs)
510 cpuid = {}
511 for input in cfg[field]:
512 inputs = input.split(',')
513 if inputs[0][0:2] == '0x':
514 inputs[0] = str(int(inputs[0], 16))
515 if len(inputs) == 2:
516 if inputs[1][0:2] == '0x':
517 inputs[1] = str(int(inputs[1], 16))
518 new_input = ','.join(inputs)
519 cpuid[new_input] = {} # new input
520 for reg in cfg[field][input]:
521 val = cfg[field][input][reg]
522 if val[0:2] == '0x':
523 cpuid[new_input][reg] = int2bin(int(val, 16))
524 else:
525 cpuid[new_input][reg] = val
526 cfg[field] = cpuid
528 def _parse_sxp(self, sxp_cfg):
529 """ Populate this XendConfig using the parsed SXP.
531 @param sxp_cfg: Parsed SXP Configuration
532 @type sxp_cfg: list of lists
533 @rtype: dictionary
534 @return: A dictionary containing the parsed options of the SXP.
535 """
536 cfg = {}
538 for key, typ in XENAPI_CFG_TYPES.items():
539 val = sxp.child_value(sxp_cfg, key)
540 if val is not None:
541 try:
542 cfg[key] = typ(val)
543 except (ValueError, TypeError), e:
544 log.warn('Unable to convert type value for key: %s' % key)
546 # Convert deprecated options to current equivalents.
548 restart = sxp.child_value(sxp_cfg, 'restart')
549 if restart:
550 if restart == 'onreboot':
551 cfg['on_poweroff'] = 'destroy'
552 cfg['on_reboot'] = 'restart'
553 cfg['on_crash'] = 'destroy'
554 elif restart == 'always':
555 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
556 cfg[opt] = 'restart'
557 elif restart == 'never':
558 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
559 cfg[opt] = 'never'
560 else:
561 log.warn('Ignoring unrecognised value for deprecated option:'
562 'restart = \'%s\'', restart)
564 # Handle memory, passed in as MiB
566 if sxp.child_value(sxp_cfg, "memory") != None:
567 cfg["memory"] = int(sxp.child_value(sxp_cfg, "memory"))
568 if sxp.child_value(sxp_cfg, "maxmem") != None:
569 cfg["maxmem"] = int(sxp.child_value(sxp_cfg, "maxmem"))
571 # Convert scheduling parameters to vcpus_params
572 if 'vcpus_params' not in cfg:
573 cfg['vcpus_params'] = {}
574 cfg["vcpus_params"]["weight"] = \
575 int(sxp.child_value(sxp_cfg, "cpu_weight", 256))
576 cfg["vcpus_params"]["cap"] = \
577 int(sxp.child_value(sxp_cfg, "cpu_cap", 0))
579 # Only extract options we know about.
580 extract_keys = LEGACY_UNSUPPORTED_BY_XENAPI_CFG + \
581 XENAPI_CFG_TO_LEGACY_CFG.values()
583 for key in extract_keys:
584 val = sxp.child_value(sxp_cfg, key)
585 if val != None:
586 try:
587 cfg[key] = LEGACY_CFG_TYPES[key](val)
588 except KeyError:
589 cfg[key] = val
590 except (TypeError, ValueError), e:
591 log.warn("Unable to parse key %s: %s: %s" %
592 (key, str(val), e))
594 if 'platform' not in cfg:
595 cfg['platform'] = {}
596 localtime = sxp.child_value(sxp_cfg, 'localtime')
597 if localtime is not None:
598 cfg['platform']['localtime'] = localtime
600 # Compatibility hack -- can go soon.
601 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
602 val = sxp.child_value(sxp_cfg, "platform_" + key, None)
603 if val is not None:
604 self['platform'][key] = val
606 # Compatibility hack -- can go soon.
607 boot_order = sxp.child_value(sxp_cfg, 'HVM_boot')
608 if boot_order:
609 cfg['HVM_boot_policy'] = 'BIOS order'
610 cfg['HVM_boot_params'] = { 'order' : boot_order }
613 # Parsing the device SXP's.
614 cfg['devices'] = {}
615 for dev in sxp.children(sxp_cfg, 'device'):
616 config = sxp.child0(dev)
617 dev_type = sxp.name(config)
618 self.device_add(dev_type, cfg_sxp = config, target = cfg)
620 # Extract missing data from configuration entries
621 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
622 if image_sxp:
623 image_vcpus = sxp.child_value(image_sxp, 'vcpus')
624 if image_vcpus != None:
625 try:
626 if 'VCPUs_max' not in cfg:
627 cfg['VCPUs_max'] = int(image_vcpus)
628 elif cfg['VCPUs_max'] != int(image_vcpus):
629 cfg['VCPUs_max'] = int(image_vcpus)
630 log.warn('Overriding vcpus from %d to %d using image'
631 'vcpus value.', cfg['VCPUs_max'])
632 except ValueError, e:
633 raise XendConfigError('integer expeceted: %s: %s' %
634 image_sxp, e)
636 # Deprecated cpu configuration
637 if 'cpu' in cfg:
638 if 'cpus' in cfg:
639 cfg['cpus'] = "%s,%s" % (str(cfg['cpu']), cfg['cpus'])
640 else:
641 cfg['cpus'] = str(cfg['cpu'])
643 # Convert 'cpus' to list of ints
644 if 'cpus' in cfg:
645 cpus = []
646 if type(cfg['cpus']) == list:
647 # If sxp_cfg was created from config.sxp,
648 # the form of 'cpus' is list of string.
649 # Convert 'cpus' to list of ints.
650 # ['1'] -> [1]
651 # ['0','2','3'] -> [0,2,3]
652 try:
653 for c in cfg['cpus']:
654 cpus.append(int(c))
656 cfg['cpus'] = cpus
657 except ValueError, e:
658 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
659 else:
660 # Convert 'cpus' string to list of ints
661 # 'cpus' supports a list of ranges (0-3),
662 # seperated by commas, and negation, (^1).
663 # Precedence is settled by order of the
664 # string:
665 # "0-3,^1" -> [0,2,3]
666 # "0-3,^1,1" -> [0,1,2,3]
667 try:
668 for c in cfg['cpus'].split(','):
669 if c.find('-') != -1:
670 (x, y) = c.split('-')
671 for i in range(int(x), int(y)+1):
672 cpus.append(int(i))
673 else:
674 # remove this element from the list
675 if c[0] == '^':
676 cpus = [x for x in cpus if x != int(c[1:])]
677 else:
678 cpus.append(int(c))
680 cfg['cpus'] = cpus
681 except ValueError, e:
682 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
684 # Parse cpuid
685 if 'cpuid' in cfg:
686 self.parse_cpuid(cfg, 'cpuid')
687 if 'cpuid_check' in cfg:
688 self.parse_cpuid(cfg, 'cpuid_check')
690 import xen.util.xsm.xsm as security
691 if security.on() == xsconstants.XS_POLICY_ACM:
692 from xen.util.acmpolicy import ACM_LABEL_UNLABELED
693 if not 'security' in cfg and sxp.child_value(sxp_cfg, 'security'):
694 cfg['security'] = sxp.child_value(sxp_cfg, 'security')
695 elif not cfg.get('security_label'):
696 cfg['security'] = [['access_control',
697 ['policy', security.get_active_policy_name() ],
698 ['label', ACM_LABEL_UNLABELED ]]]
700 if 'security' in cfg and not cfg.get('security_label'):
701 secinfo = cfg['security']
702 # The xm command sends a list formatted like this:
703 # [['access_control', ['policy', 'xm-test'],['label', 'red']],
704 # ['ssidref', 196611]]
705 policy = ""
706 label = ""
707 for idx in range(0, len(secinfo)):
708 if secinfo[idx][0] == "access_control":
709 for aidx in range(1, len(secinfo[idx])):
710 if secinfo[idx][aidx][0] == "policy":
711 policy = secinfo[idx][aidx][1]
712 if secinfo[idx][aidx][0] == "label":
713 label = secinfo[idx][aidx][1]
714 cfg['security_label'] = \
715 security.set_security_label(policy, label)
716 if not sxp.child_value(sxp_cfg, 'security_label'):
717 del cfg['security']
719 sec_lab = cfg['security_label'].split(":")
720 if len(sec_lab) != 3:
721 raise XendConfigError("Badly formatted security label: %s"
722 % cfg['security_label'])
724 old_state = sxp.child_value(sxp_cfg, 'state')
725 if old_state:
726 for i in range(len(CONFIG_OLD_DOM_STATES)):
727 cfg[CONFIG_OLD_DOM_STATES[i]] = int(old_state[i] != '-')
729 return cfg
732 def _sxp_to_xapi(self, sxp_cfg):
733 """Read in an SXP Configuration object and
734 populate at much of the Xen API with valid values.
735 """
736 log.debug('_sxp_to_xapi(%s)' % scrub_password(sxp_cfg))
738 cfg = self._parse_sxp(sxp_cfg)
740 for key, typ in XENAPI_CFG_TYPES.items():
741 val = cfg.get(key)
742 if val is not None:
743 self[key] = typ(val)
745 # Convert parameters that can be directly mapped from
746 # the Legacy Config to Xen API Config
748 for apikey, cfgkey in XENAPI_CFG_TO_LEGACY_CFG.items():
749 try:
750 type_conv = XENAPI_CFG_TYPES.get(apikey)
751 if callable(type_conv):
752 self[apikey] = type_conv(cfg[cfgkey])
753 else:
754 log.warn("Unconverted key: " + apikey)
755 self[apikey] = cfg[cfgkey]
756 except KeyError:
757 pass
759 # Lets try and handle memory correctly
761 MiB = 1024 * 1024
763 if "memory" in cfg:
764 self["memory_static_min"] = 0
765 self["memory_static_max"] = int(cfg["memory"]) * MiB
766 self["memory_dynamic_min"] = int(cfg["memory"]) * MiB
767 self["memory_dynamic_max"] = int(cfg["memory"]) * MiB
769 if "maxmem" in cfg:
770 self["memory_static_max"] = int(cfg["maxmem"]) * MiB
772 self._memory_sanity_check()
774 def update_with(n, o):
775 if not self.get(n):
776 self[n] = cfg.get(o, '')
778 update_with('PV_bootloader', 'bootloader')
779 update_with('PV_bootloader_args', 'bootloader_args')
781 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
782 if image_sxp:
783 self.update_with_image_sxp(image_sxp)
785 # Convert Legacy HVM parameters to Xen API configuration
786 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
787 if key in cfg:
788 self['platform'][key] = cfg[key]
790 # set device references in the configuration
791 self['devices'] = cfg.get('devices', {})
792 self['console_refs'] = cfg.get('console_refs', [])
793 self['vif_refs'] = cfg.get('vif_refs', [])
794 self['vbd_refs'] = cfg.get('vbd_refs', [])
795 self['vtpm_refs'] = cfg.get('vtpm_refs', [])
797 # coalesce hvm vnc frame buffer with vfb config
798 if self.is_hvm() and int(self['platform'].get('vnc', 0)) != 0:
799 # add vfb device if it isn't there already
800 if not self.has_rfb():
801 dev_config = ['vfb']
802 dev_config.append(['type', 'vnc'])
803 # copy VNC related params from platform config to vfb dev conf
804 for key in ['vncpasswd', 'vncunused', 'vncdisplay',
805 'vnclisten']:
806 if key in self['platform']:
807 dev_config.append([key, self['platform'][key]])
809 self.device_add('vfb', cfg_sxp = dev_config)
812 def has_rfb(self):
813 for console_uuid in self['console_refs']:
814 if self['devices'][console_uuid][1].get('protocol') == 'rfb':
815 return True
816 if self['devices'][console_uuid][0] == 'vfb':
817 return True
818 return False
820 def _sxp_to_xapi_unsupported(self, sxp_cfg):
821 """Read in an SXP configuration object and populate
822 values are that not related directly supported in
823 the Xen API.
824 """
826 log.debug('_sxp_to_xapi_unsupported(%s)' % scrub_password(sxp_cfg))
828 # Parse and convert parameters used to configure
829 # the image (as well as HVM images)
830 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
831 if image_sxp:
832 image_type = sxp.name(image_sxp)
833 if image_type != 'hvm' and image_type != 'linux':
834 self['platform']['image_type'] = image_type
836 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
837 val = sxp.child_value(image_sxp, key, None)
838 if val is not None and val != '':
839 self['platform'][key] = val
841 notes = sxp.children(image_sxp, 'notes')
842 if notes:
843 self['notes'] = self.notes_from_sxp(notes[0])
845 self._hvm_boot_params_from_sxp(image_sxp)
847 # extract backend value
849 backend = []
850 for c in sxp.children(sxp_cfg, 'backend'):
851 backend.append(sxp.name(sxp.child0(c)))
852 if backend:
853 self['backend'] = backend
855 # Parse and convert other Non Xen API parameters.
856 def _set_cfg_if_exists(sxp_arg):
857 val = sxp.child_value(sxp_cfg, sxp_arg)
858 if val != None:
859 if LEGACY_CFG_TYPES.get(sxp_arg):
860 self[sxp_arg] = LEGACY_CFG_TYPES[sxp_arg](val)
861 else:
862 self[sxp_arg] = val
864 _set_cfg_if_exists('shadow_memory')
865 _set_cfg_if_exists('features')
866 _set_cfg_if_exists('on_xend_stop')
867 _set_cfg_if_exists('on_xend_start')
868 _set_cfg_if_exists('vcpu_avail')
870 # Parse and store runtime configuration
871 _set_cfg_if_exists('start_time')
872 _set_cfg_if_exists('cpu_time')
873 _set_cfg_if_exists('shutdown_reason')
874 _set_cfg_if_exists('up_time')
875 _set_cfg_if_exists('status') # TODO, deprecated
877 def _get_old_state_string(self):
878 """Returns the old xm state string.
879 @rtype: string
880 @return: old state string
881 """
882 state_string = ''
883 for state_name in CONFIG_OLD_DOM_STATES:
884 on_off = self.get(state_name, 0)
885 if on_off:
886 state_string += state_name[0]
887 else:
888 state_string += '-'
890 return state_string
893 def update_config(self, dominfo):
894 """Update configuration with the output from xc.domain_getinfo().
896 @param dominfo: Domain information via xc.domain_getinfo()
897 @type dominfo: dict
898 """
899 self._dominfo_to_xapi(dominfo)
900 self.validate()
902 def update_with_xenapi_config(self, xapi):
903 """Update configuration with a Xen API VM struct
905 @param xapi: Xen API VM Struct
906 @type xapi: dict
907 """
909 log.debug('update_with_xenapi_config: %s' % scrub_password(xapi))
911 for key, val in xapi.items():
912 type_conv = XENAPI_CFG_TYPES.get(key)
913 if type_conv is None:
914 key = key.lower()
915 type_conv = XENAPI_CFG_TYPES.get(key)
916 if callable(type_conv):
917 self[key] = type_conv(val)
918 else:
919 self[key] = val
921 # XenAPI defines platform as a string-string map. If platform
922 # configuration exists, convert values to appropriate type.
923 if 'platform' in xapi:
924 for key, val in xapi['platform'].items():
925 type_conv = XENAPI_PLATFORM_CFG_TYPES.get(key)
926 if type_conv is None:
927 key = key.lower()
928 type_conv = XENAPI_PLATFORM_CFG_TYPES.get(key)
929 if callable(type_conv):
930 self['platform'][key] = type_conv(val)
931 else:
932 self['platform'][key] = val
934 self['vcpus_params']['weight'] = \
935 int(self['vcpus_params'].get('weight', 256))
936 self['vcpus_params']['cap'] = int(self['vcpus_params'].get('cap', 0))
938 def cpuid_to_sxp(self, sxpr, field):
939 regs_list = []
940 for input, regs in self[field].iteritems():
941 reg_list = []
942 for reg, val in regs.iteritems():
943 reg_list.append([reg, val])
944 regs_list.append([input, reg_list])
945 sxpr.append([field, regs_list])
948 def to_sxp(self, domain = None, ignore_devices = False, ignore = [],
949 legacy_only = True):
950 """ Get SXP representation of this config object.
952 Incompat: removed store_mfn, console_mfn
954 @keyword domain: (optional) XendDomainInfo to get extra information
955 from such as domid and running devices.
956 @type domain: XendDomainInfo
957 @keyword ignore: (optional) list of 'keys' that we do not want
958 to export.
959 @type ignore: list of strings
960 @rtype: list of list (SXP representation)
961 """
962 sxpr = ['domain']
964 # TODO: domid/dom is the same thing but called differently
965 # depending if it is from xenstore or sxpr.
967 if domain.getDomid() is not None:
968 sxpr.append(['domid', domain.getDomid()])
970 if not legacy_only:
971 for name, typ in XENAPI_CFG_TYPES.items():
972 if name in self and self[name] not in (None, []):
973 if typ == dict:
974 s = self[name].items()
975 elif typ == list:
976 s = self[name]
977 else:
978 s = str(self[name])
979 sxpr.append([name, s])
981 for xenapi, legacy in XENAPI_CFG_TO_LEGACY_CFG.items():
982 if legacy in ('cpus'): # skip this
983 continue
984 if self.has_key(xenapi) and self[xenapi] not in (None, []):
985 if type(self[xenapi]) == bool:
986 # convert booleans to ints before making an sxp item
987 sxpr.append([legacy, int(self[xenapi])])
988 else:
989 sxpr.append([legacy, self[xenapi]])
991 MiB = 1024*1024
993 sxpr.append(["maxmem", int(self["memory_static_max"])/MiB])
994 sxpr.append(["memory", int(self["memory_dynamic_max"])/MiB])
996 for legacy in LEGACY_UNSUPPORTED_BY_XENAPI_CFG:
997 if legacy in ('domid', 'uuid', 'cpus'): # skip these
998 continue
999 if self.has_key(legacy) and self[legacy] not in (None, []):
1000 sxpr.append([legacy, self[legacy]])
1002 if self.has_key('security_label'):
1003 sxpr.append(['security_label', self['security_label']])
1005 sxpr.append(['image', self.image_sxpr()])
1006 sxpr.append(['status', domain._stateGet()])
1008 if domain.getDomid() is not None:
1009 sxpr.append(['state', self._get_old_state_string()])
1011 if domain:
1012 if domain.store_mfn:
1013 sxpr.append(['store_mfn', domain.store_mfn])
1014 if domain.console_mfn:
1015 sxpr.append(['console_mfn', domain.console_mfn])
1018 # Marshall devices (running or from configuration)
1019 if not ignore_devices:
1020 txn = xstransact()
1021 try:
1022 for cls in XendDevices.valid_devices():
1023 found = False
1025 # figure if there is a dev controller is valid and running
1026 if domain and domain.getDomid() != None:
1027 try:
1028 controller = domain.getDeviceController(cls)
1029 configs = controller.configurations(txn)
1030 for config in configs:
1031 if sxp.name(config) in ('vbd', 'tap'):
1032 # The bootable flag is never written to the
1033 # store as part of the device config.
1034 dev_uuid = sxp.child_value(config, 'uuid')
1035 dev_type, dev_cfg = self['devices'][dev_uuid]
1036 is_bootable = dev_cfg.get('bootable', 0)
1037 config.append(['bootable', int(is_bootable)])
1038 config.append(['VDI', dev_cfg.get('VDI', '')])
1040 sxpr.append(['device', config])
1042 found = True
1043 except:
1044 log.exception("dumping sxp from device controllers")
1045 pass
1047 # if we didn't find that device, check the existing config
1048 # for a device in the same class
1049 if not found:
1050 for dev_type, dev_info in self.all_devices_sxpr():
1051 if dev_type == cls:
1052 sxpr.append(['device', dev_info])
1054 txn.commit()
1055 except:
1056 txn.abort()
1057 raise
1059 if 'cpuid' in self:
1060 self.cpuid_to_sxp(sxpr, 'cpuid')
1061 if 'cpuid_check' in self:
1062 self.cpuid_to_sxp(sxpr, 'cpuid_check')
1064 log.debug(sxpr)
1066 return sxpr
1068 def _blkdev_name_to_number(self, dev):
1069 if 'ioemu:' in dev:
1070 _, dev = dev.split(':', 1)
1071 try:
1072 dev, _ = dev.split(':', 1)
1073 except ValueError:
1074 pass
1076 try:
1077 devid = int(dev)
1078 except ValueError:
1079 # devid is not a number but a string containing either device
1080 # name (e.g. xvda) or device_type/device_id (e.g. vbd/51728)
1081 dev2 = type(dev) is str and dev.split('/')[-1] or None
1082 if dev2 == None:
1083 log.debug("Could not check the device %s", dev)
1084 return None
1085 try:
1086 devid = int(dev2)
1087 except ValueError:
1088 devid = blkdev_name_to_number(dev2)
1089 if devid == None:
1090 log.debug("The device %s is not device name", dev2)
1091 return None
1092 return devid
1094 def device_duplicate_check(self, dev_type, dev_info, defined_config):
1095 defined_devices_sxpr = self.all_devices_sxpr(target = defined_config)
1097 if dev_type == 'vbd' or dev_type == 'tap':
1098 dev_uname = dev_info.get('uname')
1099 blkdev_name = dev_info.get('dev')
1100 devid = self._blkdev_name_to_number(blkdev_name)
1101 if devid == None or dev_uname == None:
1102 return
1104 for o_dev_type, o_dev_info in defined_devices_sxpr:
1105 if o_dev_type == 'vbd' or o_dev_type == 'tap':
1106 blkdev_file = blkdev_uname_to_file(dev_uname)
1107 o_dev_uname = sxp.child_value(o_dev_info, 'uname')
1108 o_blkdev_file = blkdev_uname_to_file(o_dev_uname)
1109 if blkdev_file == o_blkdev_file:
1110 raise XendConfigError('The file "%s" is already used' %
1111 blkdev_file)
1112 o_blkdev_name = sxp.child_value(o_dev_info, 'dev')
1113 o_devid = self._blkdev_name_to_number(o_blkdev_name)
1114 if o_devid != None and devid == o_devid:
1115 raise XendConfigError('The device "%s" is already defined' %
1116 blkdev_name)
1118 elif dev_type == 'vif':
1119 dev_mac = dev_info.get('mac')
1121 for o_dev_type, o_dev_info in defined_devices_sxpr:
1122 if dev_type == o_dev_type:
1123 if dev_mac.lower() == sxp.child_value(o_dev_info, 'mac').lower():
1124 raise XendConfigError('The mac "%s" is already defined' %
1125 dev_mac)
1127 def device_add(self, dev_type, cfg_sxp = None, cfg_xenapi = None,
1128 target = None):
1129 """Add a device configuration in SXP format or XenAPI struct format.
1131 For SXP, it could be either:
1133 [device, [vbd, [uname ...]]
1135 or:
1137 [vbd, [uname ..]]
1139 @type cfg_sxp: list of lists (parsed sxp object)
1140 @param cfg_sxp: SXP configuration object
1141 @type cfg_xenapi: dict
1142 @param cfg_xenapi: A device configuration from Xen API (eg. vbd,vif)
1143 @param target: write device information to
1144 @type target: None or a dictionary
1145 @rtype: string
1146 @return: Assigned UUID of the device.
1147 """
1148 if target == None:
1149 target = self
1151 if dev_type not in XendDevices.valid_devices():
1152 raise XendConfigError("XendConfig: %s not a valid device type" %
1153 dev_type)
1155 if cfg_sxp == None and cfg_xenapi == None:
1156 raise XendConfigError("XendConfig: device_add requires some "
1157 "config.")
1159 #if cfg_sxp:
1160 # log.debug("XendConfig.device_add: %s" % str(cfg_sxp))
1161 #if cfg_xenapi:
1162 # log.debug("XendConfig.device_add: %s" % str(cfg_xenapi))
1164 if cfg_sxp:
1165 if sxp.child0(cfg_sxp) == 'device':
1166 config = sxp.child0(cfg_sxp)
1167 else:
1168 config = cfg_sxp
1170 dev_type = sxp.name(config)
1171 dev_info = {}
1173 # Parsing the device SXP's. In most cases, the SXP looks
1174 # like this:
1176 # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]]
1178 # However, for PCI devices it looks like this:
1180 # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1]]]]
1182 # It seems the reasoning for this difference is because
1183 # pciif.py needs all the PCI device configurations at
1184 # the same time when creating the devices.
1186 # To further complicate matters, Xen 2.0 configuration format
1187 # uses the following for pci device configuration:
1189 # [device, [pci, [domain, 0], [bus, 0], [dev, 1], [func, 2]]]
1191 if dev_type == 'pci':
1192 pci_devs_uuid = sxp.child_value(config, 'uuid',
1193 uuid.createString())
1194 pci_devs = []
1195 for pci_dev in sxp.children(config, 'dev'):
1196 pci_dev_info = {}
1197 for opt_val in pci_dev[1:]:
1198 try:
1199 opt, val = opt_val
1200 pci_dev_info[opt] = val
1201 except TypeError:
1202 pass
1203 pci_devs.append(pci_dev_info)
1204 target['devices'][pci_devs_uuid] = (dev_type,
1205 {'devs': pci_devs,
1206 'uuid': pci_devs_uuid})
1208 log.debug("XendConfig: reading device: %s" % pci_devs)
1209 return pci_devs_uuid
1211 for opt_val in config[1:]:
1212 try:
1213 opt, val = opt_val
1214 dev_info[opt] = val
1215 except (TypeError, ValueError): # unpack error
1216 pass
1218 if dev_type == 'vbd':
1219 dev_info['bootable'] = 0
1220 if dev_info.get('dev', '').startswith('ioemu:'):
1221 dev_info['driver'] = 'ioemu'
1222 else:
1223 dev_info['driver'] = 'paravirtualised'
1225 if dev_type == 'tap':
1226 if dev_info['uname'].split(':')[1] not in blktap_disk_types:
1227 raise XendConfigError("tap:%s not a valid disk type" %
1228 dev_info['uname'].split(':')[1])
1230 if dev_type == 'vif':
1231 if not dev_info.get('mac'):
1232 dev_info['mac'] = randomMAC()
1234 self.device_duplicate_check(dev_type, dev_info, target)
1236 if dev_type == 'vif':
1237 if dev_info.get('policy') and dev_info.get('label'):
1238 dev_info['security_label'] = "%s:%s:%s" % \
1239 (xsconstants.ACM_POLICY_ID,
1240 dev_info['policy'],dev_info['label'])
1242 # create uuid if it doesn't exist
1243 dev_uuid = dev_info.get('uuid', None)
1244 if not dev_uuid:
1245 dev_uuid = uuid.createString()
1246 dev_info['uuid'] = dev_uuid
1248 # store dev references by uuid for certain device types
1249 target['devices'][dev_uuid] = (dev_type, dev_info)
1250 if dev_type in ('vif', 'vbd', 'vtpm'):
1251 param = '%s_refs' % dev_type
1252 if param not in target:
1253 target[param] = []
1254 if dev_uuid not in target[param]:
1255 if dev_type == 'vbd':
1256 # Compat hack -- mark first disk bootable
1257 dev_info['bootable'] = int(not target[param])
1258 target[param].append(dev_uuid)
1259 elif dev_type == 'tap':
1260 if 'vbd_refs' not in target:
1261 target['vbd_refs'] = []
1262 if dev_uuid not in target['vbd_refs']:
1263 # Compat hack -- mark first disk bootable
1264 dev_info['bootable'] = int(not target['vbd_refs'])
1265 target['vbd_refs'].append(dev_uuid)
1267 elif dev_type == 'vfb':
1268 # Populate other config with aux data that is associated
1269 # with vfb
1271 other_config = {}
1272 for key in XENAPI_CONSOLE_OTHER_CFG:
1273 if key in dev_info:
1274 other_config[key] = dev_info[key]
1275 target['devices'][dev_uuid][1]['other_config'] = other_config
1278 if 'console_refs' not in target:
1279 target['console_refs'] = []
1281 # Treat VFB devices as console devices so they are found
1282 # through Xen API
1283 if dev_uuid not in target['console_refs']:
1284 target['console_refs'].append(dev_uuid)
1286 elif dev_type == 'console':
1287 if 'console_refs' not in target:
1288 target['console_refs'] = []
1289 if dev_uuid not in target['console_refs']:
1290 target['console_refs'].append(dev_uuid)
1292 log.debug("XendConfig: reading device: %s" % scrub_password(dev_info))
1293 return dev_uuid
1295 if cfg_xenapi:
1296 dev_info = {}
1297 dev_uuid = ''
1298 if dev_type == 'vif':
1299 dev_info['mac'] = cfg_xenapi.get('MAC')
1300 if not dev_info['mac']:
1301 dev_info['mac'] = randomMAC()
1302 # vifname is the name on the guest, not dom0
1303 # TODO: we don't have the ability to find that out or
1304 # change it from dom0
1305 #if cfg_xenapi.get('device'): # don't add if blank
1306 # dev_info['vifname'] = cfg_xenapi.get('device')
1307 if cfg_xenapi.get('type'):
1308 dev_info['type'] = cfg_xenapi.get('type')
1309 if cfg_xenapi.get('name'):
1310 dev_info['name'] = cfg_xenapi.get('name')
1311 if cfg_xenapi.get('network'):
1312 network = XendAPIStore.get(
1313 cfg_xenapi.get('network'), 'network')
1314 dev_info['bridge'] = network.get_name_label()
1316 if cfg_xenapi.get('security_label'):
1317 dev_info['security_label'] = \
1318 cfg_xenapi.get('security_label')
1320 dev_uuid = cfg_xenapi.get('uuid', None)
1321 if not dev_uuid:
1322 dev_uuid = uuid.createString()
1323 dev_info['uuid'] = dev_uuid
1324 target['devices'][dev_uuid] = (dev_type, dev_info)
1325 target['vif_refs'].append(dev_uuid)
1327 elif dev_type in ('vbd', 'tap'):
1328 dev_info['type'] = cfg_xenapi.get('type', 'Disk')
1329 if dev_info['type'] == 'CD':
1330 old_vbd_type = 'cdrom'
1331 else:
1332 old_vbd_type = 'disk'
1334 dev_info['uname'] = cfg_xenapi.get('image', '')
1335 dev_info['dev'] = '%s:%s' % (cfg_xenapi.get('device'),
1336 old_vbd_type)
1337 dev_info['bootable'] = int(cfg_xenapi.get('bootable', 0))
1338 dev_info['driver'] = cfg_xenapi.get('driver', '')
1339 dev_info['VDI'] = cfg_xenapi.get('VDI', '')
1341 if cfg_xenapi.get('mode') == 'RW':
1342 dev_info['mode'] = 'w'
1343 else:
1344 dev_info['mode'] = 'r'
1346 dev_uuid = cfg_xenapi.get('uuid', None)
1347 if not dev_uuid:
1348 dev_uuid = uuid.createString()
1349 dev_info['uuid'] = dev_uuid
1350 target['devices'][dev_uuid] = (dev_type, dev_info)
1351 target['vbd_refs'].append(dev_uuid)
1353 elif dev_type == 'vtpm':
1354 if cfg_xenapi.get('type'):
1355 dev_info['type'] = cfg_xenapi.get('type')
1357 dev_uuid = cfg_xenapi.get('uuid', None)
1358 if not dev_uuid:
1359 dev_uuid = uuid.createString()
1360 dev_info['uuid'] = dev_uuid
1361 dev_info['other_config'] = cfg_xenapi.get('other_config', {})
1362 target['devices'][dev_uuid] = (dev_type, dev_info)
1363 target['vtpm_refs'].append(dev_uuid)
1365 elif dev_type == 'console':
1366 dev_uuid = cfg_xenapi.get('uuid', None)
1367 if not dev_uuid:
1368 dev_uuid = uuid.createString()
1369 dev_info['uuid'] = dev_uuid
1370 dev_info['protocol'] = cfg_xenapi.get('protocol', 'rfb')
1371 dev_info['other_config'] = cfg_xenapi.get('other_config', {})
1372 if dev_info['protocol'] == 'rfb':
1373 # collapse other config into devinfo for things
1374 # such as vncpasswd, vncunused, etc.
1375 dev_info.update(cfg_xenapi.get('other_config', {}))
1376 dev_info['type'] = 'vnc'
1377 target['devices'][dev_uuid] = ('vfb', dev_info)
1378 target['console_refs'].append(dev_uuid)
1380 # if console is rfb, set device_model ensuring qemu
1381 # is invoked for pvfb services
1382 if 'device_model' not in target['platform']:
1383 target['platform']['device_model'] = \
1384 xen.util.auxbin.pathTo("qemu-dm")
1386 # Finally, if we are a pvfb, we need to make a vkbd
1387 # as well that is not really exposed to Xen API
1388 vkbd_uuid = uuid.createString()
1389 target['devices'][vkbd_uuid] = ('vkbd', {})
1391 elif dev_info['protocol'] == 'vt100':
1392 # if someone tries to create a VT100 console
1393 # via the Xen API, we'll have to ignore it
1394 # because we create one automatically in
1395 # XendDomainInfo._update_consoles
1396 raise XendConfigError('Creating vt100 consoles via '
1397 'Xen API is unsupported')
1399 return dev_uuid
1401 # no valid device to add
1402 return ''
1404 def phantom_device_add(self, dev_type, cfg_xenapi = None,
1405 target = None):
1406 """Add a phantom tap device configuration in XenAPI struct format.
1407 """
1409 if target == None:
1410 target = self
1412 if dev_type not in XendDevices.valid_devices() and \
1413 dev_type not in XendDevices.pseudo_devices():
1414 raise XendConfigError("XendConfig: %s not a valid device type" %
1415 dev_type)
1417 if cfg_xenapi == None:
1418 raise XendConfigError("XendConfig: device_add requires some "
1419 "config.")
1421 if cfg_xenapi:
1422 log.debug("XendConfig.phantom_device_add: %s" % str(cfg_xenapi))
1424 if cfg_xenapi:
1425 dev_info = {}
1426 if dev_type in ('vbd', 'tap'):
1427 if dev_type == 'vbd':
1428 dev_info['uname'] = cfg_xenapi.get('image', '')
1429 dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
1430 elif dev_type == 'tap':
1431 if cfg_xenapi.get('image').find('tap:') == -1:
1432 dev_info['uname'] = 'tap:qcow:%s' % cfg_xenapi.get('image')
1433 dev_info['dev'] = '/dev/%s' % cfg_xenapi.get('device')
1434 dev_info['uname'] = cfg_xenapi.get('image')
1435 dev_info['mode'] = cfg_xenapi.get('mode')
1436 dev_info['backend'] = '0'
1437 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
1438 dev_info['uuid'] = dev_uuid
1439 self['devices'][dev_uuid] = (dev_type, dev_info)
1440 self['vbd_refs'].append(dev_uuid)
1441 return dev_uuid
1443 return ''
1445 def console_add(self, protocol, location, other_config = {}):
1446 dev_uuid = uuid.createString()
1447 if protocol == 'vt100':
1448 dev_info = {
1449 'uuid': dev_uuid,
1450 'protocol': protocol,
1451 'location': location,
1452 'other_config': other_config,
1455 if 'devices' not in self:
1456 self['devices'] = {}
1458 self['devices'][dev_uuid] = ('console', dev_info)
1459 self['console_refs'].append(dev_uuid)
1460 return dev_info
1462 return {}
1464 def console_update(self, console_uuid, key, value):
1465 for dev_uuid, (dev_type, dev_info) in self['devices'].items():
1466 if dev_uuid == console_uuid:
1467 dev_info[key] = value
1468 # collapse other_config into dev_info for things
1469 # such as vncpasswd, vncunused, etc.
1470 if key == 'other_config':
1471 for k in XENAPI_CONSOLE_OTHER_CFG:
1472 if k in dev_info and k not in value:
1473 del dev_info[k]
1474 dev_info.update(value)
1475 break
1477 def console_get_all(self, protocol):
1478 if protocol == 'vt100':
1479 consoles = [dinfo for dtype, dinfo in self['devices'].values()
1480 if dtype == 'console']
1481 return [c for c in consoles if c.get('protocol') == protocol]
1483 elif protocol == 'rfb':
1484 vfbs = [dinfo for dtype, dinfo in self['devices'].values()
1485 if dtype == 'vfb']
1487 # move all non-console key values to other_config before
1488 # returning console config
1489 valid_keys = ['uuid', 'location']
1490 for vfb in vfbs:
1491 other_config = {}
1492 for key, val in vfb.items():
1493 if key not in valid_keys:
1494 other_config[key] = vfb[key]
1495 del vfb[key]
1496 vfb['other_config'] = other_config
1497 vfb['protocol'] = 'rfb'
1499 return vfbs
1501 else:
1502 return []
1504 def device_update(self, dev_uuid, cfg_sxp = [], cfg_xenapi = {}):
1505 """Update an existing device with the new configuration.
1507 @rtype: boolean
1508 @return: Returns True if succesfully found and updated a device conf
1509 """
1510 if dev_uuid in self['devices'] and cfg_sxp:
1511 if sxp.child0(cfg_sxp) == 'device':
1512 config = sxp.child0(cfg_sxp)
1513 else:
1514 config = cfg_sxp
1516 dev_type, dev_info = self['devices'][dev_uuid]
1518 if dev_type == 'pci': # Special case for pci
1519 pci_devs = []
1520 for pci_dev in sxp.children(config, 'dev'):
1521 pci_dev_info = {}
1522 for opt_val in pci_dev[1:]:
1523 try:
1524 opt, val = opt_val
1525 pci_dev_info[opt] = val
1526 except TypeError:
1527 pass
1528 pci_devs.append(pci_dev_info)
1529 self['devices'][dev_uuid] = (dev_type,
1530 {'devs': pci_devs,
1531 'uuid': dev_uuid})
1532 return True
1534 for opt_val in config[1:]:
1535 try:
1536 opt, val = opt_val
1537 dev_info[opt] = val
1538 except (TypeError, ValueError):
1539 pass # no value for this config option
1541 self['devices'][dev_uuid] = (dev_type, dev_info)
1542 return True
1544 elif dev_uuid in self['devices'] and cfg_xenapi:
1545 dev_type, dev_info = self['devices'][dev_uuid]
1546 for key, val in cfg_xenapi.items():
1547 dev_info[key] = val
1548 self['devices'][dev_uuid] = (dev_type, dev_info)
1550 return False
1553 def device_sxpr(self, dev_uuid = None, dev_type = None, dev_info = None, target = None):
1554 """Get Device SXPR by either giving the device UUID or (type, config).
1556 @rtype: list of lists
1557 @return: device config sxpr
1558 """
1559 sxpr = []
1561 if target == None:
1562 target = self
1564 if dev_uuid != None and dev_uuid in target['devices']:
1565 dev_type, dev_info = target['devices'][dev_uuid]
1567 if dev_type == None or dev_info == None:
1568 raise XendConfigError("Required either UUID or device type and "
1569 "configuration dictionary.")
1571 sxpr.append(dev_type)
1572 if dev_type in ('console', 'vfb'):
1573 config = [(opt, val) for opt, val in dev_info.items()
1574 if opt != 'other_config']
1575 else:
1576 config = [(opt, val) for opt, val in dev_info.items()]
1578 sxpr += config
1580 return sxpr
1582 def ordered_device_refs(self, target = None):
1583 result = []
1585 if target == None:
1586 target = self
1588 # vkbd devices *must* be before vfb devices, otherwise
1589 # there is a race condition when setting up devices
1590 # where the daemon spawned for the vfb may write stuff
1591 # into xenstore vkbd backend, before DevController has
1592 # setup permissions on the vkbd backend path. This race
1593 # results in domain creation failing with 'device already
1594 # connected' messages
1595 result.extend([u for u in target['devices'].keys() if target['devices'][u][0] == 'vkbd'])
1597 result.extend(target.get('console_refs', []) +
1598 target.get('vbd_refs', []) +
1599 target.get('vif_refs', []) +
1600 target.get('vtpm_refs', []))
1602 result.extend([u for u in target['devices'].keys() if u not in result])
1603 return result
1605 def all_devices_sxpr(self, target = None):
1606 """Returns the SXPR for all devices in the current configuration."""
1607 sxprs = []
1608 pci_devs = []
1610 if target == None:
1611 target = self
1613 if 'devices' not in target:
1614 return sxprs
1616 ordered_refs = self.ordered_device_refs(target = target)
1617 for dev_uuid in ordered_refs:
1618 dev_type, dev_info = target['devices'][dev_uuid]
1619 if dev_type == 'pci': # special case for pci devices
1620 sxpr = ['pci', ['uuid', dev_info['uuid']]]
1621 for pci_dev_info in dev_info['devs']:
1622 pci_dev_sxpr = ['dev']
1623 for opt, val in pci_dev_info.items():
1624 pci_dev_sxpr.append([opt, val])
1625 sxpr.append(pci_dev_sxpr)
1626 sxprs.append((dev_type, sxpr))
1627 else:
1628 sxpr = self.device_sxpr(dev_type = dev_type,
1629 dev_info = dev_info,
1630 target = target)
1631 sxprs.append((dev_type, sxpr))
1633 return sxprs
1635 def image_sxpr(self):
1636 """Returns a backwards compatible image SXP expression that is
1637 used in xenstore's /vm/<uuid>/image value and xm list."""
1638 image = [self.image_type()]
1639 if self.has_key('PV_kernel'):
1640 image.append(['kernel', self['PV_kernel']])
1641 if self.has_key('PV_ramdisk') and self['PV_ramdisk']:
1642 image.append(['ramdisk', self['PV_ramdisk']])
1643 if self.has_key('PV_args') and self['PV_args']:
1644 image.append(['args', self['PV_args']])
1646 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
1647 if key in self['platform']:
1648 image.append([key, self['platform'][key]])
1650 if 'notes' in self:
1651 image.append(self.notes_sxp(self['notes']))
1653 return image
1655 def update_with_image_sxp(self, image_sxp, bootloader = False):
1656 # Convert Legacy "image" config to Xen API PV_*
1657 # configuration
1658 log.debug("update_with_image_sxp(%s)" % scrub_password(image_sxp))
1660 # user-specified args must come last: previous releases did this and
1661 # some domU kernels rely upon the ordering.
1662 kernel_args = sxp.child_value(image_sxp, 'args', '')
1664 # attempt to extract extra arguments from SXP config
1665 arg_ip = sxp.child_value(image_sxp, 'ip')
1666 if arg_ip and not re.search(r'ip=[^ ]+', kernel_args):
1667 kernel_args = 'ip=%s ' % arg_ip + kernel_args
1668 arg_root = sxp.child_value(image_sxp, 'root')
1669 if arg_root and not re.search(r'root=', kernel_args):
1670 kernel_args = 'root=%s ' % arg_root + kernel_args
1672 if bootloader:
1673 self['_temp_using_bootloader'] = '1'
1674 self['_temp_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1675 self['_temp_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1676 self['_temp_args'] = kernel_args
1677 else:
1678 self['PV_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1679 self['PV_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1680 self['PV_args'] = kernel_args
1682 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
1683 val = sxp.child_value(image_sxp, key, None)
1684 if val is not None and val != '':
1685 self['platform'][key] = val
1687 notes = sxp.children(image_sxp, 'notes')
1688 if notes:
1689 self['notes'] = self.notes_from_sxp(notes[0])
1691 self._hvm_boot_params_from_sxp(image_sxp)
1693 def set_notes(self, notes):
1694 'Add parsed elfnotes to image'
1695 self['notes'] = notes
1697 def get_notes(self):
1698 try:
1699 return self['notes'] or {}
1700 except KeyError:
1701 return {}
1703 def notes_from_sxp(self, nsxp):
1704 notes = {}
1705 for note in sxp.children(nsxp):
1706 notes[note[0]] = note[1]
1707 return notes
1709 def notes_sxp(self, notes):
1710 nsxp = ['notes']
1711 for k, v in notes.iteritems():
1712 nsxp.append([k, str(v)])
1713 return nsxp
1715 def _hvm_boot_params_from_sxp(self, image_sxp):
1716 boot = sxp.child_value(image_sxp, 'boot', None)
1717 if boot is not None:
1718 self['HVM_boot_policy'] = 'BIOS order'
1719 self['HVM_boot_params'] = { 'order' : boot }
1721 def is_hvm(self):
1722 return self['HVM_boot_policy'] != ''
1724 def target(self):
1725 return self['target']
1727 def image_type(self):
1728 stored_type = self['platform'].get('image_type')
1729 return stored_type or (self.is_hvm() and 'hvm' or 'linux')
1731 def is_hap(self):
1732 return self['platform'].get('hap', 0)