ia64/xen-unstable

view tools/python/xen/xend/XendConfig.py @ 18307:ce085fc0d2e4

xend: Better support for legacy HVM config of ia64

On ia64, direct Linux boot is not supported, and 'Flash.fd' or
'guest_firmware.bin' has been used as a value of the 'kernel'
option. Cset:17016 ignores those strings and overrides them with
"/usr/lib/xen/boot/hvmloader".

Signed-off-by: Kazuhiro Suzuki <kaz@jp.fujitsu.com>
Signed-off-by: KUWAMURA Shin'ya <kuwa@jp.fujitsu.com>
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Mon Aug 11 11:27:40 2008 +0100 (2008-08-11)
parents f40c310dca31
children 60af813ea458
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 'machine_address_size': int,
211 }
213 # List of legacy configuration keys that have no equivalent in the
214 # Xen API, but are still stored in XendConfig.
216 LEGACY_UNSUPPORTED_BY_XENAPI_CFG = [
217 # roundtripped (dynamic, unmodified)
218 'shadow_memory',
219 'vcpu_avail',
220 'features',
221 # read/write
222 'on_xend_start',
223 'on_xend_stop',
224 # read-only
225 'domid',
226 'start_time',
227 'cpu_time',
228 'online_vcpus',
229 # write-once
230 'cpu',
231 'cpus',
232 ]
234 LEGACY_CFG_TYPES = {
235 'uuid': str,
236 'name': str,
237 'vcpus': int,
238 'vcpu_avail': long,
239 'memory': int,
240 'shadow_memory': int,
241 'maxmem': int,
242 'start_time': float,
243 'cpu_time': float,
244 'features': str,
245 'localtime': int,
246 'name': str,
247 'on_poweroff': str,
248 'on_reboot': str,
249 'on_crash': str,
250 'on_xend_stop': str,
251 'on_xend_start': str,
252 'online_vcpus': int,
253 'rtc/timeoffset': str,
254 }
256 # Values that should be stored in xenstore's /vm/<uuid> that is used
257 # by Xend. Used in XendDomainInfo to restore running VM state from
258 # xenstore.
259 LEGACY_XENSTORE_VM_PARAMS = [
260 'uuid',
261 'name',
262 'vcpus',
263 'vcpu_avail',
264 'memory',
265 'shadow_memory',
266 'maxmem',
267 'start_time',
268 'name',
269 'on_poweroff',
270 'on_crash',
271 'on_reboot',
272 'on_xend_start',
273 'on_xend_stop',
274 ]
276 ##
277 ## Config Choices
278 ##
280 CONFIG_RESTART_MODES = ('restart', 'destroy', 'preserve', 'rename-restart',
281 'coredump-destroy', 'coredump-restart')
282 CONFIG_OLD_DOM_STATES = ('running', 'blocked', 'paused', 'shutdown',
283 'crashed', 'dying')
285 class XendConfigError(VmError):
286 def __str__(self):
287 return 'Invalid Configuration: %s' % str(self.value)
289 ##
290 ## XendConfig Class (an extended dictionary)
291 ##
293 class XendConfig(dict):
294 """ The new Xend VM Configuration.
296 Stores the configuration in xenapi compatible format but retains
297 import and export functions for SXP.
298 """
299 def __init__(self, filename = None, sxp_obj = None,
300 xapi = None, dominfo = None):
302 dict.__init__(self)
303 self.update(self._defaults())
305 if filename:
306 try:
307 sxp_obj = sxp.parse(open(filename,'r'))
308 sxp_obj = sxp_obj[0]
309 except IOError, e:
310 raise XendConfigError("Unable to read file: %s" % filename)
312 if sxp_obj:
313 self._sxp_to_xapi(sxp_obj)
314 self._sxp_to_xapi_unsupported(sxp_obj)
315 elif xapi:
316 self.update_with_xenapi_config(xapi)
317 elif dominfo:
318 # output from xc.domain_getinfo
319 self._dominfo_to_xapi(dominfo, update_mem = True)
321 log.debug('XendConfig.init: %s' % scrub_password(self))
323 # validators go here
324 self.validate()
326 """ In time, we should enable this type checking addition. It is great
327 also for tracking bugs and unintended writes to XendDomainInfo.info
328 def __setitem__(self, key, value):
329 type_conv = XENAPI_CFG_TYPES.get(key)
330 if callable(type_conv):
331 try:
332 dict.__setitem__(self, key, type_conv(value))
333 except (ValueError, TypeError):
334 raise XendConfigError("Wrong type for configuration value " +
335 "%s. Expected %s" %
336 (key, type_conv.__name__))
337 else:
338 dict.__setitem__(self, key, value)
339 """
341 def _defaults(self):
342 defaults = {
343 'name_label': 'Domain-Unnamed',
344 'actions_after_shutdown': 'destroy',
345 'actions_after_reboot': 'restart',
346 'actions_after_crash': 'restart',
347 'actions_after_suspend': '',
348 'is_a_template': False,
349 'is_control_domain': False,
350 'features': '',
351 'PV_bootloader': '',
352 'PV_kernel': '',
353 'PV_ramdisk': '',
354 'PV_args': '',
355 'PV_bootloader_args': '',
356 'HVM_boot_policy': '',
357 'HVM_boot_params': {},
358 'memory_static_min': 0,
359 'memory_dynamic_min': 0,
360 'shadow_memory': 0,
361 'memory_static_max': 0,
362 'memory_dynamic_max': 0,
363 'devices': {},
364 'on_xend_start': 'ignore',
365 'on_xend_stop': 'ignore',
366 'cpus': [],
367 'VCPUs_max': 1,
368 'VCPUs_live': 1,
369 'VCPUs_at_startup': 1,
370 'vcpus_params': {},
371 'console_refs': [],
372 'vif_refs': [],
373 'vbd_refs': [],
374 'vtpm_refs': [],
375 'other_config': {},
376 'platform': {},
377 'target': 0,
378 }
380 return defaults
382 #
383 # Here we assume these values exist in the dict.
384 # If they don't we have a bigger problem, lets not
385 # try and 'fix it up' but acutually fix the cause ;-)
386 #
387 def _memory_sanity_check(self):
388 log.trace("_memory_sanity_check memory_static_min: %s, "
389 "memory_static_max: %i, "
390 "memory_dynamic_min: %i, "
391 "memory_dynamic_max: %i",
392 self["memory_static_min"],
393 self["memory_static_max"],
394 self["memory_dynamic_min"],
395 self["memory_dynamic_max"])
397 if not self["memory_static_min"] <= self["memory_static_max"]:
398 raise XendConfigError("memory_static_min must be less " \
399 "than or equal to memory_static_max")
400 if not self["memory_static_min"] <= self["memory_dynamic_min"]:
401 raise XendConfigError("memory_static_min must be less " \
402 "than or equal to memory_dynamic_min")
403 if not self["memory_dynamic_max"] <= self["memory_static_max"]:
404 raise XendConfigError("memory_dynamic_max must be less " \
405 "than or equal to memory_static_max")
406 if not self["memory_dynamic_max"] > 0:
407 raise XendConfigError("memory_dynamic_max must be greater " \
408 "than zero")
409 if not self["memory_static_max"] > 0:
410 raise XendConfigError("memory_static_max must be greater " \
411 "than zero")
413 def _actions_sanity_check(self):
414 for event in ['shutdown', 'reboot', 'crash']:
415 if self['actions_after_' + event] not in CONFIG_RESTART_MODES:
416 raise XendConfigError('Invalid event handling mode: ' +
417 event)
419 def _vcpus_sanity_check(self):
420 if 'VCPUs_max' in self and 'vcpu_avail' not in self:
421 self['vcpu_avail'] = (1 << self['VCPUs_max']) - 1
423 def _uuid_sanity_check(self):
424 """Make sure UUID is in proper string format with hyphens."""
425 if 'uuid' not in self or not self['uuid']:
426 self['uuid'] = uuid.createString()
427 else:
428 self['uuid'] = uuid.toString(uuid.fromString(self['uuid']))
430 def _name_sanity_check(self):
431 if 'name_label' not in self:
432 self['name_label'] = 'Domain-' + self['uuid']
434 def _platform_sanity_check(self):
435 if 'keymap' not in self['platform'] and XendOptions.instance().get_keymap():
436 self['platform']['keymap'] = XendOptions.instance().get_keymap()
438 if self.is_hvm() or self.has_rfb():
439 if 'device_model' not in self['platform']:
440 self['platform']['device_model'] = xen.util.auxbin.pathTo("qemu-dm")
442 if self.is_hvm():
443 if 'timer_mode' not in self['platform']:
444 self['platform']['timer_mode'] = 0
445 if 'rtc_timeoffset' not in self['platform']:
446 self['platform']['rtc_timeoffset'] = 0
447 if 'hpet' not in self['platform']:
448 self['platform']['hpet'] = 0
449 if 'loader' not in self['platform']:
450 # Old configs may have hvmloader set as PV_kernel param
451 if self.has_key('PV_kernel'):
452 self['platform']['loader'] = self['PV_kernel']
453 self['PV_kernel'] = ''
454 else:
455 self['platform']['loader'] = "/usr/lib/xen/boot/hvmloader"
456 log.debug("Loader is %s" % str(self['platform']['loader']))
458 # Compatibility hack, can go away soon.
459 if 'soundhw' not in self['platform'] and \
460 self['platform'].get('enable_audio'):
461 self['platform']['soundhw'] = 'sb16'
463 def validate(self):
464 self._uuid_sanity_check()
465 self._name_sanity_check()
466 self._memory_sanity_check()
467 self._actions_sanity_check()
468 self._vcpus_sanity_check()
469 self._platform_sanity_check()
471 def _dominfo_to_xapi(self, dominfo, update_mem = False):
472 self['domid'] = dominfo['domid']
473 self['online_vcpus'] = dominfo['online_vcpus']
474 self['VCPUs_max'] = dominfo['max_vcpu_id'] + 1
476 if update_mem:
477 self['memory_dynamic_min'] = dominfo['mem_kb'] * 1024
478 self['memory_dynamic_max'] = dominfo['mem_kb'] * 1024
479 self['memory_static_min'] = 0
480 self['memory_static_max'] = dominfo['maxmem_kb'] * 1024
481 self._memory_sanity_check()
483 self['cpu_time'] = dominfo['cpu_time']/1e9
484 if dominfo.get('ssidref'):
485 ssidref = int(dominfo.get('ssidref'))
486 import xen.util.xsm.xsm as security
487 self['security_label'] = security.ssidref2security_label(ssidref)
489 self['shutdown_reason'] = dominfo['shutdown_reason']
491 # parse state into Xen API states
492 self['running'] = dominfo['running']
493 self['crashed'] = dominfo['crashed']
494 self['dying'] = dominfo['dying']
495 self['shutdown'] = dominfo['shutdown']
496 self['paused'] = dominfo['paused']
497 self['blocked'] = dominfo['blocked']
499 if 'name' in dominfo:
500 self['name_label'] = dominfo['name']
502 if 'handle' in dominfo:
503 self['uuid'] = uuid.toString(dominfo['handle'])
505 def parse_cpuid(self, cfg, field):
506 def int2bin(n, count=32):
507 return "".join([str((n >> y) & 1) for y in range(count-1, -1, -1)])
509 for input, regs in cfg[field].iteritems():
510 if not regs is dict:
511 cfg[field][input] = dict(regs)
513 cpuid = {}
514 for input in cfg[field]:
515 inputs = input.split(',')
516 if inputs[0][0:2] == '0x':
517 inputs[0] = str(int(inputs[0], 16))
518 if len(inputs) == 2:
519 if inputs[1][0:2] == '0x':
520 inputs[1] = str(int(inputs[1], 16))
521 new_input = ','.join(inputs)
522 cpuid[new_input] = {} # new input
523 for reg in cfg[field][input]:
524 val = cfg[field][input][reg]
525 if val[0:2] == '0x':
526 cpuid[new_input][reg] = int2bin(int(val, 16))
527 else:
528 cpuid[new_input][reg] = val
529 cfg[field] = cpuid
531 def _parse_sxp(self, sxp_cfg):
532 """ Populate this XendConfig using the parsed SXP.
534 @param sxp_cfg: Parsed SXP Configuration
535 @type sxp_cfg: list of lists
536 @rtype: dictionary
537 @return: A dictionary containing the parsed options of the SXP.
538 """
539 cfg = {}
541 for key, typ in XENAPI_CFG_TYPES.items():
542 val = sxp.child_value(sxp_cfg, key)
543 if val is not None:
544 try:
545 cfg[key] = typ(val)
546 except (ValueError, TypeError), e:
547 log.warn('Unable to convert type value for key: %s' % key)
549 # Convert deprecated options to current equivalents.
551 restart = sxp.child_value(sxp_cfg, 'restart')
552 if restart:
553 if restart == 'onreboot':
554 cfg['on_poweroff'] = 'destroy'
555 cfg['on_reboot'] = 'restart'
556 cfg['on_crash'] = 'destroy'
557 elif restart == 'always':
558 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
559 cfg[opt] = 'restart'
560 elif restart == 'never':
561 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
562 cfg[opt] = 'never'
563 else:
564 log.warn('Ignoring unrecognised value for deprecated option:'
565 'restart = \'%s\'', restart)
567 # Handle memory, passed in as MiB
569 if sxp.child_value(sxp_cfg, "memory") != None:
570 cfg["memory"] = int(sxp.child_value(sxp_cfg, "memory"))
571 if sxp.child_value(sxp_cfg, "maxmem") != None:
572 cfg["maxmem"] = int(sxp.child_value(sxp_cfg, "maxmem"))
574 # Convert scheduling parameters to vcpus_params
575 if 'vcpus_params' not in cfg:
576 cfg['vcpus_params'] = {}
577 cfg["vcpus_params"]["weight"] = \
578 int(sxp.child_value(sxp_cfg, "cpu_weight", 256))
579 cfg["vcpus_params"]["cap"] = \
580 int(sxp.child_value(sxp_cfg, "cpu_cap", 0))
582 # Only extract options we know about.
583 extract_keys = LEGACY_UNSUPPORTED_BY_XENAPI_CFG + \
584 XENAPI_CFG_TO_LEGACY_CFG.values()
586 for key in extract_keys:
587 val = sxp.child_value(sxp_cfg, key)
588 if val != None:
589 try:
590 cfg[key] = LEGACY_CFG_TYPES[key](val)
591 except KeyError:
592 cfg[key] = val
593 except (TypeError, ValueError), e:
594 log.warn("Unable to parse key %s: %s: %s" %
595 (key, str(val), e))
597 if 'platform' not in cfg:
598 cfg['platform'] = {}
599 localtime = sxp.child_value(sxp_cfg, 'localtime')
600 if localtime is not None:
601 cfg['platform']['localtime'] = localtime
603 # Compatibility hack -- can go soon.
604 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
605 val = sxp.child_value(sxp_cfg, "platform_" + key, None)
606 if val is not None:
607 self['platform'][key] = val
609 # Compatibility hack -- can go soon.
610 boot_order = sxp.child_value(sxp_cfg, 'HVM_boot')
611 if boot_order:
612 cfg['HVM_boot_policy'] = 'BIOS order'
613 cfg['HVM_boot_params'] = { 'order' : boot_order }
616 # Parsing the device SXP's.
617 cfg['devices'] = {}
618 for dev in sxp.children(sxp_cfg, 'device'):
619 config = sxp.child0(dev)
620 dev_type = sxp.name(config)
621 self.device_add(dev_type, cfg_sxp = config, target = cfg)
623 # Extract missing data from configuration entries
624 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
625 if image_sxp:
626 image_vcpus = sxp.child_value(image_sxp, 'vcpus')
627 if image_vcpus != None:
628 try:
629 if 'VCPUs_max' not in cfg:
630 cfg['VCPUs_max'] = int(image_vcpus)
631 elif cfg['VCPUs_max'] != int(image_vcpus):
632 cfg['VCPUs_max'] = int(image_vcpus)
633 log.warn('Overriding vcpus from %d to %d using image'
634 'vcpus value.', cfg['VCPUs_max'])
635 except ValueError, e:
636 raise XendConfigError('integer expeceted: %s: %s' %
637 image_sxp, e)
639 # Deprecated cpu configuration
640 if 'cpu' in cfg:
641 if 'cpus' in cfg:
642 cfg['cpus'] = "%s,%s" % (str(cfg['cpu']), cfg['cpus'])
643 else:
644 cfg['cpus'] = str(cfg['cpu'])
646 # Convert 'cpus' to list of list of ints
647 cpus_list = []
648 if 'cpus' in cfg:
649 # Convert the following string to list of ints.
650 # The string supports a list of ranges (0-3),
651 # seperated by commas, and negation (^1).
652 # Precedence is settled by order of the string:
653 # "0-3,^1" -> [0,2,3]
654 # "0-3,^1,1" -> [0,1,2,3]
655 def cnv(s):
656 l = []
657 for c in s.split(','):
658 if c.find('-') != -1:
659 (x, y) = c.split('-')
660 for i in range(int(x), int(y)+1):
661 l.append(int(i))
662 else:
663 # remove this element from the list
664 if c[0] == '^':
665 l = [x for x in l if x != int(c[1:])]
666 else:
667 l.append(int(c))
668 return l
670 if type(cfg['cpus']) == list:
671 if len(cfg['cpus']) > 0 and type(cfg['cpus'][0]) == list:
672 # If sxp_cfg was created from config.sxp,
673 # the form of 'cpus' is list of list of string.
674 # Convert 'cpus' to list of list of ints.
675 # Conversion examples:
676 # [['1']] -> [[1]]
677 # [['0','2'],['1','3']] -> [[0,2],[1,3]]
678 try:
679 for c1 in cfg['cpus']:
680 cpus = []
681 for c2 in c1:
682 cpus.append(int(c2))
683 cpus_list.append(cpus)
684 except ValueError, e:
685 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
686 else:
687 # Conversion examples:
688 # ["1"] -> [[1]]
689 # ["0,2","1,3"] -> [[0,2],[1,3]]
690 # ["0-3,^1","1-4,^2"] -> [[0,2,3],[1,3,4]]
691 try:
692 for c in cfg['cpus']:
693 cpus = cnv(c)
694 cpus_list.append(cpus)
695 except ValueError, e:
696 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
698 if len(cpus_list) != cfg['vcpus']:
699 raise XendConfigError('vcpus and the item number of cpus are not same')
700 else:
701 # Conversion examples:
702 # vcpus=1:
703 # "1" -> [[1]]
704 # "0-3,^1" -> [[0,2,3]]
705 # vcpus=2:
706 # "1" -> [[1],[1]]
707 # "0-3,^1" -> [[0,2,3],[0,2,3]]
708 try:
709 cpus = cnv(cfg['cpus'])
710 for v in range(0, cfg['vcpus']):
711 cpus_list.append(cpus)
712 except ValueError, e:
713 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
714 else:
715 # Generation examples:
716 # vcpus=1:
717 # -> [[]]
718 # vcpus=2:
719 # -> [[],[]]
720 for v in range(0, cfg['vcpus']):
721 cpus_list.append(list())
723 cfg['cpus'] = cpus_list
725 # Parse cpuid
726 if 'cpuid' in cfg:
727 self.parse_cpuid(cfg, 'cpuid')
728 if 'cpuid_check' in cfg:
729 self.parse_cpuid(cfg, 'cpuid_check')
731 import xen.util.xsm.xsm as security
732 if security.on() == xsconstants.XS_POLICY_ACM:
733 from xen.util.acmpolicy import ACM_LABEL_UNLABELED
734 if not 'security' in cfg and sxp.child_value(sxp_cfg, 'security'):
735 cfg['security'] = sxp.child_value(sxp_cfg, 'security')
736 elif not cfg.get('security_label'):
737 cfg['security'] = [['access_control',
738 ['policy', security.get_active_policy_name() ],
739 ['label', ACM_LABEL_UNLABELED ]]]
741 if 'security' in cfg and not cfg.get('security_label'):
742 secinfo = cfg['security']
743 # The xm command sends a list formatted like this:
744 # [['access_control', ['policy', 'xm-test'],['label', 'red']],
745 # ['ssidref', 196611]]
746 policy = ""
747 label = ""
748 for idx in range(0, len(secinfo)):
749 if secinfo[idx][0] == "access_control":
750 for aidx in range(1, len(secinfo[idx])):
751 if secinfo[idx][aidx][0] == "policy":
752 policy = secinfo[idx][aidx][1]
753 if secinfo[idx][aidx][0] == "label":
754 label = secinfo[idx][aidx][1]
755 cfg['security_label'] = \
756 security.set_security_label(policy, label)
757 if not sxp.child_value(sxp_cfg, 'security_label'):
758 del cfg['security']
760 sec_lab = cfg['security_label'].split(":")
761 if len(sec_lab) != 3:
762 raise XendConfigError("Badly formatted security label: %s"
763 % cfg['security_label'])
765 old_state = sxp.child_value(sxp_cfg, 'state')
766 if old_state:
767 for i in range(len(CONFIG_OLD_DOM_STATES)):
768 cfg[CONFIG_OLD_DOM_STATES[i]] = int(old_state[i] != '-')
770 return cfg
773 def _sxp_to_xapi(self, sxp_cfg):
774 """Read in an SXP Configuration object and
775 populate at much of the Xen API with valid values.
776 """
777 log.debug('_sxp_to_xapi(%s)' % scrub_password(sxp_cfg))
779 # _parse_sxp() below will call device_add() and construct devices.
780 # Some devices (currently only pci) may require VM's uuid, so
781 # setup self['uuid'] beforehand.
782 self['uuid'] = sxp.child_value(sxp_cfg, 'uuid', uuid.createString())
784 cfg = self._parse_sxp(sxp_cfg)
786 for key, typ in XENAPI_CFG_TYPES.items():
787 val = cfg.get(key)
788 if val is not None:
789 self[key] = typ(val)
791 # Convert parameters that can be directly mapped from
792 # the Legacy Config to Xen API Config
794 for apikey, cfgkey in XENAPI_CFG_TO_LEGACY_CFG.items():
795 try:
796 type_conv = XENAPI_CFG_TYPES.get(apikey)
797 if callable(type_conv):
798 self[apikey] = type_conv(cfg[cfgkey])
799 else:
800 log.warn("Unconverted key: " + apikey)
801 self[apikey] = cfg[cfgkey]
802 except KeyError:
803 pass
805 # Lets try and handle memory correctly
807 MiB = 1024 * 1024
809 if "memory" in cfg:
810 self["memory_static_min"] = 0
811 self["memory_static_max"] = int(cfg["memory"]) * MiB
812 self["memory_dynamic_min"] = int(cfg["memory"]) * MiB
813 self["memory_dynamic_max"] = int(cfg["memory"]) * MiB
815 if "maxmem" in cfg:
816 self["memory_static_max"] = int(cfg["maxmem"]) * MiB
818 self._memory_sanity_check()
820 def update_with(n, o):
821 if not self.get(n):
822 self[n] = cfg.get(o, '')
824 update_with('PV_bootloader', 'bootloader')
825 update_with('PV_bootloader_args', 'bootloader_args')
827 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
828 if image_sxp:
829 self.update_with_image_sxp(image_sxp)
831 # Convert Legacy HVM parameters to Xen API configuration
832 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
833 if key in cfg:
834 self['platform'][key] = cfg[key]
836 # set device references in the configuration
837 self['devices'] = cfg.get('devices', {})
838 self['console_refs'] = cfg.get('console_refs', [])
839 self['vif_refs'] = cfg.get('vif_refs', [])
840 self['vbd_refs'] = cfg.get('vbd_refs', [])
841 self['vtpm_refs'] = cfg.get('vtpm_refs', [])
843 # coalesce hvm vnc frame buffer with vfb config
844 if self.is_hvm() and int(self['platform'].get('vnc', 0)) != 0:
845 # add vfb device if it isn't there already
846 if not self.has_rfb():
847 dev_config = ['vfb']
848 dev_config.append(['type', 'vnc'])
849 # copy VNC related params from platform config to vfb dev conf
850 for key in ['vncpasswd', 'vncunused', 'vncdisplay',
851 'vnclisten']:
852 if key in self['platform']:
853 dev_config.append([key, self['platform'][key]])
855 self.device_add('vfb', cfg_sxp = dev_config)
858 def has_rfb(self):
859 for console_uuid in self['console_refs']:
860 if self['devices'][console_uuid][1].get('protocol') == 'rfb':
861 return True
862 if self['devices'][console_uuid][0] == 'vfb':
863 return True
864 return False
866 def _sxp_to_xapi_unsupported(self, sxp_cfg):
867 """Read in an SXP configuration object and populate
868 values are that not related directly supported in
869 the Xen API.
870 """
872 log.debug('_sxp_to_xapi_unsupported(%s)' % scrub_password(sxp_cfg))
874 # Parse and convert parameters used to configure
875 # the image (as well as HVM images)
876 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
877 if image_sxp:
878 image_type = sxp.name(image_sxp)
879 if image_type != 'hvm' and image_type != 'linux':
880 self['platform']['image_type'] = image_type
882 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
883 val = sxp.child_value(image_sxp, key, None)
884 if val is not None and val != '':
885 self['platform'][key] = val
887 notes = sxp.children(image_sxp, 'notes')
888 if notes:
889 self['notes'] = self.notes_from_sxp(notes[0])
891 self._hvm_boot_params_from_sxp(image_sxp)
893 # extract backend value
895 backend = []
896 for c in sxp.children(sxp_cfg, 'backend'):
897 backend.append(sxp.name(sxp.child0(c)))
898 if backend:
899 self['backend'] = backend
901 # Parse and convert other Non Xen API parameters.
902 def _set_cfg_if_exists(sxp_arg):
903 val = sxp.child_value(sxp_cfg, sxp_arg)
904 if val != None:
905 if LEGACY_CFG_TYPES.get(sxp_arg):
906 self[sxp_arg] = LEGACY_CFG_TYPES[sxp_arg](val)
907 else:
908 self[sxp_arg] = val
910 _set_cfg_if_exists('shadow_memory')
911 _set_cfg_if_exists('features')
912 _set_cfg_if_exists('on_xend_stop')
913 _set_cfg_if_exists('on_xend_start')
914 _set_cfg_if_exists('vcpu_avail')
916 # Parse and store runtime configuration
917 _set_cfg_if_exists('start_time')
918 _set_cfg_if_exists('cpu_time')
919 _set_cfg_if_exists('shutdown_reason')
920 _set_cfg_if_exists('up_time')
921 _set_cfg_if_exists('status') # TODO, deprecated
923 def _get_old_state_string(self):
924 """Returns the old xm state string.
925 @rtype: string
926 @return: old state string
927 """
928 state_string = ''
929 for state_name in CONFIG_OLD_DOM_STATES:
930 on_off = self.get(state_name, 0)
931 if on_off:
932 state_string += state_name[0]
933 else:
934 state_string += '-'
936 return state_string
939 def update_config(self, dominfo):
940 """Update configuration with the output from xc.domain_getinfo().
942 @param dominfo: Domain information via xc.domain_getinfo()
943 @type dominfo: dict
944 """
945 self._dominfo_to_xapi(dominfo)
946 self.validate()
948 def update_with_xenapi_config(self, xapi):
949 """Update configuration with a Xen API VM struct
951 @param xapi: Xen API VM Struct
952 @type xapi: dict
953 """
955 log.debug('update_with_xenapi_config: %s' % scrub_password(xapi))
957 for key, val in xapi.items():
958 type_conv = XENAPI_CFG_TYPES.get(key)
959 if type_conv is None:
960 key = key.lower()
961 type_conv = XENAPI_CFG_TYPES.get(key)
962 if callable(type_conv):
963 self[key] = type_conv(val)
964 else:
965 self[key] = val
967 # XenAPI defines platform as a string-string map. If platform
968 # configuration exists, convert values to appropriate type.
969 if 'platform' in xapi:
970 for key, val in xapi['platform'].items():
971 type_conv = XENAPI_PLATFORM_CFG_TYPES.get(key)
972 if type_conv is None:
973 key = key.lower()
974 type_conv = XENAPI_PLATFORM_CFG_TYPES.get(key)
975 if callable(type_conv):
976 self['platform'][key] = type_conv(val)
977 else:
978 self['platform'][key] = val
980 self['vcpus_params']['weight'] = \
981 int(self['vcpus_params'].get('weight', 256))
982 self['vcpus_params']['cap'] = int(self['vcpus_params'].get('cap', 0))
984 def cpuid_to_sxp(self, sxpr, field):
985 regs_list = []
986 for input, regs in self[field].iteritems():
987 reg_list = []
988 for reg, val in regs.iteritems():
989 reg_list.append([reg, val])
990 regs_list.append([input, reg_list])
991 sxpr.append([field, regs_list])
994 def to_sxp(self, domain = None, ignore_devices = False, ignore = [],
995 legacy_only = True):
996 """ Get SXP representation of this config object.
998 Incompat: removed store_mfn, console_mfn
1000 @keyword domain: (optional) XendDomainInfo to get extra information
1001 from such as domid and running devices.
1002 @type domain: XendDomainInfo
1003 @keyword ignore: (optional) list of 'keys' that we do not want
1004 to export.
1005 @type ignore: list of strings
1006 @rtype: list of list (SXP representation)
1007 """
1008 sxpr = ['domain']
1010 # TODO: domid/dom is the same thing but called differently
1011 # depending if it is from xenstore or sxpr.
1013 if domain.getDomid() is not None:
1014 sxpr.append(['domid', domain.getDomid()])
1016 if not legacy_only:
1017 for name, typ in XENAPI_CFG_TYPES.items():
1018 if name in self and self[name] not in (None, []):
1019 if typ == dict:
1020 s = self[name].items()
1021 elif typ == list:
1022 s = self[name]
1023 else:
1024 s = str(self[name])
1025 sxpr.append([name, s])
1027 for xenapi, legacy in XENAPI_CFG_TO_LEGACY_CFG.items():
1028 if legacy in ('cpus'): # skip this
1029 continue
1030 if self.has_key(xenapi) and self[xenapi] not in (None, []):
1031 if type(self[xenapi]) == bool:
1032 # convert booleans to ints before making an sxp item
1033 sxpr.append([legacy, int(self[xenapi])])
1034 else:
1035 sxpr.append([legacy, self[xenapi]])
1037 MiB = 1024*1024
1039 sxpr.append(["maxmem", int(self["memory_static_max"])/MiB])
1040 sxpr.append(["memory", int(self["memory_dynamic_max"])/MiB])
1042 for legacy in LEGACY_UNSUPPORTED_BY_XENAPI_CFG:
1043 if legacy in ('domid', 'uuid', 'cpus'): # skip these
1044 continue
1045 if self.has_key(legacy) and self[legacy] not in (None, []):
1046 sxpr.append([legacy, self[legacy]])
1048 if self.has_key('security_label'):
1049 sxpr.append(['security_label', self['security_label']])
1051 sxpr.append(['image', self.image_sxpr()])
1052 sxpr.append(['status', domain._stateGet()])
1054 if domain.getDomid() is not None:
1055 sxpr.append(['state', self._get_old_state_string()])
1057 if domain:
1058 if domain.store_mfn:
1059 sxpr.append(['store_mfn', domain.store_mfn])
1060 if domain.console_mfn:
1061 sxpr.append(['console_mfn', domain.console_mfn])
1064 # Marshall devices (running or from configuration)
1065 if not ignore_devices:
1066 txn = xstransact()
1067 try:
1068 for cls in XendDevices.valid_devices():
1069 found = False
1071 # figure if there is a dev controller is valid and running
1072 if domain and domain.getDomid() != None:
1073 try:
1074 controller = domain.getDeviceController(cls)
1075 configs = controller.configurations(txn)
1076 for config in configs:
1077 if sxp.name(config) in ('vbd', 'tap'):
1078 # The bootable flag is never written to the
1079 # store as part of the device config.
1080 dev_uuid = sxp.child_value(config, 'uuid')
1081 dev_type, dev_cfg = self['devices'][dev_uuid]
1082 is_bootable = dev_cfg.get('bootable', 0)
1083 config.append(['bootable', int(is_bootable)])
1084 config.append(['VDI', dev_cfg.get('VDI', '')])
1086 sxpr.append(['device', config])
1088 found = True
1089 except:
1090 log.exception("dumping sxp from device controllers")
1091 pass
1093 # if we didn't find that device, check the existing config
1094 # for a device in the same class
1095 if not found:
1096 for dev_type, dev_info in self.all_devices_sxpr():
1097 if dev_type == cls:
1098 sxpr.append(['device', dev_info])
1100 txn.commit()
1101 except:
1102 txn.abort()
1103 raise
1105 if 'cpuid' in self:
1106 self.cpuid_to_sxp(sxpr, 'cpuid')
1107 if 'cpuid_check' in self:
1108 self.cpuid_to_sxp(sxpr, 'cpuid_check')
1110 log.debug(sxpr)
1112 return sxpr
1114 def _blkdev_name_to_number(self, dev):
1115 if 'ioemu:' in dev:
1116 _, dev = dev.split(':', 1)
1117 try:
1118 dev, _ = dev.split(':', 1)
1119 except ValueError:
1120 pass
1122 try:
1123 devid = int(dev)
1124 except ValueError:
1125 # devid is not a number but a string containing either device
1126 # name (e.g. xvda) or device_type/device_id (e.g. vbd/51728)
1127 dev2 = type(dev) is str and dev.split('/')[-1] or None
1128 if dev2 == None:
1129 log.debug("Could not check the device %s", dev)
1130 return None
1131 try:
1132 devid = int(dev2)
1133 except ValueError:
1134 (xenbus, devid) = blkdev_name_to_number(dev2)
1135 if devid == None:
1136 log.debug("The device %s is not device name", dev2)
1137 return None
1138 return devid
1140 def device_duplicate_check(self, dev_type, dev_info, defined_config):
1141 defined_devices_sxpr = self.all_devices_sxpr(target = defined_config)
1143 if dev_type == 'vbd' or dev_type == 'tap':
1144 dev_uname = dev_info.get('uname')
1145 blkdev_name = dev_info.get('dev')
1146 devid = self._blkdev_name_to_number(blkdev_name)
1147 if devid == None or dev_uname == None:
1148 return
1150 for o_dev_type, o_dev_info in defined_devices_sxpr:
1151 if o_dev_type == 'vbd' or o_dev_type == 'tap':
1152 blkdev_file = blkdev_uname_to_file(dev_uname)
1153 o_dev_uname = sxp.child_value(o_dev_info, 'uname')
1154 if o_dev_uname != None:
1155 o_blkdev_file = blkdev_uname_to_file(o_dev_uname)
1156 if blkdev_file == o_blkdev_file:
1157 raise XendConfigError('The file "%s" is already used' %
1158 blkdev_file)
1159 o_blkdev_name = sxp.child_value(o_dev_info, 'dev')
1160 o_devid = self._blkdev_name_to_number(o_blkdev_name)
1161 if o_devid != None and devid == o_devid:
1162 raise XendConfigError('The device "%s" is already defined' %
1163 blkdev_name)
1165 elif dev_type == 'vif':
1166 dev_mac = dev_info.get('mac')
1168 for o_dev_type, o_dev_info in defined_devices_sxpr:
1169 if dev_type == o_dev_type:
1170 if dev_mac.lower() == sxp.child_value(o_dev_info, 'mac').lower():
1171 raise XendConfigError('The mac "%s" is already defined' %
1172 dev_mac)
1174 def device_add(self, dev_type, cfg_sxp = None, cfg_xenapi = None,
1175 target = None):
1176 """Add a device configuration in SXP format or XenAPI struct format.
1178 For SXP, it could be either:
1180 [device, [vbd, [uname ...]]
1182 or:
1184 [vbd, [uname ..]]
1186 @type cfg_sxp: list of lists (parsed sxp object)
1187 @param cfg_sxp: SXP configuration object
1188 @type cfg_xenapi: dict
1189 @param cfg_xenapi: A device configuration from Xen API (eg. vbd,vif)
1190 @param target: write device information to
1191 @type target: None or a dictionary
1192 @rtype: string
1193 @return: Assigned UUID of the device.
1194 """
1195 if target == None:
1196 target = self
1198 if dev_type not in XendDevices.valid_devices():
1199 raise XendConfigError("XendConfig: %s not a valid device type" %
1200 dev_type)
1202 if cfg_sxp == None and cfg_xenapi == None:
1203 raise XendConfigError("XendConfig: device_add requires some "
1204 "config.")
1206 #if cfg_sxp:
1207 # log.debug("XendConfig.device_add: %s" % str(cfg_sxp))
1208 #if cfg_xenapi:
1209 # log.debug("XendConfig.device_add: %s" % str(cfg_xenapi))
1211 if cfg_sxp:
1212 if sxp.child0(cfg_sxp) == 'device':
1213 config = sxp.child0(cfg_sxp)
1214 else:
1215 config = cfg_sxp
1217 dev_type = sxp.name(config)
1218 dev_info = {}
1220 if dev_type == 'pci' or dev_type == 'vscsi':
1221 pci_devs_uuid = sxp.child_value(config, 'uuid',
1222 uuid.createString())
1224 pci_dict = self.pci_convert_sxp_to_dict(config)
1225 pci_devs = pci_dict['devs']
1227 if dev_type != 'vscsi':
1228 # create XenAPI DPCI objects.
1229 for pci_dev in pci_devs:
1230 dpci_uuid = pci_dev.get('uuid')
1231 ppci_uuid = XendPPCI.get_by_sbdf(pci_dev['domain'],
1232 pci_dev['bus'],
1233 pci_dev['slot'],
1234 pci_dev['func'])
1235 if ppci_uuid is None:
1236 continue
1237 dpci_record = {
1238 'VM': self['uuid'],
1239 'PPCI': ppci_uuid,
1240 'hotplug_slot': pci_dev.get('vslot', 0)
1242 XendDPCI(dpci_uuid, dpci_record)
1244 target['devices'][pci_devs_uuid] = (dev_type,
1245 {'devs': pci_devs,
1246 'uuid': pci_devs_uuid})
1248 log.debug("XendConfig: reading device: %s" % pci_devs)
1250 return pci_devs_uuid
1252 for opt_val in config[1:]:
1253 try:
1254 opt, val = opt_val
1255 dev_info[opt] = val
1256 except (TypeError, ValueError): # unpack error
1257 pass
1259 if dev_type == 'vbd':
1260 dev_info['bootable'] = 0
1261 if dev_info.get('dev', '').startswith('ioemu:'):
1262 dev_info['driver'] = 'ioemu'
1263 else:
1264 dev_info['driver'] = 'paravirtualised'
1266 if dev_type == 'tap':
1267 if dev_info['uname'].split(':')[1] not in blktap_disk_types:
1268 raise XendConfigError("tap:%s not a valid disk type" %
1269 dev_info['uname'].split(':')[1])
1271 if dev_type == 'vif':
1272 if not dev_info.get('mac'):
1273 dev_info['mac'] = randomMAC()
1275 self.device_duplicate_check(dev_type, dev_info, target)
1277 if dev_type == 'vif':
1278 if dev_info.get('policy') and dev_info.get('label'):
1279 dev_info['security_label'] = "%s:%s:%s" % \
1280 (xsconstants.ACM_POLICY_ID,
1281 dev_info['policy'],dev_info['label'])
1283 # create uuid if it doesn't exist
1284 dev_uuid = dev_info.get('uuid', None)
1285 if not dev_uuid:
1286 dev_uuid = uuid.createString()
1287 dev_info['uuid'] = dev_uuid
1289 # store dev references by uuid for certain device types
1290 target['devices'][dev_uuid] = (dev_type, dev_info)
1291 if dev_type in ('vif', 'vbd', 'vtpm'):
1292 param = '%s_refs' % dev_type
1293 if param not in target:
1294 target[param] = []
1295 if dev_uuid not in target[param]:
1296 if dev_type == 'vbd':
1297 # Compat hack -- mark first disk bootable
1298 dev_info['bootable'] = int(not target[param])
1299 target[param].append(dev_uuid)
1300 elif dev_type == 'tap':
1301 if 'vbd_refs' not in target:
1302 target['vbd_refs'] = []
1303 if dev_uuid not in target['vbd_refs']:
1304 # Compat hack -- mark first disk bootable
1305 dev_info['bootable'] = int(not target['vbd_refs'])
1306 target['vbd_refs'].append(dev_uuid)
1308 elif dev_type == 'vfb':
1309 # Populate other config with aux data that is associated
1310 # with vfb
1312 other_config = {}
1313 for key in XENAPI_CONSOLE_OTHER_CFG:
1314 if key in dev_info:
1315 other_config[key] = dev_info[key]
1316 target['devices'][dev_uuid][1]['other_config'] = other_config
1319 if 'console_refs' not in target:
1320 target['console_refs'] = []
1322 # Treat VFB devices as console devices so they are found
1323 # through Xen API
1324 if dev_uuid not in target['console_refs']:
1325 target['console_refs'].append(dev_uuid)
1327 elif dev_type == 'console':
1328 if 'console_refs' not in target:
1329 target['console_refs'] = []
1330 if dev_uuid not in target['console_refs']:
1331 target['console_refs'].append(dev_uuid)
1333 log.debug("XendConfig: reading device: %s" % scrub_password(dev_info))
1334 return dev_uuid
1336 if cfg_xenapi:
1337 dev_info = {}
1338 dev_uuid = ''
1339 if dev_type == 'vif':
1340 dev_info['mac'] = cfg_xenapi.get('MAC')
1341 if not dev_info['mac']:
1342 dev_info['mac'] = randomMAC()
1343 # vifname is the name on the guest, not dom0
1344 # TODO: we don't have the ability to find that out or
1345 # change it from dom0
1346 #if cfg_xenapi.get('device'): # don't add if blank
1347 # dev_info['vifname'] = cfg_xenapi.get('device')
1348 if cfg_xenapi.get('type'):
1349 dev_info['type'] = cfg_xenapi.get('type')
1350 if cfg_xenapi.get('name'):
1351 dev_info['name'] = cfg_xenapi.get('name')
1352 if cfg_xenapi.get('network'):
1353 network = XendAPIStore.get(
1354 cfg_xenapi.get('network'), 'network')
1355 dev_info['bridge'] = network.get_name_label()
1357 if cfg_xenapi.get('security_label'):
1358 dev_info['security_label'] = \
1359 cfg_xenapi.get('security_label')
1361 dev_uuid = cfg_xenapi.get('uuid', None)
1362 if not dev_uuid:
1363 dev_uuid = uuid.createString()
1364 dev_info['uuid'] = dev_uuid
1365 target['devices'][dev_uuid] = (dev_type, dev_info)
1366 target['vif_refs'].append(dev_uuid)
1368 elif dev_type in ('vbd', 'tap'):
1369 dev_info['type'] = cfg_xenapi.get('type', 'Disk')
1370 if dev_info['type'] == 'CD':
1371 old_vbd_type = 'cdrom'
1372 else:
1373 old_vbd_type = 'disk'
1375 dev_info['uname'] = cfg_xenapi.get('image', '')
1376 dev_info['dev'] = '%s:%s' % (cfg_xenapi.get('device'),
1377 old_vbd_type)
1378 dev_info['bootable'] = int(cfg_xenapi.get('bootable', 0))
1379 dev_info['driver'] = cfg_xenapi.get('driver', '')
1380 dev_info['VDI'] = cfg_xenapi.get('VDI', '')
1382 if cfg_xenapi.get('mode') == 'RW':
1383 dev_info['mode'] = 'w'
1384 else:
1385 dev_info['mode'] = 'r'
1387 dev_uuid = cfg_xenapi.get('uuid', None)
1388 if not dev_uuid:
1389 dev_uuid = uuid.createString()
1390 dev_info['uuid'] = dev_uuid
1391 target['devices'][dev_uuid] = (dev_type, dev_info)
1392 target['vbd_refs'].append(dev_uuid)
1394 elif dev_type == 'vtpm':
1395 if cfg_xenapi.get('type'):
1396 dev_info['type'] = cfg_xenapi.get('type')
1398 dev_uuid = cfg_xenapi.get('uuid', None)
1399 if not dev_uuid:
1400 dev_uuid = uuid.createString()
1401 dev_info['uuid'] = dev_uuid
1402 dev_info['other_config'] = cfg_xenapi.get('other_config', {})
1403 target['devices'][dev_uuid] = (dev_type, dev_info)
1404 target['vtpm_refs'].append(dev_uuid)
1406 elif dev_type == 'console':
1407 dev_uuid = cfg_xenapi.get('uuid', None)
1408 if not dev_uuid:
1409 dev_uuid = uuid.createString()
1410 dev_info['uuid'] = dev_uuid
1411 dev_info['protocol'] = cfg_xenapi.get('protocol', 'rfb')
1412 console_other_config = cfg_xenapi.get('other_config', {})
1413 dev_info['other_config'] = console_other_config
1414 if dev_info['protocol'] == 'rfb':
1415 # collapse other config into devinfo for things
1416 # such as vncpasswd, vncunused, etc.
1417 dev_info.update(console_other_config)
1418 dev_info['type'] = console_other_config.get('type', 'vnc')
1419 target['devices'][dev_uuid] = ('vfb', dev_info)
1420 target['console_refs'].append(dev_uuid)
1422 # if console is rfb, set device_model ensuring qemu
1423 # is invoked for pvfb services
1424 if 'device_model' not in target['platform']:
1425 target['platform']['device_model'] = \
1426 xen.util.auxbin.pathTo("qemu-dm")
1428 # Finally, if we are a pvfb, we need to make a vkbd
1429 # as well that is not really exposed to Xen API
1430 vkbd_uuid = uuid.createString()
1431 target['devices'][vkbd_uuid] = ('vkbd', {})
1433 elif dev_info['protocol'] == 'vt100':
1434 # if someone tries to create a VT100 console
1435 # via the Xen API, we'll have to ignore it
1436 # because we create one automatically in
1437 # XendDomainInfo._update_consoles
1438 raise XendConfigError('Creating vt100 consoles via '
1439 'Xen API is unsupported')
1441 return dev_uuid
1443 # no valid device to add
1444 return ''
1446 def phantom_device_add(self, dev_type, cfg_xenapi = None,
1447 target = None):
1448 """Add a phantom tap device configuration in XenAPI struct format.
1449 """
1451 if target == None:
1452 target = self
1454 if dev_type not in XendDevices.valid_devices() and \
1455 dev_type not in XendDevices.pseudo_devices():
1456 raise XendConfigError("XendConfig: %s not a valid device type" %
1457 dev_type)
1459 if cfg_xenapi == None:
1460 raise XendConfigError("XendConfig: device_add requires some "
1461 "config.")
1463 if cfg_xenapi:
1464 log.debug("XendConfig.phantom_device_add: %s" % str(cfg_xenapi))
1466 if cfg_xenapi:
1467 dev_info = {}
1468 if dev_type in ('vbd', 'tap'):
1469 if dev_type == 'vbd':
1470 dev_info['uname'] = cfg_xenapi.get('image', '')
1471 dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
1472 elif dev_type == 'tap':
1473 if cfg_xenapi.get('image').find('tap:') == -1:
1474 dev_info['uname'] = 'tap:qcow:%s' % cfg_xenapi.get('image')
1475 dev_info['dev'] = '/dev/%s' % cfg_xenapi.get('device')
1476 dev_info['uname'] = cfg_xenapi.get('image')
1477 dev_info['mode'] = cfg_xenapi.get('mode')
1478 dev_info['backend'] = '0'
1479 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
1480 dev_info['uuid'] = dev_uuid
1481 self['devices'][dev_uuid] = (dev_type, dev_info)
1482 self['vbd_refs'].append(dev_uuid)
1483 return dev_uuid
1485 return ''
1487 def pci_convert_sxp_to_dict(self, dev_sxp):
1488 """Convert pci device sxp to dict
1489 @param dev_sxp: device configuration
1490 @type dev_sxp: SXP object (parsed config)
1491 @return: dev_config
1492 @rtype: dictionary
1493 """
1494 # Parsing the device SXP's. In most cases, the SXP looks
1495 # like this:
1497 # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]]
1499 # However, for PCI devices it looks like this:
1501 # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1], [func, 2]]]
1503 # It seems the reasoning for this difference is because
1504 # pciif.py needs all the PCI device configurations at
1505 # the same time when creating the devices.
1507 # To further complicate matters, Xen 2.0 configuration format
1508 # uses the following for pci device configuration:
1510 # [device, [pci, [domain, 0], [bus, 0], [dev, 1], [func, 2]]]
1512 # For PCI device hotplug support, the SXP of PCI devices is
1513 # extendend like this:
1515 # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1], [func, 2],
1516 # [vslt, 0]],
1517 # [state, 'Initialising']]]
1519 # 'vslt' shows the virtual hotplug slot number which the PCI device
1520 # is inserted in. This is only effective for HVM domains.
1522 # state 'Initialising' indicates that the device is being attached,
1523 # while state 'Closing' indicates that the device is being detached.
1525 # The Dict looks like this:
1527 # { devs: [{domain: 0, bus: 0, slot: 1, func: 2, vslt: 0}],
1528 # states: ['Initialising'] }
1530 dev_config = {}
1532 pci_devs = []
1533 for pci_dev in sxp.children(dev_sxp, 'dev'):
1534 pci_dev_info = {}
1535 for opt_val in pci_dev[1:]:
1536 try:
1537 opt, val = opt_val
1538 pci_dev_info[opt] = val
1539 except TypeError:
1540 pass
1541 # append uuid for each pci device.
1542 dpci_uuid = pci_dev_info.get('uuid', uuid.createString())
1543 pci_dev_info['uuid'] = dpci_uuid
1544 pci_devs.append(pci_dev_info)
1545 dev_config['devs'] = pci_devs
1547 pci_states = []
1548 for pci_state in sxp.children(dev_sxp, 'state'):
1549 try:
1550 pci_states.append(pci_state[1])
1551 except IndexError:
1552 raise XendError("Error reading state while parsing pci sxp")
1553 dev_config['states'] = pci_states
1555 return dev_config
1557 def console_add(self, protocol, location, other_config = {}):
1558 dev_uuid = uuid.createString()
1559 if protocol == 'vt100':
1560 dev_info = {
1561 'uuid': dev_uuid,
1562 'protocol': protocol,
1563 'location': location,
1564 'other_config': other_config,
1567 if 'devices' not in self:
1568 self['devices'] = {}
1570 self['devices'][dev_uuid] = ('console', dev_info)
1571 self['console_refs'].append(dev_uuid)
1572 return dev_info
1574 return {}
1576 def console_update(self, console_uuid, key, value):
1577 for dev_uuid, (dev_type, dev_info) in self['devices'].items():
1578 if dev_uuid == console_uuid:
1579 dev_info[key] = value
1580 # collapse other_config into dev_info for things
1581 # such as vncpasswd, vncunused, etc.
1582 if key == 'other_config':
1583 for k in XENAPI_CONSOLE_OTHER_CFG:
1584 if k in dev_info and k not in value:
1585 del dev_info[k]
1586 dev_info.update(value)
1587 break
1589 def console_get_all(self, protocol):
1590 if protocol == 'vt100':
1591 consoles = [dinfo for dtype, dinfo in self['devices'].values()
1592 if dtype == 'console']
1593 return [c for c in consoles if c.get('protocol') == protocol]
1595 elif protocol == 'rfb':
1596 vfbs = [dinfo for dtype, dinfo in self['devices'].values()
1597 if dtype == 'vfb']
1599 # move all non-console key values to other_config before
1600 # returning console config
1601 valid_keys = ['uuid', 'location']
1602 for vfb in vfbs:
1603 other_config = {}
1604 for key, val in vfb.items():
1605 if key not in valid_keys:
1606 other_config[key] = vfb[key]
1607 del vfb[key]
1608 vfb['other_config'] = other_config
1609 vfb['protocol'] = 'rfb'
1611 return vfbs
1613 else:
1614 return []
1616 def device_update(self, dev_uuid, cfg_sxp = [], cfg_xenapi = {}):
1617 """Update an existing device with the new configuration.
1619 @rtype: boolean
1620 @return: Returns True if succesfully found and updated a device conf
1621 """
1622 if dev_uuid in self['devices'] and cfg_sxp:
1623 if sxp.child0(cfg_sxp) == 'device':
1624 config = sxp.child0(cfg_sxp)
1625 else:
1626 config = cfg_sxp
1628 dev_type, dev_info = self['devices'][dev_uuid]
1630 if dev_type == 'pci' or dev_type == 'vscsi': # Special case for pci
1631 pci_dict = self.pci_convert_sxp_to_dict(config)
1632 pci_devs = pci_dict['devs']
1634 # destroy existing XenAPI DPCI objects
1635 for dpci_uuid in XendDPCI.get_by_VM(self['uuid']):
1636 XendAPIStore.deregister(dpci_uuid, "DPCI")
1638 if dev_type != 'vscsi':
1639 # create XenAPI DPCI objects.
1640 for pci_dev in pci_devs:
1641 dpci_uuid = pci_dev.get('uuid')
1642 ppci_uuid = XendPPCI.get_by_sbdf(pci_dev['domain'],
1643 pci_dev['bus'],
1644 pci_dev['slot'],
1645 pci_dev['func'])
1646 if ppci_uuid is None:
1647 continue
1648 dpci_record = {
1649 'VM': self['uuid'],
1650 'PPCI': ppci_uuid,
1651 'hotplug_slot': pci_dev.get('vslot', 0)
1653 XendDPCI(dpci_uuid, dpci_record)
1655 self['devices'][dev_uuid] = (dev_type,
1656 {'devs': pci_devs,
1657 'uuid': dev_uuid})
1658 return True
1660 for opt_val in config[1:]:
1661 try:
1662 opt, val = opt_val
1663 dev_info[opt] = val
1664 except (TypeError, ValueError):
1665 pass # no value for this config option
1667 self['devices'][dev_uuid] = (dev_type, dev_info)
1668 return True
1670 elif dev_uuid in self['devices'] and cfg_xenapi:
1671 dev_type, dev_info = self['devices'][dev_uuid]
1672 for key, val in cfg_xenapi.items():
1673 dev_info[key] = val
1674 self['devices'][dev_uuid] = (dev_type, dev_info)
1676 return False
1679 def device_sxpr(self, dev_uuid = None, dev_type = None, dev_info = None, target = None):
1680 """Get Device SXPR by either giving the device UUID or (type, config).
1682 @rtype: list of lists
1683 @return: device config sxpr
1684 """
1685 sxpr = []
1687 if target == None:
1688 target = self
1690 if dev_uuid != None and dev_uuid in target['devices']:
1691 dev_type, dev_info = target['devices'][dev_uuid]
1693 if dev_type == None or dev_info == None:
1694 raise XendConfigError("Required either UUID or device type and "
1695 "configuration dictionary.")
1697 sxpr.append(dev_type)
1698 if dev_type in ('console', 'vfb'):
1699 config = [(opt, val) for opt, val in dev_info.items()
1700 if opt != 'other_config']
1701 else:
1702 config = [(opt, val) for opt, val in dev_info.items()]
1704 sxpr += config
1706 return sxpr
1708 def ordered_device_refs(self, target = None):
1709 result = []
1711 if target == None:
1712 target = self
1714 # vkbd devices *must* be before vfb devices, otherwise
1715 # there is a race condition when setting up devices
1716 # where the daemon spawned for the vfb may write stuff
1717 # into xenstore vkbd backend, before DevController has
1718 # setup permissions on the vkbd backend path. This race
1719 # results in domain creation failing with 'device already
1720 # connected' messages
1721 result.extend([u for u in target['devices'].keys() if target['devices'][u][0] == 'vkbd'])
1723 result.extend(target.get('console_refs', []) +
1724 target.get('vbd_refs', []) +
1725 target.get('vif_refs', []) +
1726 target.get('vtpm_refs', []))
1728 result.extend([u for u in target['devices'].keys() if u not in result])
1729 return result
1731 def all_devices_sxpr(self, target = None):
1732 """Returns the SXPR for all devices in the current configuration."""
1733 sxprs = []
1734 pci_devs = []
1736 if target == None:
1737 target = self
1739 if 'devices' not in target:
1740 return sxprs
1742 ordered_refs = self.ordered_device_refs(target = target)
1743 for dev_uuid in ordered_refs:
1744 dev_type, dev_info = target['devices'][dev_uuid]
1745 if dev_type == 'pci' or dev_type == 'vscsi': # special case for pci devices
1746 if dev_type == 'pci':
1747 sxpr = ['pci', ['uuid', dev_info['uuid']]]
1748 elif dev_type == 'vscsi':
1749 sxpr = ['vscsi', ['uuid', dev_info['uuid']]]
1750 for pci_dev_info in dev_info['devs']:
1751 pci_dev_sxpr = ['dev']
1752 for opt, val in pci_dev_info.items():
1753 pci_dev_sxpr.append([opt, val])
1754 sxpr.append(pci_dev_sxpr)
1755 sxprs.append((dev_type, sxpr))
1756 else:
1757 sxpr = self.device_sxpr(dev_type = dev_type,
1758 dev_info = dev_info,
1759 target = target)
1760 sxprs.append((dev_type, sxpr))
1762 return sxprs
1764 def image_sxpr(self):
1765 """Returns a backwards compatible image SXP expression that is
1766 used in xenstore's /vm/<uuid>/image value and xm list."""
1767 image = [self.image_type()]
1768 if self.has_key('PV_kernel'):
1769 image.append(['kernel', self['PV_kernel']])
1770 if self.has_key('PV_ramdisk') and self['PV_ramdisk']:
1771 image.append(['ramdisk', self['PV_ramdisk']])
1772 if self.has_key('PV_args') and self['PV_args']:
1773 image.append(['args', self['PV_args']])
1775 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
1776 if key in self['platform']:
1777 image.append([key, self['platform'][key]])
1779 if 'notes' in self:
1780 image.append(self.notes_sxp(self['notes']))
1782 return image
1784 def update_with_image_sxp(self, image_sxp, bootloader = False):
1785 # Convert Legacy "image" config to Xen API PV_*
1786 # configuration
1787 log.debug("update_with_image_sxp(%s)" % scrub_password(image_sxp))
1789 # user-specified args must come last: previous releases did this and
1790 # some domU kernels rely upon the ordering.
1791 kernel_args = sxp.child_value(image_sxp, 'args', '')
1793 # attempt to extract extra arguments from SXP config
1794 arg_ip = sxp.child_value(image_sxp, 'ip')
1795 if arg_ip and not re.search(r'ip=[^ ]+', kernel_args):
1796 kernel_args = 'ip=%s ' % arg_ip + kernel_args
1797 arg_root = sxp.child_value(image_sxp, 'root')
1798 if arg_root and not re.search(r'root=', kernel_args):
1799 kernel_args = 'root=%s ' % arg_root + kernel_args
1801 if bootloader:
1802 self['_temp_using_bootloader'] = '1'
1803 self['_temp_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1804 self['_temp_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1805 self['_temp_args'] = kernel_args
1806 else:
1807 self['PV_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1808 self['PV_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1809 self['PV_args'] = kernel_args
1811 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
1812 val = sxp.child_value(image_sxp, key, None)
1813 if val is not None and val != '':
1814 self['platform'][key] = val
1816 notes = sxp.children(image_sxp, 'notes')
1817 if notes:
1818 self['notes'] = self.notes_from_sxp(notes[0])
1820 self._hvm_boot_params_from_sxp(image_sxp)
1822 def set_notes(self, notes):
1823 'Add parsed elfnotes to image'
1824 self['notes'] = notes
1826 def get_notes(self):
1827 try:
1828 return self['notes'] or {}
1829 except KeyError:
1830 return {}
1832 def notes_from_sxp(self, nsxp):
1833 notes = {}
1834 for note in sxp.children(nsxp):
1835 notes[note[0]] = note[1]
1836 return notes
1838 def notes_sxp(self, notes):
1839 nsxp = ['notes']
1840 for k, v in notes.iteritems():
1841 nsxp.append([k, str(v)])
1842 return nsxp
1844 def _hvm_boot_params_from_sxp(self, image_sxp):
1845 boot = sxp.child_value(image_sxp, 'boot', None)
1846 if boot is not None:
1847 self['HVM_boot_policy'] = 'BIOS order'
1848 self['HVM_boot_params'] = { 'order' : boot }
1850 def is_hvm(self):
1851 return self['HVM_boot_policy'] != ''
1853 def target(self):
1854 return self['target']
1856 def image_type(self):
1857 stored_type = self['platform'].get('image_type')
1858 return stored_type or (self.is_hvm() and 'hvm' or 'linux')
1860 def is_hap(self):
1861 return self['platform'].get('hap', 0)