ia64/xen-unstable

view tools/python/xen/xend/XendConfig.py @ 17993:bd97e45e073a

pvSCSI: fix xend

Previous "xend" assumed initial Xenbus state would be "Connected" when
LUN hot-plug starts. However it was not guaranteed in general, and it
may cause some problems.

Signed-off-by: Tomonari Horikoshi <t.horikoshi@jp.fujitsu.com>
Signed-off-by: Jun Kamada <kama@jp.fujitsu.com>
author Keir Fraser <keir.fraser@citrix.com>
date Tue Jul 08 09:28:50 2008 +0100 (2008-07-08)
parents 6ae87b27ccea
children f40c310dca31
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.XendPPCI import XendPPCI
28 from xen.xend.XendDPCI import XendDPCI
29 from xen.xend.XendError import VmError
30 from xen.xend.XendDevices import XendDevices
31 from xen.xend.PrettyPrint import prettyprintstring
32 from xen.xend.XendConstants import DOM_STATE_HALTED
33 from xen.xend.xenstore.xstransact import xstransact
34 from xen.xend.server.BlktapController import blktap_disk_types
35 from xen.xend.server.netif import randomMAC
36 from xen.util.blkif import blkdev_name_to_number, blkdev_uname_to_file
37 from xen.util import xsconstants
38 import xen.util.auxbin
40 log = logging.getLogger("xend.XendConfig")
41 log.setLevel(logging.WARN)
44 """
45 XendConfig API
47 XendConfig will try to mirror as closely the Xen API VM Struct
48 with extra parameters for those options that are not supported.
50 """
52 def reverse_dict(adict):
53 """Return the reverse mapping of a dictionary."""
54 return dict([(v, k) for k, v in adict.items()])
56 def bool0(v):
57 return v != '0' and v != 'False' and bool(v)
59 # Recursively copy a data struct, scrubbing out VNC passwords.
60 # Will scrub any dict entry with a key of 'vncpasswd' or any
61 # 2-element list whose first member is 'vncpasswd'. It will
62 # also scrub a string matching '(vncpasswd XYZ)'. Everything
63 # else is no-op passthrough
64 def scrub_password(data):
65 if type(data) == dict or type(data) == XendConfig:
66 scrubbed = {}
67 for key in data.keys():
68 if key == "vncpasswd":
69 scrubbed[key] = "XXXXXXXX"
70 else:
71 scrubbed[key] = scrub_password(data[key])
72 return scrubbed
73 elif type(data) == list:
74 if len(data) == 2 and type(data[0]) == str and data[0] == 'vncpasswd':
75 return ['vncpasswd', 'XXXXXXXX']
76 else:
77 scrubbed = []
78 for entry in data:
79 scrubbed.append(scrub_password(entry))
80 return scrubbed
81 elif type(data) == tuple:
82 scrubbed = []
83 for entry in data:
84 scrubbed.append(scrub_password(entry))
85 return tuple(scrubbed)
86 elif type(data) == str:
87 return re.sub(r'\(vncpasswd\s+[^\)]+\)','(vncpasswd XXXXXX)', data)
88 else:
89 return data
91 #
92 # CPU fields:
93 #
94 # VCPUs_max -- the maximum number of vcpus that this domain may ever have.
95 # aka XendDomainInfo.getVCpuCount().
96 # vcpus -- the legacy configuration name for above.
97 # max_vcpu_id -- vcpus_number - 1. This is given to us by Xen.
98 #
99 # cpus -- the list of pCPUs available to each vCPU.
100 #
101 # vcpu_avail -- a bitmap telling the guest domain whether it may use each of
102 # its VCPUs. This is translated to
103 # <dompath>/cpu/<id>/availability = {online,offline} for use
104 # by the guest domain.
105 # VCPUs_live -- the number of VCPUs currently up, as reported by Xen. This
106 # is changed by changing vcpu_avail, and waiting for the
107 # domain to respond.
108 #
111 # Mapping from XendConfig configuration keys to the old
112 # legacy configuration keys that map directly.
114 XENAPI_CFG_TO_LEGACY_CFG = {
115 'uuid': 'uuid',
116 'VCPUs_max': 'vcpus',
117 'cpus': 'cpus',
118 'name_label': 'name',
119 'actions_after_shutdown': 'on_poweroff',
120 'actions_after_reboot': 'on_reboot',
121 'actions_after_crash': 'on_crash',
122 'PV_bootloader': 'bootloader',
123 'PV_bootloader_args': 'bootloader_args',
124 }
126 LEGACY_CFG_TO_XENAPI_CFG = reverse_dict(XENAPI_CFG_TO_LEGACY_CFG)
128 # Platform configuration keys and their types.
129 XENAPI_PLATFORM_CFG_TYPES = {
130 'acpi': int,
131 'apic': int,
132 'boot': str,
133 'device_model': str,
134 'loader': str,
135 'display' : str,
136 'fda': str,
137 'fdb': str,
138 'keymap': str,
139 'isa' : int,
140 'localtime': int,
141 'monitor': int,
142 'nographic': int,
143 'pae' : int,
144 'rtc_timeoffset': int,
145 'serial': str,
146 'sdl': int,
147 'opengl': int,
148 'soundhw': str,
149 'stdvga': int,
150 'usb': int,
151 'usbdevice': str,
152 'hpet': int,
153 'vnc': int,
154 'vncconsole': int,
155 'vncdisplay': int,
156 'vnclisten': str,
157 'timer_mode': int,
158 'vncpasswd': str,
159 'vncunused': int,
160 'xauthority': str,
161 'pci': str,
162 'vhpt': int,
163 'guest_os_type': str,
164 'hap': int,
165 }
167 # Xen API console 'other_config' keys.
168 XENAPI_CONSOLE_OTHER_CFG = ['vncunused', 'vncdisplay', 'vnclisten',
169 'vncpasswd', 'type', 'display', 'xauthority',
170 'keymap', 'opengl']
172 # List of XendConfig configuration keys that have no direct equivalent
173 # in the old world.
175 XENAPI_CFG_TYPES = {
176 'uuid': str,
177 'name_label': str,
178 'name_description': str,
179 'user_version': str,
180 'is_a_template': bool0,
181 'resident_on': str,
182 'memory_static_min': int, # note these are stored in bytes, not KB!
183 'memory_static_max': int,
184 'memory_dynamic_min': int,
185 'memory_dynamic_max': int,
186 'cpus': list,
187 'vcpus_params': dict,
188 'VCPUs_max': int,
189 'VCPUs_at_startup': int,
190 'VCPUs_live': int,
191 'actions_after_shutdown': str,
192 'actions_after_reboot': str,
193 'actions_after_crash': str,
194 'PV_bootloader': str,
195 'PV_kernel': str,
196 'PV_ramdisk': str,
197 'PV_args': str,
198 'PV_bootloader_args': str,
199 'HVM_boot_policy': str,
200 'HVM_boot_params': dict,
201 'PCI_bus': str,
202 'platform': dict,
203 'tools_version': dict,
204 'other_config': dict,
205 'target': int,
206 'security_label': str,
207 'pci': str,
208 'cpuid' : dict,
209 'cpuid_check' : dict,
210 }
212 # List of legacy configuration keys that have no equivalent in the
213 # Xen API, but are still stored in XendConfig.
215 LEGACY_UNSUPPORTED_BY_XENAPI_CFG = [
216 # roundtripped (dynamic, unmodified)
217 'shadow_memory',
218 'vcpu_avail',
219 'features',
220 # read/write
221 'on_xend_start',
222 'on_xend_stop',
223 # read-only
224 'domid',
225 'start_time',
226 'cpu_time',
227 'online_vcpus',
228 # write-once
229 'cpu',
230 'cpus',
231 ]
233 LEGACY_CFG_TYPES = {
234 'uuid': str,
235 'name': str,
236 'vcpus': int,
237 'vcpu_avail': long,
238 'memory': int,
239 'shadow_memory': int,
240 'maxmem': int,
241 'start_time': float,
242 'cpu_time': float,
243 'features': str,
244 'localtime': int,
245 'name': str,
246 'on_poweroff': str,
247 'on_reboot': str,
248 'on_crash': str,
249 'on_xend_stop': str,
250 'on_xend_start': str,
251 'online_vcpus': int,
252 'rtc/timeoffset': str,
253 }
255 # Values that should be stored in xenstore's /vm/<uuid> that is used
256 # by Xend. Used in XendDomainInfo to restore running VM state from
257 # xenstore.
258 LEGACY_XENSTORE_VM_PARAMS = [
259 'uuid',
260 'name',
261 'vcpus',
262 'vcpu_avail',
263 'memory',
264 'shadow_memory',
265 'maxmem',
266 'start_time',
267 'name',
268 'on_poweroff',
269 'on_crash',
270 'on_reboot',
271 'on_xend_start',
272 'on_xend_stop',
273 ]
275 ##
276 ## Config Choices
277 ##
279 CONFIG_RESTART_MODES = ('restart', 'destroy', 'preserve', 'rename-restart',
280 'coredump-destroy', 'coredump-restart')
281 CONFIG_OLD_DOM_STATES = ('running', 'blocked', 'paused', 'shutdown',
282 'crashed', 'dying')
284 class XendConfigError(VmError):
285 def __str__(self):
286 return 'Invalid Configuration: %s' % str(self.value)
288 ##
289 ## XendConfig Class (an extended dictionary)
290 ##
292 class XendConfig(dict):
293 """ The new Xend VM Configuration.
295 Stores the configuration in xenapi compatible format but retains
296 import and export functions for SXP.
297 """
298 def __init__(self, filename = None, sxp_obj = None,
299 xapi = None, dominfo = None):
301 dict.__init__(self)
302 self.update(self._defaults())
304 if filename:
305 try:
306 sxp_obj = sxp.parse(open(filename,'r'))
307 sxp_obj = sxp_obj[0]
308 except IOError, e:
309 raise XendConfigError("Unable to read file: %s" % filename)
311 if sxp_obj:
312 self._sxp_to_xapi(sxp_obj)
313 self._sxp_to_xapi_unsupported(sxp_obj)
314 elif xapi:
315 self.update_with_xenapi_config(xapi)
316 elif dominfo:
317 # output from xc.domain_getinfo
318 self._dominfo_to_xapi(dominfo, update_mem = True)
320 log.debug('XendConfig.init: %s' % scrub_password(self))
322 # validators go here
323 self.validate()
325 """ In time, we should enable this type checking addition. It is great
326 also for tracking bugs and unintended writes to XendDomainInfo.info
327 def __setitem__(self, key, value):
328 type_conv = XENAPI_CFG_TYPES.get(key)
329 if callable(type_conv):
330 try:
331 dict.__setitem__(self, key, type_conv(value))
332 except (ValueError, TypeError):
333 raise XendConfigError("Wrong type for configuration value " +
334 "%s. Expected %s" %
335 (key, type_conv.__name__))
336 else:
337 dict.__setitem__(self, key, value)
338 """
340 def _defaults(self):
341 defaults = {
342 'name_label': 'Domain-Unnamed',
343 'actions_after_shutdown': 'destroy',
344 'actions_after_reboot': 'restart',
345 'actions_after_crash': 'restart',
346 'actions_after_suspend': '',
347 'is_a_template': False,
348 'is_control_domain': False,
349 'features': '',
350 'PV_bootloader': '',
351 'PV_kernel': '',
352 'PV_ramdisk': '',
353 'PV_args': '',
354 'PV_bootloader_args': '',
355 'HVM_boot_policy': '',
356 'HVM_boot_params': {},
357 'memory_static_min': 0,
358 'memory_dynamic_min': 0,
359 'shadow_memory': 0,
360 'memory_static_max': 0,
361 'memory_dynamic_max': 0,
362 'devices': {},
363 'on_xend_start': 'ignore',
364 'on_xend_stop': 'ignore',
365 'cpus': [],
366 'VCPUs_max': 1,
367 'VCPUs_live': 1,
368 'VCPUs_at_startup': 1,
369 'vcpus_params': {},
370 'console_refs': [],
371 'vif_refs': [],
372 'vbd_refs': [],
373 'vtpm_refs': [],
374 'other_config': {},
375 'platform': {},
376 'target': 0,
377 }
379 return defaults
381 #
382 # Here we assume these values exist in the dict.
383 # If they don't we have a bigger problem, lets not
384 # try and 'fix it up' but acutually fix the cause ;-)
385 #
386 def _memory_sanity_check(self):
387 log.trace("_memory_sanity_check memory_static_min: %s, "
388 "memory_static_max: %i, "
389 "memory_dynamic_min: %i, "
390 "memory_dynamic_max: %i",
391 self["memory_static_min"],
392 self["memory_static_max"],
393 self["memory_dynamic_min"],
394 self["memory_dynamic_max"])
396 if not self["memory_static_min"] <= self["memory_static_max"]:
397 raise XendConfigError("memory_static_min must be less " \
398 "than or equal to memory_static_max")
399 if not self["memory_static_min"] <= self["memory_dynamic_min"]:
400 raise XendConfigError("memory_static_min must be less " \
401 "than or equal to memory_dynamic_min")
402 if not self["memory_dynamic_max"] <= self["memory_static_max"]:
403 raise XendConfigError("memory_dynamic_max must be less " \
404 "than or equal to memory_static_max")
405 if not self["memory_dynamic_max"] > 0:
406 raise XendConfigError("memory_dynamic_max must be greater " \
407 "than zero")
408 if not self["memory_static_max"] > 0:
409 raise XendConfigError("memory_static_max must be greater " \
410 "than zero")
412 def _actions_sanity_check(self):
413 for event in ['shutdown', 'reboot', 'crash']:
414 if self['actions_after_' + event] not in CONFIG_RESTART_MODES:
415 raise XendConfigError('Invalid event handling mode: ' +
416 event)
418 def _vcpus_sanity_check(self):
419 if 'VCPUs_max' in self and 'vcpu_avail' not in self:
420 self['vcpu_avail'] = (1 << self['VCPUs_max']) - 1
422 def _uuid_sanity_check(self):
423 """Make sure UUID is in proper string format with hyphens."""
424 if 'uuid' not in self or not self['uuid']:
425 self['uuid'] = uuid.createString()
426 else:
427 self['uuid'] = uuid.toString(uuid.fromString(self['uuid']))
429 def _name_sanity_check(self):
430 if 'name_label' not in self:
431 self['name_label'] = 'Domain-' + self['uuid']
433 def _platform_sanity_check(self):
434 if 'keymap' not in self['platform'] and XendOptions.instance().get_keymap():
435 self['platform']['keymap'] = XendOptions.instance().get_keymap()
437 if self.is_hvm() or self.has_rfb():
438 if 'device_model' not in self['platform']:
439 self['platform']['device_model'] = xen.util.auxbin.pathTo("qemu-dm")
441 if self.is_hvm():
442 if 'timer_mode' not in self['platform']:
443 self['platform']['timer_mode'] = 0
444 if 'rtc_timeoffset' not in self['platform']:
445 self['platform']['rtc_timeoffset'] = 0
446 if 'hpet' not in self['platform']:
447 self['platform']['hpet'] = 0
448 if 'loader' not in self['platform']:
449 # Old configs may have hvmloader set as PV_kernel param
450 if self.has_key('PV_kernel') and re.search('hvmloader', self['PV_kernel']):
451 self['platform']['loader'] = self['PV_kernel']
452 self['PV_kernel'] = ''
453 else:
454 self['platform']['loader'] = "/usr/lib/xen/boot/hvmloader"
455 log.debug("Loader is %s" % str(self['platform']['loader']))
457 # Compatibility hack, can go away soon.
458 if 'soundhw' not in self['platform'] and \
459 self['platform'].get('enable_audio'):
460 self['platform']['soundhw'] = 'sb16'
462 def validate(self):
463 self._uuid_sanity_check()
464 self._name_sanity_check()
465 self._memory_sanity_check()
466 self._actions_sanity_check()
467 self._vcpus_sanity_check()
468 self._platform_sanity_check()
470 def _dominfo_to_xapi(self, dominfo, update_mem = False):
471 self['domid'] = dominfo['domid']
472 self['online_vcpus'] = dominfo['online_vcpus']
473 self['VCPUs_max'] = dominfo['max_vcpu_id'] + 1
475 if update_mem:
476 self['memory_dynamic_min'] = dominfo['mem_kb'] * 1024
477 self['memory_dynamic_max'] = dominfo['mem_kb'] * 1024
478 self['memory_static_min'] = 0
479 self['memory_static_max'] = dominfo['maxmem_kb'] * 1024
480 self._memory_sanity_check()
482 self['cpu_time'] = dominfo['cpu_time']/1e9
483 if dominfo.get('ssidref'):
484 ssidref = int(dominfo.get('ssidref'))
485 import xen.util.xsm.xsm as security
486 self['security_label'] = security.ssidref2security_label(ssidref)
488 self['shutdown_reason'] = dominfo['shutdown_reason']
490 # parse state into Xen API states
491 self['running'] = dominfo['running']
492 self['crashed'] = dominfo['crashed']
493 self['dying'] = dominfo['dying']
494 self['shutdown'] = dominfo['shutdown']
495 self['paused'] = dominfo['paused']
496 self['blocked'] = dominfo['blocked']
498 if 'name' in dominfo:
499 self['name_label'] = dominfo['name']
501 if 'handle' in dominfo:
502 self['uuid'] = uuid.toString(dominfo['handle'])
504 def parse_cpuid(self, cfg, field):
505 def int2bin(n, count=32):
506 return "".join([str((n >> y) & 1) for y in range(count-1, -1, -1)])
508 for input, regs in cfg[field].iteritems():
509 if not regs is dict:
510 cfg[field][input] = dict(regs)
512 cpuid = {}
513 for input in cfg[field]:
514 inputs = input.split(',')
515 if inputs[0][0:2] == '0x':
516 inputs[0] = str(int(inputs[0], 16))
517 if len(inputs) == 2:
518 if inputs[1][0:2] == '0x':
519 inputs[1] = str(int(inputs[1], 16))
520 new_input = ','.join(inputs)
521 cpuid[new_input] = {} # new input
522 for reg in cfg[field][input]:
523 val = cfg[field][input][reg]
524 if val[0:2] == '0x':
525 cpuid[new_input][reg] = int2bin(int(val, 16))
526 else:
527 cpuid[new_input][reg] = val
528 cfg[field] = cpuid
530 def _parse_sxp(self, sxp_cfg):
531 """ Populate this XendConfig using the parsed SXP.
533 @param sxp_cfg: Parsed SXP Configuration
534 @type sxp_cfg: list of lists
535 @rtype: dictionary
536 @return: A dictionary containing the parsed options of the SXP.
537 """
538 cfg = {}
540 for key, typ in XENAPI_CFG_TYPES.items():
541 val = sxp.child_value(sxp_cfg, key)
542 if val is not None:
543 try:
544 cfg[key] = typ(val)
545 except (ValueError, TypeError), e:
546 log.warn('Unable to convert type value for key: %s' % key)
548 # Convert deprecated options to current equivalents.
550 restart = sxp.child_value(sxp_cfg, 'restart')
551 if restart:
552 if restart == 'onreboot':
553 cfg['on_poweroff'] = 'destroy'
554 cfg['on_reboot'] = 'restart'
555 cfg['on_crash'] = 'destroy'
556 elif restart == 'always':
557 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
558 cfg[opt] = 'restart'
559 elif restart == 'never':
560 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
561 cfg[opt] = 'never'
562 else:
563 log.warn('Ignoring unrecognised value for deprecated option:'
564 'restart = \'%s\'', restart)
566 # Handle memory, passed in as MiB
568 if sxp.child_value(sxp_cfg, "memory") != None:
569 cfg["memory"] = int(sxp.child_value(sxp_cfg, "memory"))
570 if sxp.child_value(sxp_cfg, "maxmem") != None:
571 cfg["maxmem"] = int(sxp.child_value(sxp_cfg, "maxmem"))
573 # Convert scheduling parameters to vcpus_params
574 if 'vcpus_params' not in cfg:
575 cfg['vcpus_params'] = {}
576 cfg["vcpus_params"]["weight"] = \
577 int(sxp.child_value(sxp_cfg, "cpu_weight", 256))
578 cfg["vcpus_params"]["cap"] = \
579 int(sxp.child_value(sxp_cfg, "cpu_cap", 0))
581 # Only extract options we know about.
582 extract_keys = LEGACY_UNSUPPORTED_BY_XENAPI_CFG + \
583 XENAPI_CFG_TO_LEGACY_CFG.values()
585 for key in extract_keys:
586 val = sxp.child_value(sxp_cfg, key)
587 if val != None:
588 try:
589 cfg[key] = LEGACY_CFG_TYPES[key](val)
590 except KeyError:
591 cfg[key] = val
592 except (TypeError, ValueError), e:
593 log.warn("Unable to parse key %s: %s: %s" %
594 (key, str(val), e))
596 if 'platform' not in cfg:
597 cfg['platform'] = {}
598 localtime = sxp.child_value(sxp_cfg, 'localtime')
599 if localtime is not None:
600 cfg['platform']['localtime'] = localtime
602 # Compatibility hack -- can go soon.
603 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
604 val = sxp.child_value(sxp_cfg, "platform_" + key, None)
605 if val is not None:
606 self['platform'][key] = val
608 # Compatibility hack -- can go soon.
609 boot_order = sxp.child_value(sxp_cfg, 'HVM_boot')
610 if boot_order:
611 cfg['HVM_boot_policy'] = 'BIOS order'
612 cfg['HVM_boot_params'] = { 'order' : boot_order }
615 # Parsing the device SXP's.
616 cfg['devices'] = {}
617 for dev in sxp.children(sxp_cfg, 'device'):
618 config = sxp.child0(dev)
619 dev_type = sxp.name(config)
620 self.device_add(dev_type, cfg_sxp = config, target = cfg)
622 # Extract missing data from configuration entries
623 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
624 if image_sxp:
625 image_vcpus = sxp.child_value(image_sxp, 'vcpus')
626 if image_vcpus != None:
627 try:
628 if 'VCPUs_max' not in cfg:
629 cfg['VCPUs_max'] = int(image_vcpus)
630 elif cfg['VCPUs_max'] != int(image_vcpus):
631 cfg['VCPUs_max'] = int(image_vcpus)
632 log.warn('Overriding vcpus from %d to %d using image'
633 'vcpus value.', cfg['VCPUs_max'])
634 except ValueError, e:
635 raise XendConfigError('integer expeceted: %s: %s' %
636 image_sxp, e)
638 # Deprecated cpu configuration
639 if 'cpu' in cfg:
640 if 'cpus' in cfg:
641 cfg['cpus'] = "%s,%s" % (str(cfg['cpu']), cfg['cpus'])
642 else:
643 cfg['cpus'] = str(cfg['cpu'])
645 # Convert 'cpus' to list of list of ints
646 cpus_list = []
647 if 'cpus' in cfg:
648 # Convert the following string to list of ints.
649 # The string supports a list of ranges (0-3),
650 # seperated by commas, and negation (^1).
651 # Precedence is settled by order of the string:
652 # "0-3,^1" -> [0,2,3]
653 # "0-3,^1,1" -> [0,1,2,3]
654 def cnv(s):
655 l = []
656 for c in s.split(','):
657 if c.find('-') != -1:
658 (x, y) = c.split('-')
659 for i in range(int(x), int(y)+1):
660 l.append(int(i))
661 else:
662 # remove this element from the list
663 if c[0] == '^':
664 l = [x for x in l if x != int(c[1:])]
665 else:
666 l.append(int(c))
667 return l
669 if type(cfg['cpus']) == list:
670 if len(cfg['cpus']) > 0 and type(cfg['cpus'][0]) == list:
671 # If sxp_cfg was created from config.sxp,
672 # the form of 'cpus' is list of list of string.
673 # Convert 'cpus' to list of list of ints.
674 # Conversion examples:
675 # [['1']] -> [[1]]
676 # [['0','2'],['1','3']] -> [[0,2],[1,3]]
677 try:
678 for c1 in cfg['cpus']:
679 cpus = []
680 for c2 in c1:
681 cpus.append(int(c2))
682 cpus_list.append(cpus)
683 except ValueError, e:
684 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
685 else:
686 # Conversion examples:
687 # ["1"] -> [[1]]
688 # ["0,2","1,3"] -> [[0,2],[1,3]]
689 # ["0-3,^1","1-4,^2"] -> [[0,2,3],[1,3,4]]
690 try:
691 for c in cfg['cpus']:
692 cpus = cnv(c)
693 cpus_list.append(cpus)
694 except ValueError, e:
695 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
697 if len(cpus_list) != cfg['vcpus']:
698 raise XendConfigError('vcpus and the item number of cpus are not same')
699 else:
700 # Conversion examples:
701 # vcpus=1:
702 # "1" -> [[1]]
703 # "0-3,^1" -> [[0,2,3]]
704 # vcpus=2:
705 # "1" -> [[1],[1]]
706 # "0-3,^1" -> [[0,2,3],[0,2,3]]
707 try:
708 cpus = cnv(cfg['cpus'])
709 for v in range(0, cfg['vcpus']):
710 cpus_list.append(cpus)
711 except ValueError, e:
712 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
713 else:
714 # Generation examples:
715 # vcpus=1:
716 # -> [[]]
717 # vcpus=2:
718 # -> [[],[]]
719 for v in range(0, cfg['vcpus']):
720 cpus_list.append(list())
722 cfg['cpus'] = cpus_list
724 # Parse cpuid
725 if 'cpuid' in cfg:
726 self.parse_cpuid(cfg, 'cpuid')
727 if 'cpuid_check' in cfg:
728 self.parse_cpuid(cfg, 'cpuid_check')
730 import xen.util.xsm.xsm as security
731 if security.on() == xsconstants.XS_POLICY_ACM:
732 from xen.util.acmpolicy import ACM_LABEL_UNLABELED
733 if not 'security' in cfg and sxp.child_value(sxp_cfg, 'security'):
734 cfg['security'] = sxp.child_value(sxp_cfg, 'security')
735 elif not cfg.get('security_label'):
736 cfg['security'] = [['access_control',
737 ['policy', security.get_active_policy_name() ],
738 ['label', ACM_LABEL_UNLABELED ]]]
740 if 'security' in cfg and not cfg.get('security_label'):
741 secinfo = cfg['security']
742 # The xm command sends a list formatted like this:
743 # [['access_control', ['policy', 'xm-test'],['label', 'red']],
744 # ['ssidref', 196611]]
745 policy = ""
746 label = ""
747 for idx in range(0, len(secinfo)):
748 if secinfo[idx][0] == "access_control":
749 for aidx in range(1, len(secinfo[idx])):
750 if secinfo[idx][aidx][0] == "policy":
751 policy = secinfo[idx][aidx][1]
752 if secinfo[idx][aidx][0] == "label":
753 label = secinfo[idx][aidx][1]
754 cfg['security_label'] = \
755 security.set_security_label(policy, label)
756 if not sxp.child_value(sxp_cfg, 'security_label'):
757 del cfg['security']
759 sec_lab = cfg['security_label'].split(":")
760 if len(sec_lab) != 3:
761 raise XendConfigError("Badly formatted security label: %s"
762 % cfg['security_label'])
764 old_state = sxp.child_value(sxp_cfg, 'state')
765 if old_state:
766 for i in range(len(CONFIG_OLD_DOM_STATES)):
767 cfg[CONFIG_OLD_DOM_STATES[i]] = int(old_state[i] != '-')
769 return cfg
772 def _sxp_to_xapi(self, sxp_cfg):
773 """Read in an SXP Configuration object and
774 populate at much of the Xen API with valid values.
775 """
776 log.debug('_sxp_to_xapi(%s)' % scrub_password(sxp_cfg))
778 # _parse_sxp() below will call device_add() and construct devices.
779 # Some devices (currently only pci) may require VM's uuid, so
780 # setup self['uuid'] beforehand.
781 self['uuid'] = sxp.child_value(sxp_cfg, 'uuid', uuid.createString())
783 cfg = self._parse_sxp(sxp_cfg)
785 for key, typ in XENAPI_CFG_TYPES.items():
786 val = cfg.get(key)
787 if val is not None:
788 self[key] = typ(val)
790 # Convert parameters that can be directly mapped from
791 # the Legacy Config to Xen API Config
793 for apikey, cfgkey in XENAPI_CFG_TO_LEGACY_CFG.items():
794 try:
795 type_conv = XENAPI_CFG_TYPES.get(apikey)
796 if callable(type_conv):
797 self[apikey] = type_conv(cfg[cfgkey])
798 else:
799 log.warn("Unconverted key: " + apikey)
800 self[apikey] = cfg[cfgkey]
801 except KeyError:
802 pass
804 # Lets try and handle memory correctly
806 MiB = 1024 * 1024
808 if "memory" in cfg:
809 self["memory_static_min"] = 0
810 self["memory_static_max"] = int(cfg["memory"]) * MiB
811 self["memory_dynamic_min"] = int(cfg["memory"]) * MiB
812 self["memory_dynamic_max"] = int(cfg["memory"]) * MiB
814 if "maxmem" in cfg:
815 self["memory_static_max"] = int(cfg["maxmem"]) * MiB
817 self._memory_sanity_check()
819 def update_with(n, o):
820 if not self.get(n):
821 self[n] = cfg.get(o, '')
823 update_with('PV_bootloader', 'bootloader')
824 update_with('PV_bootloader_args', 'bootloader_args')
826 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
827 if image_sxp:
828 self.update_with_image_sxp(image_sxp)
830 # Convert Legacy HVM parameters to Xen API configuration
831 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
832 if key in cfg:
833 self['platform'][key] = cfg[key]
835 # set device references in the configuration
836 self['devices'] = cfg.get('devices', {})
837 self['console_refs'] = cfg.get('console_refs', [])
838 self['vif_refs'] = cfg.get('vif_refs', [])
839 self['vbd_refs'] = cfg.get('vbd_refs', [])
840 self['vtpm_refs'] = cfg.get('vtpm_refs', [])
842 # coalesce hvm vnc frame buffer with vfb config
843 if self.is_hvm() and int(self['platform'].get('vnc', 0)) != 0:
844 # add vfb device if it isn't there already
845 if not self.has_rfb():
846 dev_config = ['vfb']
847 dev_config.append(['type', 'vnc'])
848 # copy VNC related params from platform config to vfb dev conf
849 for key in ['vncpasswd', 'vncunused', 'vncdisplay',
850 'vnclisten']:
851 if key in self['platform']:
852 dev_config.append([key, self['platform'][key]])
854 self.device_add('vfb', cfg_sxp = dev_config)
857 def has_rfb(self):
858 for console_uuid in self['console_refs']:
859 if self['devices'][console_uuid][1].get('protocol') == 'rfb':
860 return True
861 if self['devices'][console_uuid][0] == 'vfb':
862 return True
863 return False
865 def _sxp_to_xapi_unsupported(self, sxp_cfg):
866 """Read in an SXP configuration object and populate
867 values are that not related directly supported in
868 the Xen API.
869 """
871 log.debug('_sxp_to_xapi_unsupported(%s)' % scrub_password(sxp_cfg))
873 # Parse and convert parameters used to configure
874 # the image (as well as HVM images)
875 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
876 if image_sxp:
877 image_type = sxp.name(image_sxp)
878 if image_type != 'hvm' and image_type != 'linux':
879 self['platform']['image_type'] = image_type
881 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
882 val = sxp.child_value(image_sxp, key, None)
883 if val is not None and val != '':
884 self['platform'][key] = val
886 notes = sxp.children(image_sxp, 'notes')
887 if notes:
888 self['notes'] = self.notes_from_sxp(notes[0])
890 self._hvm_boot_params_from_sxp(image_sxp)
892 # extract backend value
894 backend = []
895 for c in sxp.children(sxp_cfg, 'backend'):
896 backend.append(sxp.name(sxp.child0(c)))
897 if backend:
898 self['backend'] = backend
900 # Parse and convert other Non Xen API parameters.
901 def _set_cfg_if_exists(sxp_arg):
902 val = sxp.child_value(sxp_cfg, sxp_arg)
903 if val != None:
904 if LEGACY_CFG_TYPES.get(sxp_arg):
905 self[sxp_arg] = LEGACY_CFG_TYPES[sxp_arg](val)
906 else:
907 self[sxp_arg] = val
909 _set_cfg_if_exists('shadow_memory')
910 _set_cfg_if_exists('features')
911 _set_cfg_if_exists('on_xend_stop')
912 _set_cfg_if_exists('on_xend_start')
913 _set_cfg_if_exists('vcpu_avail')
915 # Parse and store runtime configuration
916 _set_cfg_if_exists('start_time')
917 _set_cfg_if_exists('cpu_time')
918 _set_cfg_if_exists('shutdown_reason')
919 _set_cfg_if_exists('up_time')
920 _set_cfg_if_exists('status') # TODO, deprecated
922 def _get_old_state_string(self):
923 """Returns the old xm state string.
924 @rtype: string
925 @return: old state string
926 """
927 state_string = ''
928 for state_name in CONFIG_OLD_DOM_STATES:
929 on_off = self.get(state_name, 0)
930 if on_off:
931 state_string += state_name[0]
932 else:
933 state_string += '-'
935 return state_string
938 def update_config(self, dominfo):
939 """Update configuration with the output from xc.domain_getinfo().
941 @param dominfo: Domain information via xc.domain_getinfo()
942 @type dominfo: dict
943 """
944 self._dominfo_to_xapi(dominfo)
945 self.validate()
947 def update_with_xenapi_config(self, xapi):
948 """Update configuration with a Xen API VM struct
950 @param xapi: Xen API VM Struct
951 @type xapi: dict
952 """
954 log.debug('update_with_xenapi_config: %s' % scrub_password(xapi))
956 for key, val in xapi.items():
957 type_conv = XENAPI_CFG_TYPES.get(key)
958 if type_conv is None:
959 key = key.lower()
960 type_conv = XENAPI_CFG_TYPES.get(key)
961 if callable(type_conv):
962 self[key] = type_conv(val)
963 else:
964 self[key] = val
966 # XenAPI defines platform as a string-string map. If platform
967 # configuration exists, convert values to appropriate type.
968 if 'platform' in xapi:
969 for key, val in xapi['platform'].items():
970 type_conv = XENAPI_PLATFORM_CFG_TYPES.get(key)
971 if type_conv is None:
972 key = key.lower()
973 type_conv = XENAPI_PLATFORM_CFG_TYPES.get(key)
974 if callable(type_conv):
975 self['platform'][key] = type_conv(val)
976 else:
977 self['platform'][key] = val
979 self['vcpus_params']['weight'] = \
980 int(self['vcpus_params'].get('weight', 256))
981 self['vcpus_params']['cap'] = int(self['vcpus_params'].get('cap', 0))
983 def cpuid_to_sxp(self, sxpr, field):
984 regs_list = []
985 for input, regs in self[field].iteritems():
986 reg_list = []
987 for reg, val in regs.iteritems():
988 reg_list.append([reg, val])
989 regs_list.append([input, reg_list])
990 sxpr.append([field, regs_list])
993 def to_sxp(self, domain = None, ignore_devices = False, ignore = [],
994 legacy_only = True):
995 """ Get SXP representation of this config object.
997 Incompat: removed store_mfn, console_mfn
999 @keyword domain: (optional) XendDomainInfo to get extra information
1000 from such as domid and running devices.
1001 @type domain: XendDomainInfo
1002 @keyword ignore: (optional) list of 'keys' that we do not want
1003 to export.
1004 @type ignore: list of strings
1005 @rtype: list of list (SXP representation)
1006 """
1007 sxpr = ['domain']
1009 # TODO: domid/dom is the same thing but called differently
1010 # depending if it is from xenstore or sxpr.
1012 if domain.getDomid() is not None:
1013 sxpr.append(['domid', domain.getDomid()])
1015 if not legacy_only:
1016 for name, typ in XENAPI_CFG_TYPES.items():
1017 if name in self and self[name] not in (None, []):
1018 if typ == dict:
1019 s = self[name].items()
1020 elif typ == list:
1021 s = self[name]
1022 else:
1023 s = str(self[name])
1024 sxpr.append([name, s])
1026 for xenapi, legacy in XENAPI_CFG_TO_LEGACY_CFG.items():
1027 if legacy in ('cpus'): # skip this
1028 continue
1029 if self.has_key(xenapi) and self[xenapi] not in (None, []):
1030 if type(self[xenapi]) == bool:
1031 # convert booleans to ints before making an sxp item
1032 sxpr.append([legacy, int(self[xenapi])])
1033 else:
1034 sxpr.append([legacy, self[xenapi]])
1036 MiB = 1024*1024
1038 sxpr.append(["maxmem", int(self["memory_static_max"])/MiB])
1039 sxpr.append(["memory", int(self["memory_dynamic_max"])/MiB])
1041 for legacy in LEGACY_UNSUPPORTED_BY_XENAPI_CFG:
1042 if legacy in ('domid', 'uuid', 'cpus'): # skip these
1043 continue
1044 if self.has_key(legacy) and self[legacy] not in (None, []):
1045 sxpr.append([legacy, self[legacy]])
1047 if self.has_key('security_label'):
1048 sxpr.append(['security_label', self['security_label']])
1050 sxpr.append(['image', self.image_sxpr()])
1051 sxpr.append(['status', domain._stateGet()])
1053 if domain.getDomid() is not None:
1054 sxpr.append(['state', self._get_old_state_string()])
1056 if domain:
1057 if domain.store_mfn:
1058 sxpr.append(['store_mfn', domain.store_mfn])
1059 if domain.console_mfn:
1060 sxpr.append(['console_mfn', domain.console_mfn])
1063 # Marshall devices (running or from configuration)
1064 if not ignore_devices:
1065 txn = xstransact()
1066 try:
1067 for cls in XendDevices.valid_devices():
1068 found = False
1070 # figure if there is a dev controller is valid and running
1071 if domain and domain.getDomid() != None:
1072 try:
1073 controller = domain.getDeviceController(cls)
1074 configs = controller.configurations(txn)
1075 for config in configs:
1076 if sxp.name(config) in ('vbd', 'tap'):
1077 # The bootable flag is never written to the
1078 # store as part of the device config.
1079 dev_uuid = sxp.child_value(config, 'uuid')
1080 dev_type, dev_cfg = self['devices'][dev_uuid]
1081 is_bootable = dev_cfg.get('bootable', 0)
1082 config.append(['bootable', int(is_bootable)])
1083 config.append(['VDI', dev_cfg.get('VDI', '')])
1085 sxpr.append(['device', config])
1087 found = True
1088 except:
1089 log.exception("dumping sxp from device controllers")
1090 pass
1092 # if we didn't find that device, check the existing config
1093 # for a device in the same class
1094 if not found:
1095 for dev_type, dev_info in self.all_devices_sxpr():
1096 if dev_type == cls:
1097 sxpr.append(['device', dev_info])
1099 txn.commit()
1100 except:
1101 txn.abort()
1102 raise
1104 if 'cpuid' in self:
1105 self.cpuid_to_sxp(sxpr, 'cpuid')
1106 if 'cpuid_check' in self:
1107 self.cpuid_to_sxp(sxpr, 'cpuid_check')
1109 log.debug(sxpr)
1111 return sxpr
1113 def _blkdev_name_to_number(self, dev):
1114 if 'ioemu:' in dev:
1115 _, dev = dev.split(':', 1)
1116 try:
1117 dev, _ = dev.split(':', 1)
1118 except ValueError:
1119 pass
1121 try:
1122 devid = int(dev)
1123 except ValueError:
1124 # devid is not a number but a string containing either device
1125 # name (e.g. xvda) or device_type/device_id (e.g. vbd/51728)
1126 dev2 = type(dev) is str and dev.split('/')[-1] or None
1127 if dev2 == None:
1128 log.debug("Could not check the device %s", dev)
1129 return None
1130 try:
1131 devid = int(dev2)
1132 except ValueError:
1133 (xenbus, devid) = blkdev_name_to_number(dev2)
1134 if devid == None:
1135 log.debug("The device %s is not device name", dev2)
1136 return None
1137 return devid
1139 def device_duplicate_check(self, dev_type, dev_info, defined_config):
1140 defined_devices_sxpr = self.all_devices_sxpr(target = defined_config)
1142 if dev_type == 'vbd' or dev_type == 'tap':
1143 dev_uname = dev_info.get('uname')
1144 blkdev_name = dev_info.get('dev')
1145 devid = self._blkdev_name_to_number(blkdev_name)
1146 if devid == None or dev_uname == None:
1147 return
1149 for o_dev_type, o_dev_info in defined_devices_sxpr:
1150 if o_dev_type == 'vbd' or o_dev_type == 'tap':
1151 blkdev_file = blkdev_uname_to_file(dev_uname)
1152 o_dev_uname = sxp.child_value(o_dev_info, 'uname')
1153 if o_dev_uname != None:
1154 o_blkdev_file = blkdev_uname_to_file(o_dev_uname)
1155 if blkdev_file == o_blkdev_file:
1156 raise XendConfigError('The file "%s" is already used' %
1157 blkdev_file)
1158 o_blkdev_name = sxp.child_value(o_dev_info, 'dev')
1159 o_devid = self._blkdev_name_to_number(o_blkdev_name)
1160 if o_devid != None and devid == o_devid:
1161 raise XendConfigError('The device "%s" is already defined' %
1162 blkdev_name)
1164 elif dev_type == 'vif':
1165 dev_mac = dev_info.get('mac')
1167 for o_dev_type, o_dev_info in defined_devices_sxpr:
1168 if dev_type == o_dev_type:
1169 if dev_mac.lower() == sxp.child_value(o_dev_info, 'mac').lower():
1170 raise XendConfigError('The mac "%s" is already defined' %
1171 dev_mac)
1173 def device_add(self, dev_type, cfg_sxp = None, cfg_xenapi = None,
1174 target = None):
1175 """Add a device configuration in SXP format or XenAPI struct format.
1177 For SXP, it could be either:
1179 [device, [vbd, [uname ...]]
1181 or:
1183 [vbd, [uname ..]]
1185 @type cfg_sxp: list of lists (parsed sxp object)
1186 @param cfg_sxp: SXP configuration object
1187 @type cfg_xenapi: dict
1188 @param cfg_xenapi: A device configuration from Xen API (eg. vbd,vif)
1189 @param target: write device information to
1190 @type target: None or a dictionary
1191 @rtype: string
1192 @return: Assigned UUID of the device.
1193 """
1194 if target == None:
1195 target = self
1197 if dev_type not in XendDevices.valid_devices():
1198 raise XendConfigError("XendConfig: %s not a valid device type" %
1199 dev_type)
1201 if cfg_sxp == None and cfg_xenapi == None:
1202 raise XendConfigError("XendConfig: device_add requires some "
1203 "config.")
1205 #if cfg_sxp:
1206 # log.debug("XendConfig.device_add: %s" % str(cfg_sxp))
1207 #if cfg_xenapi:
1208 # log.debug("XendConfig.device_add: %s" % str(cfg_xenapi))
1210 if cfg_sxp:
1211 if sxp.child0(cfg_sxp) == 'device':
1212 config = sxp.child0(cfg_sxp)
1213 else:
1214 config = cfg_sxp
1216 dev_type = sxp.name(config)
1217 dev_info = {}
1219 if dev_type == 'pci' or dev_type == 'vscsi':
1220 pci_devs_uuid = sxp.child_value(config, 'uuid',
1221 uuid.createString())
1223 pci_dict = self.pci_convert_sxp_to_dict(config)
1224 pci_devs = pci_dict['devs']
1226 if dev_type != 'vscsi':
1227 # create XenAPI DPCI objects.
1228 for pci_dev in pci_devs:
1229 dpci_uuid = pci_dev.get('uuid')
1230 ppci_uuid = XendPPCI.get_by_sbdf(pci_dev['domain'],
1231 pci_dev['bus'],
1232 pci_dev['slot'],
1233 pci_dev['func'])
1234 if ppci_uuid is None:
1235 continue
1236 dpci_record = {
1237 'VM': self['uuid'],
1238 'PPCI': ppci_uuid,
1239 'hotplug_slot': pci_dev.get('vslot', 0)
1241 XendDPCI(dpci_uuid, dpci_record)
1243 target['devices'][pci_devs_uuid] = (dev_type,
1244 {'devs': pci_devs,
1245 'uuid': pci_devs_uuid})
1247 log.debug("XendConfig: reading device: %s" % pci_devs)
1249 return pci_devs_uuid
1251 for opt_val in config[1:]:
1252 try:
1253 opt, val = opt_val
1254 dev_info[opt] = val
1255 except (TypeError, ValueError): # unpack error
1256 pass
1258 if dev_type == 'vbd':
1259 dev_info['bootable'] = 0
1260 if dev_info.get('dev', '').startswith('ioemu:'):
1261 dev_info['driver'] = 'ioemu'
1262 else:
1263 dev_info['driver'] = 'paravirtualised'
1265 if dev_type == 'tap':
1266 if dev_info['uname'].split(':')[1] not in blktap_disk_types:
1267 raise XendConfigError("tap:%s not a valid disk type" %
1268 dev_info['uname'].split(':')[1])
1270 if dev_type == 'vif':
1271 if not dev_info.get('mac'):
1272 dev_info['mac'] = randomMAC()
1274 self.device_duplicate_check(dev_type, dev_info, target)
1276 if dev_type == 'vif':
1277 if dev_info.get('policy') and dev_info.get('label'):
1278 dev_info['security_label'] = "%s:%s:%s" % \
1279 (xsconstants.ACM_POLICY_ID,
1280 dev_info['policy'],dev_info['label'])
1282 # create uuid if it doesn't exist
1283 dev_uuid = dev_info.get('uuid', None)
1284 if not dev_uuid:
1285 dev_uuid = uuid.createString()
1286 dev_info['uuid'] = dev_uuid
1288 # store dev references by uuid for certain device types
1289 target['devices'][dev_uuid] = (dev_type, dev_info)
1290 if dev_type in ('vif', 'vbd', 'vtpm'):
1291 param = '%s_refs' % dev_type
1292 if param not in target:
1293 target[param] = []
1294 if dev_uuid not in target[param]:
1295 if dev_type == 'vbd':
1296 # Compat hack -- mark first disk bootable
1297 dev_info['bootable'] = int(not target[param])
1298 target[param].append(dev_uuid)
1299 elif dev_type == 'tap':
1300 if 'vbd_refs' not in target:
1301 target['vbd_refs'] = []
1302 if dev_uuid not in target['vbd_refs']:
1303 # Compat hack -- mark first disk bootable
1304 dev_info['bootable'] = int(not target['vbd_refs'])
1305 target['vbd_refs'].append(dev_uuid)
1307 elif dev_type == 'vfb':
1308 # Populate other config with aux data that is associated
1309 # with vfb
1311 other_config = {}
1312 for key in XENAPI_CONSOLE_OTHER_CFG:
1313 if key in dev_info:
1314 other_config[key] = dev_info[key]
1315 target['devices'][dev_uuid][1]['other_config'] = other_config
1318 if 'console_refs' not in target:
1319 target['console_refs'] = []
1321 # Treat VFB devices as console devices so they are found
1322 # through Xen API
1323 if dev_uuid not in target['console_refs']:
1324 target['console_refs'].append(dev_uuid)
1326 elif dev_type == 'console':
1327 if 'console_refs' not in target:
1328 target['console_refs'] = []
1329 if dev_uuid not in target['console_refs']:
1330 target['console_refs'].append(dev_uuid)
1332 log.debug("XendConfig: reading device: %s" % scrub_password(dev_info))
1333 return dev_uuid
1335 if cfg_xenapi:
1336 dev_info = {}
1337 dev_uuid = ''
1338 if dev_type == 'vif':
1339 dev_info['mac'] = cfg_xenapi.get('MAC')
1340 if not dev_info['mac']:
1341 dev_info['mac'] = randomMAC()
1342 # vifname is the name on the guest, not dom0
1343 # TODO: we don't have the ability to find that out or
1344 # change it from dom0
1345 #if cfg_xenapi.get('device'): # don't add if blank
1346 # dev_info['vifname'] = cfg_xenapi.get('device')
1347 if cfg_xenapi.get('type'):
1348 dev_info['type'] = cfg_xenapi.get('type')
1349 if cfg_xenapi.get('name'):
1350 dev_info['name'] = cfg_xenapi.get('name')
1351 if cfg_xenapi.get('network'):
1352 network = XendAPIStore.get(
1353 cfg_xenapi.get('network'), 'network')
1354 dev_info['bridge'] = network.get_name_label()
1356 if cfg_xenapi.get('security_label'):
1357 dev_info['security_label'] = \
1358 cfg_xenapi.get('security_label')
1360 dev_uuid = cfg_xenapi.get('uuid', None)
1361 if not dev_uuid:
1362 dev_uuid = uuid.createString()
1363 dev_info['uuid'] = dev_uuid
1364 target['devices'][dev_uuid] = (dev_type, dev_info)
1365 target['vif_refs'].append(dev_uuid)
1367 elif dev_type in ('vbd', 'tap'):
1368 dev_info['type'] = cfg_xenapi.get('type', 'Disk')
1369 if dev_info['type'] == 'CD':
1370 old_vbd_type = 'cdrom'
1371 else:
1372 old_vbd_type = 'disk'
1374 dev_info['uname'] = cfg_xenapi.get('image', '')
1375 dev_info['dev'] = '%s:%s' % (cfg_xenapi.get('device'),
1376 old_vbd_type)
1377 dev_info['bootable'] = int(cfg_xenapi.get('bootable', 0))
1378 dev_info['driver'] = cfg_xenapi.get('driver', '')
1379 dev_info['VDI'] = cfg_xenapi.get('VDI', '')
1381 if cfg_xenapi.get('mode') == 'RW':
1382 dev_info['mode'] = 'w'
1383 else:
1384 dev_info['mode'] = 'r'
1386 dev_uuid = cfg_xenapi.get('uuid', None)
1387 if not dev_uuid:
1388 dev_uuid = uuid.createString()
1389 dev_info['uuid'] = dev_uuid
1390 target['devices'][dev_uuid] = (dev_type, dev_info)
1391 target['vbd_refs'].append(dev_uuid)
1393 elif dev_type == 'vtpm':
1394 if cfg_xenapi.get('type'):
1395 dev_info['type'] = cfg_xenapi.get('type')
1397 dev_uuid = cfg_xenapi.get('uuid', None)
1398 if not dev_uuid:
1399 dev_uuid = uuid.createString()
1400 dev_info['uuid'] = dev_uuid
1401 dev_info['other_config'] = cfg_xenapi.get('other_config', {})
1402 target['devices'][dev_uuid] = (dev_type, dev_info)
1403 target['vtpm_refs'].append(dev_uuid)
1405 elif dev_type == 'console':
1406 dev_uuid = cfg_xenapi.get('uuid', None)
1407 if not dev_uuid:
1408 dev_uuid = uuid.createString()
1409 dev_info['uuid'] = dev_uuid
1410 dev_info['protocol'] = cfg_xenapi.get('protocol', 'rfb')
1411 console_other_config = cfg_xenapi.get('other_config', {})
1412 dev_info['other_config'] = console_other_config
1413 if dev_info['protocol'] == 'rfb':
1414 # collapse other config into devinfo for things
1415 # such as vncpasswd, vncunused, etc.
1416 dev_info.update(console_other_config)
1417 dev_info['type'] = console_other_config.get('type', 'vnc')
1418 target['devices'][dev_uuid] = ('vfb', dev_info)
1419 target['console_refs'].append(dev_uuid)
1421 # if console is rfb, set device_model ensuring qemu
1422 # is invoked for pvfb services
1423 if 'device_model' not in target['platform']:
1424 target['platform']['device_model'] = \
1425 xen.util.auxbin.pathTo("qemu-dm")
1427 # Finally, if we are a pvfb, we need to make a vkbd
1428 # as well that is not really exposed to Xen API
1429 vkbd_uuid = uuid.createString()
1430 target['devices'][vkbd_uuid] = ('vkbd', {})
1432 elif dev_info['protocol'] == 'vt100':
1433 # if someone tries to create a VT100 console
1434 # via the Xen API, we'll have to ignore it
1435 # because we create one automatically in
1436 # XendDomainInfo._update_consoles
1437 raise XendConfigError('Creating vt100 consoles via '
1438 'Xen API is unsupported')
1440 return dev_uuid
1442 # no valid device to add
1443 return ''
1445 def phantom_device_add(self, dev_type, cfg_xenapi = None,
1446 target = None):
1447 """Add a phantom tap device configuration in XenAPI struct format.
1448 """
1450 if target == None:
1451 target = self
1453 if dev_type not in XendDevices.valid_devices() and \
1454 dev_type not in XendDevices.pseudo_devices():
1455 raise XendConfigError("XendConfig: %s not a valid device type" %
1456 dev_type)
1458 if cfg_xenapi == None:
1459 raise XendConfigError("XendConfig: device_add requires some "
1460 "config.")
1462 if cfg_xenapi:
1463 log.debug("XendConfig.phantom_device_add: %s" % str(cfg_xenapi))
1465 if cfg_xenapi:
1466 dev_info = {}
1467 if dev_type in ('vbd', 'tap'):
1468 if dev_type == 'vbd':
1469 dev_info['uname'] = cfg_xenapi.get('image', '')
1470 dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
1471 elif dev_type == 'tap':
1472 if cfg_xenapi.get('image').find('tap:') == -1:
1473 dev_info['uname'] = 'tap:qcow:%s' % cfg_xenapi.get('image')
1474 dev_info['dev'] = '/dev/%s' % cfg_xenapi.get('device')
1475 dev_info['uname'] = cfg_xenapi.get('image')
1476 dev_info['mode'] = cfg_xenapi.get('mode')
1477 dev_info['backend'] = '0'
1478 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
1479 dev_info['uuid'] = dev_uuid
1480 self['devices'][dev_uuid] = (dev_type, dev_info)
1481 self['vbd_refs'].append(dev_uuid)
1482 return dev_uuid
1484 return ''
1486 def pci_convert_sxp_to_dict(self, dev_sxp):
1487 """Convert pci device sxp to dict
1488 @param dev_sxp: device configuration
1489 @type dev_sxp: SXP object (parsed config)
1490 @return: dev_config
1491 @rtype: dictionary
1492 """
1493 # Parsing the device SXP's. In most cases, the SXP looks
1494 # like this:
1496 # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]]
1498 # However, for PCI devices it looks like this:
1500 # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1], [func, 2]]]
1502 # It seems the reasoning for this difference is because
1503 # pciif.py needs all the PCI device configurations at
1504 # the same time when creating the devices.
1506 # To further complicate matters, Xen 2.0 configuration format
1507 # uses the following for pci device configuration:
1509 # [device, [pci, [domain, 0], [bus, 0], [dev, 1], [func, 2]]]
1511 # For PCI device hotplug support, the SXP of PCI devices is
1512 # extendend like this:
1514 # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1], [func, 2],
1515 # [vslt, 0]],
1516 # [state, 'Initialising']]]
1518 # 'vslt' shows the virtual hotplug slot number which the PCI device
1519 # is inserted in. This is only effective for HVM domains.
1521 # state 'Initialising' indicates that the device is being attached,
1522 # while state 'Closing' indicates that the device is being detached.
1524 # The Dict looks like this:
1526 # { devs: [{domain: 0, bus: 0, slot: 1, func: 2, vslt: 0}],
1527 # states: ['Initialising'] }
1529 dev_config = {}
1531 pci_devs = []
1532 for pci_dev in sxp.children(dev_sxp, 'dev'):
1533 pci_dev_info = {}
1534 for opt_val in pci_dev[1:]:
1535 try:
1536 opt, val = opt_val
1537 pci_dev_info[opt] = val
1538 except TypeError:
1539 pass
1540 # append uuid for each pci device.
1541 dpci_uuid = pci_dev_info.get('uuid', uuid.createString())
1542 pci_dev_info['uuid'] = dpci_uuid
1543 pci_devs.append(pci_dev_info)
1544 dev_config['devs'] = pci_devs
1546 pci_states = []
1547 for pci_state in sxp.children(dev_sxp, 'state'):
1548 try:
1549 pci_states.append(pci_state[1])
1550 except IndexError:
1551 raise XendError("Error reading state while parsing pci sxp")
1552 dev_config['states'] = pci_states
1554 return dev_config
1556 def console_add(self, protocol, location, other_config = {}):
1557 dev_uuid = uuid.createString()
1558 if protocol == 'vt100':
1559 dev_info = {
1560 'uuid': dev_uuid,
1561 'protocol': protocol,
1562 'location': location,
1563 'other_config': other_config,
1566 if 'devices' not in self:
1567 self['devices'] = {}
1569 self['devices'][dev_uuid] = ('console', dev_info)
1570 self['console_refs'].append(dev_uuid)
1571 return dev_info
1573 return {}
1575 def console_update(self, console_uuid, key, value):
1576 for dev_uuid, (dev_type, dev_info) in self['devices'].items():
1577 if dev_uuid == console_uuid:
1578 dev_info[key] = value
1579 # collapse other_config into dev_info for things
1580 # such as vncpasswd, vncunused, etc.
1581 if key == 'other_config':
1582 for k in XENAPI_CONSOLE_OTHER_CFG:
1583 if k in dev_info and k not in value:
1584 del dev_info[k]
1585 dev_info.update(value)
1586 break
1588 def console_get_all(self, protocol):
1589 if protocol == 'vt100':
1590 consoles = [dinfo for dtype, dinfo in self['devices'].values()
1591 if dtype == 'console']
1592 return [c for c in consoles if c.get('protocol') == protocol]
1594 elif protocol == 'rfb':
1595 vfbs = [dinfo for dtype, dinfo in self['devices'].values()
1596 if dtype == 'vfb']
1598 # move all non-console key values to other_config before
1599 # returning console config
1600 valid_keys = ['uuid', 'location']
1601 for vfb in vfbs:
1602 other_config = {}
1603 for key, val in vfb.items():
1604 if key not in valid_keys:
1605 other_config[key] = vfb[key]
1606 del vfb[key]
1607 vfb['other_config'] = other_config
1608 vfb['protocol'] = 'rfb'
1610 return vfbs
1612 else:
1613 return []
1615 def device_update(self, dev_uuid, cfg_sxp = [], cfg_xenapi = {}):
1616 """Update an existing device with the new configuration.
1618 @rtype: boolean
1619 @return: Returns True if succesfully found and updated a device conf
1620 """
1621 if dev_uuid in self['devices'] and cfg_sxp:
1622 if sxp.child0(cfg_sxp) == 'device':
1623 config = sxp.child0(cfg_sxp)
1624 else:
1625 config = cfg_sxp
1627 dev_type, dev_info = self['devices'][dev_uuid]
1629 if dev_type == 'pci' or dev_type == 'vscsi': # Special case for pci
1630 pci_dict = self.pci_convert_sxp_to_dict(config)
1631 pci_devs = pci_dict['devs']
1633 # destroy existing XenAPI DPCI objects
1634 for dpci_uuid in XendDPCI.get_by_VM(self['uuid']):
1635 XendAPIStore.deregister(dpci_uuid, "DPCI")
1637 if dev_type != 'vscsi':
1638 # create XenAPI DPCI objects.
1639 for pci_dev in pci_devs:
1640 dpci_uuid = pci_dev.get('uuid')
1641 ppci_uuid = XendPPCI.get_by_sbdf(pci_dev['domain'],
1642 pci_dev['bus'],
1643 pci_dev['slot'],
1644 pci_dev['func'])
1645 if ppci_uuid is None:
1646 continue
1647 dpci_record = {
1648 'VM': self['uuid'],
1649 'PPCI': ppci_uuid,
1650 'hotplug_slot': pci_dev.get('vslot', 0)
1652 XendDPCI(dpci_uuid, dpci_record)
1654 self['devices'][dev_uuid] = (dev_type,
1655 {'devs': pci_devs,
1656 'uuid': dev_uuid})
1657 return True
1659 for opt_val in config[1:]:
1660 try:
1661 opt, val = opt_val
1662 dev_info[opt] = val
1663 except (TypeError, ValueError):
1664 pass # no value for this config option
1666 self['devices'][dev_uuid] = (dev_type, dev_info)
1667 return True
1669 elif dev_uuid in self['devices'] and cfg_xenapi:
1670 dev_type, dev_info = self['devices'][dev_uuid]
1671 for key, val in cfg_xenapi.items():
1672 dev_info[key] = val
1673 self['devices'][dev_uuid] = (dev_type, dev_info)
1675 return False
1678 def device_sxpr(self, dev_uuid = None, dev_type = None, dev_info = None, target = None):
1679 """Get Device SXPR by either giving the device UUID or (type, config).
1681 @rtype: list of lists
1682 @return: device config sxpr
1683 """
1684 sxpr = []
1686 if target == None:
1687 target = self
1689 if dev_uuid != None and dev_uuid in target['devices']:
1690 dev_type, dev_info = target['devices'][dev_uuid]
1692 if dev_type == None or dev_info == None:
1693 raise XendConfigError("Required either UUID or device type and "
1694 "configuration dictionary.")
1696 sxpr.append(dev_type)
1697 if dev_type in ('console', 'vfb'):
1698 config = [(opt, val) for opt, val in dev_info.items()
1699 if opt != 'other_config']
1700 else:
1701 config = [(opt, val) for opt, val in dev_info.items()]
1703 sxpr += config
1705 return sxpr
1707 def ordered_device_refs(self, target = None):
1708 result = []
1710 if target == None:
1711 target = self
1713 # vkbd devices *must* be before vfb devices, otherwise
1714 # there is a race condition when setting up devices
1715 # where the daemon spawned for the vfb may write stuff
1716 # into xenstore vkbd backend, before DevController has
1717 # setup permissions on the vkbd backend path. This race
1718 # results in domain creation failing with 'device already
1719 # connected' messages
1720 result.extend([u for u in target['devices'].keys() if target['devices'][u][0] == 'vkbd'])
1722 result.extend(target.get('console_refs', []) +
1723 target.get('vbd_refs', []) +
1724 target.get('vif_refs', []) +
1725 target.get('vtpm_refs', []))
1727 result.extend([u for u in target['devices'].keys() if u not in result])
1728 return result
1730 def all_devices_sxpr(self, target = None):
1731 """Returns the SXPR for all devices in the current configuration."""
1732 sxprs = []
1733 pci_devs = []
1735 if target == None:
1736 target = self
1738 if 'devices' not in target:
1739 return sxprs
1741 ordered_refs = self.ordered_device_refs(target = target)
1742 for dev_uuid in ordered_refs:
1743 dev_type, dev_info = target['devices'][dev_uuid]
1744 if dev_type == 'pci' or dev_type == 'vscsi': # special case for pci devices
1745 if dev_type == 'pci':
1746 sxpr = ['pci', ['uuid', dev_info['uuid']]]
1747 elif dev_type == 'vscsi':
1748 sxpr = ['vscsi', ['uuid', dev_info['uuid']]]
1749 for pci_dev_info in dev_info['devs']:
1750 pci_dev_sxpr = ['dev']
1751 for opt, val in pci_dev_info.items():
1752 pci_dev_sxpr.append([opt, val])
1753 sxpr.append(pci_dev_sxpr)
1754 sxprs.append((dev_type, sxpr))
1755 else:
1756 sxpr = self.device_sxpr(dev_type = dev_type,
1757 dev_info = dev_info,
1758 target = target)
1759 sxprs.append((dev_type, sxpr))
1761 return sxprs
1763 def image_sxpr(self):
1764 """Returns a backwards compatible image SXP expression that is
1765 used in xenstore's /vm/<uuid>/image value and xm list."""
1766 image = [self.image_type()]
1767 if self.has_key('PV_kernel'):
1768 image.append(['kernel', self['PV_kernel']])
1769 if self.has_key('PV_ramdisk') and self['PV_ramdisk']:
1770 image.append(['ramdisk', self['PV_ramdisk']])
1771 if self.has_key('PV_args') and self['PV_args']:
1772 image.append(['args', self['PV_args']])
1774 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
1775 if key in self['platform']:
1776 image.append([key, self['platform'][key]])
1778 if 'notes' in self:
1779 image.append(self.notes_sxp(self['notes']))
1781 return image
1783 def update_with_image_sxp(self, image_sxp, bootloader = False):
1784 # Convert Legacy "image" config to Xen API PV_*
1785 # configuration
1786 log.debug("update_with_image_sxp(%s)" % scrub_password(image_sxp))
1788 # user-specified args must come last: previous releases did this and
1789 # some domU kernels rely upon the ordering.
1790 kernel_args = sxp.child_value(image_sxp, 'args', '')
1792 # attempt to extract extra arguments from SXP config
1793 arg_ip = sxp.child_value(image_sxp, 'ip')
1794 if arg_ip and not re.search(r'ip=[^ ]+', kernel_args):
1795 kernel_args = 'ip=%s ' % arg_ip + kernel_args
1796 arg_root = sxp.child_value(image_sxp, 'root')
1797 if arg_root and not re.search(r'root=', kernel_args):
1798 kernel_args = 'root=%s ' % arg_root + kernel_args
1800 if bootloader:
1801 self['_temp_using_bootloader'] = '1'
1802 self['_temp_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1803 self['_temp_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1804 self['_temp_args'] = kernel_args
1805 else:
1806 self['PV_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1807 self['PV_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1808 self['PV_args'] = kernel_args
1810 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
1811 val = sxp.child_value(image_sxp, key, None)
1812 if val is not None and val != '':
1813 self['platform'][key] = val
1815 notes = sxp.children(image_sxp, 'notes')
1816 if notes:
1817 self['notes'] = self.notes_from_sxp(notes[0])
1819 self._hvm_boot_params_from_sxp(image_sxp)
1821 def set_notes(self, notes):
1822 'Add parsed elfnotes to image'
1823 self['notes'] = notes
1825 def get_notes(self):
1826 try:
1827 return self['notes'] or {}
1828 except KeyError:
1829 return {}
1831 def notes_from_sxp(self, nsxp):
1832 notes = {}
1833 for note in sxp.children(nsxp):
1834 notes[note[0]] = note[1]
1835 return notes
1837 def notes_sxp(self, notes):
1838 nsxp = ['notes']
1839 for k, v in notes.iteritems():
1840 nsxp.append([k, str(v)])
1841 return nsxp
1843 def _hvm_boot_params_from_sxp(self, image_sxp):
1844 boot = sxp.child_value(image_sxp, 'boot', None)
1845 if boot is not None:
1846 self['HVM_boot_policy'] = 'BIOS order'
1847 self['HVM_boot_params'] = { 'order' : boot }
1849 def is_hvm(self):
1850 return self['HVM_boot_policy'] != ''
1852 def target(self):
1853 return self['target']
1855 def image_type(self):
1856 stored_type = self['platform'].get('image_type')
1857 return stored_type or (self.is_hvm() and 'hvm' or 'linux')
1859 def is_hap(self):
1860 return self['platform'].get('hap', 0)