debuggers.hg

view tools/python/xen/xend/XendConfig.py @ 0:7d21f7218375

Exact replica of unstable on 051908 + README-this
author Mukesh Rathor
date Mon May 19 15:34:57 2008 -0700 (2008-05-19)
parents
children 5c0bf00e371d
line source
1 #===========================================================================
2 # This library is free software; you can redistribute it and/or
3 # modify it under the terms of version 2.1 of the GNU Lesser General Public
4 # License as published by the Free Software Foundation.
5 #
6 # This library is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 # Lesser General Public License for more details.
10 #
11 # You should have received a copy of the GNU Lesser General Public
12 # License along with this library; if not, write to the Free Software
13 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 #============================================================================
15 # Copyright (C) 2006-2007 XenSource Ltd
16 #============================================================================
18 import logging
19 import re
20 import time
21 import types
23 from xen.xend import sxp
24 from xen.xend import uuid
25 from xen.xend import XendOptions
26 from xen.xend import XendAPIStore
27 from xen.xend.XendError import VmError
28 from xen.xend.XendDevices import XendDevices
29 from xen.xend.PrettyPrint import prettyprintstring
30 from xen.xend.XendConstants import DOM_STATE_HALTED
31 from xen.xend.xenstore.xstransact import xstransact
32 from xen.xend.server.BlktapController import blktap_disk_types
33 from xen.xend.server.netif import randomMAC
34 from xen.util.blkif import blkdev_name_to_number, blkdev_uname_to_file
35 from xen.util import xsconstants
36 import xen.util.auxbin
38 log = logging.getLogger("xend.XendConfig")
39 log.setLevel(logging.WARN)
42 """
43 XendConfig API
45 XendConfig will try to mirror as closely the Xen API VM Struct
46 with extra parameters for those options that are not supported.
48 """
50 def reverse_dict(adict):
51 """Return the reverse mapping of a dictionary."""
52 return dict([(v, k) for k, v in adict.items()])
54 def bool0(v):
55 return v != '0' and v != 'False' and bool(v)
57 # Recursively copy a data struct, scrubbing out VNC passwords.
58 # Will scrub any dict entry with a key of 'vncpasswd' or any
59 # 2-element list whose first member is 'vncpasswd'. It will
60 # also scrub a string matching '(vncpasswd XYZ)'. Everything
61 # else is no-op passthrough
62 def scrub_password(data):
63 if type(data) == dict or type(data) == XendConfig:
64 scrubbed = {}
65 for key in data.keys():
66 if key == "vncpasswd":
67 scrubbed[key] = "XXXXXXXX"
68 else:
69 scrubbed[key] = scrub_password(data[key])
70 return scrubbed
71 elif type(data) == list:
72 if len(data) == 2 and type(data[0]) == str and data[0] == 'vncpasswd':
73 return ['vncpasswd', 'XXXXXXXX']
74 else:
75 scrubbed = []
76 for entry in data:
77 scrubbed.append(scrub_password(entry))
78 return scrubbed
79 elif type(data) == tuple:
80 scrubbed = []
81 for entry in data:
82 scrubbed.append(scrub_password(entry))
83 return tuple(scrubbed)
84 elif type(data) == str:
85 return re.sub(r'\(vncpasswd\s+[^\)]+\)','(vncpasswd XXXXXX)', data)
86 else:
87 return data
89 #
90 # CPU fields:
91 #
92 # VCPUs_max -- the maximum number of vcpus that this domain may ever have.
93 # aka XendDomainInfo.getVCpuCount().
94 # vcpus -- the legacy configuration name for above.
95 # max_vcpu_id -- vcpus_number - 1. This is given to us by Xen.
96 #
97 # cpus -- the list of pCPUs available to each vCPU.
98 #
99 # vcpu_avail -- a bitmap telling the guest domain whether it may use each of
100 # its VCPUs. This is translated to
101 # <dompath>/cpu/<id>/availability = {online,offline} for use
102 # by the guest domain.
103 # VCPUs_live -- the number of VCPUs currently up, as reported by Xen. This
104 # is changed by changing vcpu_avail, and waiting for the
105 # domain to respond.
106 #
109 # Mapping from XendConfig configuration keys to the old
110 # legacy configuration keys that map directly.
112 XENAPI_CFG_TO_LEGACY_CFG = {
113 'uuid': 'uuid',
114 'VCPUs_max': 'vcpus',
115 'cpus': 'cpus',
116 'name_label': 'name',
117 'actions_after_shutdown': 'on_poweroff',
118 'actions_after_reboot': 'on_reboot',
119 'actions_after_crash': 'on_crash',
120 'PV_bootloader': 'bootloader',
121 'PV_bootloader_args': 'bootloader_args',
122 }
124 LEGACY_CFG_TO_XENAPI_CFG = reverse_dict(XENAPI_CFG_TO_LEGACY_CFG)
126 # Platform configuration keys and their types.
127 XENAPI_PLATFORM_CFG_TYPES = {
128 'acpi': int,
129 'apic': int,
130 'boot': str,
131 'device_model': str,
132 'loader': str,
133 'display' : str,
134 'fda': str,
135 'fdb': str,
136 'keymap': str,
137 'isa' : int,
138 'localtime': int,
139 'monitor': int,
140 'nographic': int,
141 'pae' : int,
142 'rtc_timeoffset': int,
143 'serial': str,
144 'sdl': int,
145 'opengl': int,
146 'soundhw': str,
147 'stdvga': int,
148 'usb': int,
149 'usbdevice': str,
150 'hpet': int,
151 'vnc': int,
152 'vncconsole': int,
153 'vncdisplay': int,
154 'vnclisten': str,
155 'timer_mode': int,
156 'vncpasswd': str,
157 'vncunused': int,
158 'xauthority': str,
159 'pci': str,
160 'vhpt': int,
161 'guest_os_type': str,
162 'hap': int,
163 }
165 # Xen API console 'other_config' keys.
166 XENAPI_CONSOLE_OTHER_CFG = ['vncunused', 'vncdisplay', 'vnclisten',
167 'vncpasswd', 'type', 'display', 'xauthority',
168 'keymap', 'opengl']
170 # List of XendConfig configuration keys that have no direct equivalent
171 # in the old world.
173 XENAPI_CFG_TYPES = {
174 'uuid': str,
175 'name_label': str,
176 'name_description': str,
177 'user_version': str,
178 'is_a_template': bool0,
179 'resident_on': str,
180 'memory_static_min': int, # note these are stored in bytes, not KB!
181 'memory_static_max': int,
182 'memory_dynamic_min': int,
183 'memory_dynamic_max': int,
184 'cpus': list,
185 'vcpus_params': dict,
186 'VCPUs_max': int,
187 'VCPUs_at_startup': int,
188 'VCPUs_live': int,
189 'actions_after_shutdown': str,
190 'actions_after_reboot': str,
191 'actions_after_crash': str,
192 'PV_bootloader': str,
193 'PV_kernel': str,
194 'PV_ramdisk': str,
195 'PV_args': str,
196 'PV_bootloader_args': str,
197 'HVM_boot_policy': str,
198 'HVM_boot_params': dict,
199 'PCI_bus': str,
200 'platform': dict,
201 'tools_version': dict,
202 'other_config': dict,
203 'target': int,
204 'security_label': str,
205 'pci': str,
206 'cpuid' : dict,
207 'cpuid_check' : dict,
208 }
210 # List of legacy configuration keys that have no equivalent in the
211 # Xen API, but are still stored in XendConfig.
213 LEGACY_UNSUPPORTED_BY_XENAPI_CFG = [
214 # roundtripped (dynamic, unmodified)
215 'shadow_memory',
216 'vcpu_avail',
217 'features',
218 # read/write
219 'on_xend_start',
220 'on_xend_stop',
221 # read-only
222 'domid',
223 'start_time',
224 'cpu_time',
225 'online_vcpus',
226 # write-once
227 'cpu',
228 'cpus',
229 ]
231 LEGACY_CFG_TYPES = {
232 'uuid': str,
233 'name': str,
234 'vcpus': int,
235 'vcpu_avail': long,
236 'memory': int,
237 'shadow_memory': int,
238 'maxmem': int,
239 'start_time': float,
240 'cpu_time': float,
241 'features': str,
242 'localtime': int,
243 'name': str,
244 'on_poweroff': str,
245 'on_reboot': str,
246 'on_crash': str,
247 'on_xend_stop': str,
248 'on_xend_start': str,
249 'online_vcpus': int,
250 'rtc/timeoffset': str,
251 }
253 # Values that should be stored in xenstore's /vm/<uuid> that is used
254 # by Xend. Used in XendDomainInfo to restore running VM state from
255 # xenstore.
256 LEGACY_XENSTORE_VM_PARAMS = [
257 'uuid',
258 'name',
259 'vcpus',
260 'vcpu_avail',
261 'memory',
262 'shadow_memory',
263 'maxmem',
264 'start_time',
265 'name',
266 'on_poweroff',
267 'on_crash',
268 'on_reboot',
269 'on_xend_start',
270 'on_xend_stop',
271 ]
273 ##
274 ## Config Choices
275 ##
277 CONFIG_RESTART_MODES = ('restart', 'destroy', 'preserve', 'rename-restart',
278 'coredump-destroy', 'coredump-restart')
279 CONFIG_OLD_DOM_STATES = ('running', 'blocked', 'paused', 'shutdown',
280 'crashed', 'dying')
282 class XendConfigError(VmError):
283 def __str__(self):
284 return 'Invalid Configuration: %s' % str(self.value)
286 ##
287 ## XendConfig Class (an extended dictionary)
288 ##
290 class XendConfig(dict):
291 """ The new Xend VM Configuration.
293 Stores the configuration in xenapi compatible format but retains
294 import and export functions for SXP.
295 """
296 def __init__(self, filename = None, sxp_obj = None,
297 xapi = None, dominfo = None):
299 dict.__init__(self)
300 self.update(self._defaults())
302 if filename:
303 try:
304 sxp_obj = sxp.parse(open(filename,'r'))
305 sxp_obj = sxp_obj[0]
306 except IOError, e:
307 raise XendConfigError("Unable to read file: %s" % filename)
309 if sxp_obj:
310 self._sxp_to_xapi(sxp_obj)
311 self._sxp_to_xapi_unsupported(sxp_obj)
312 elif xapi:
313 self.update_with_xenapi_config(xapi)
314 elif dominfo:
315 # output from xc.domain_getinfo
316 self._dominfo_to_xapi(dominfo, update_mem = True)
318 log.debug('XendConfig.init: %s' % scrub_password(self))
320 # validators go here
321 self.validate()
323 """ In time, we should enable this type checking addition. It is great
324 also for tracking bugs and unintended writes to XendDomainInfo.info
325 def __setitem__(self, key, value):
326 type_conv = XENAPI_CFG_TYPES.get(key)
327 if callable(type_conv):
328 try:
329 dict.__setitem__(self, key, type_conv(value))
330 except (ValueError, TypeError):
331 raise XendConfigError("Wrong type for configuration value " +
332 "%s. Expected %s" %
333 (key, type_conv.__name__))
334 else:
335 dict.__setitem__(self, key, value)
336 """
338 def _defaults(self):
339 defaults = {
340 'name_label': 'Domain-Unnamed',
341 'actions_after_shutdown': 'destroy',
342 'actions_after_reboot': 'restart',
343 'actions_after_crash': 'restart',
344 'actions_after_suspend': '',
345 'is_a_template': False,
346 'is_control_domain': False,
347 'features': '',
348 'PV_bootloader': '',
349 'PV_kernel': '',
350 'PV_ramdisk': '',
351 'PV_args': '',
352 'PV_bootloader_args': '',
353 'HVM_boot_policy': '',
354 'HVM_boot_params': {},
355 'memory_static_min': 0,
356 'memory_dynamic_min': 0,
357 'shadow_memory': 0,
358 'memory_static_max': 0,
359 'memory_dynamic_max': 0,
360 'devices': {},
361 'on_xend_start': 'ignore',
362 'on_xend_stop': 'ignore',
363 'cpus': [],
364 'VCPUs_max': 1,
365 'VCPUs_live': 1,
366 'VCPUs_at_startup': 1,
367 'vcpus_params': {},
368 'console_refs': [],
369 'vif_refs': [],
370 'vbd_refs': [],
371 'vtpm_refs': [],
372 'other_config': {},
373 'platform': {},
374 'target': 0,
375 }
377 return defaults
379 #
380 # Here we assume these values exist in the dict.
381 # If they don't we have a bigger problem, lets not
382 # try and 'fix it up' but acutually fix the cause ;-)
383 #
384 def _memory_sanity_check(self):
385 log.trace("_memory_sanity_check memory_static_min: %s, "
386 "memory_static_max: %i, "
387 "memory_dynamic_min: %i, "
388 "memory_dynamic_max: %i",
389 self["memory_static_min"],
390 self["memory_static_max"],
391 self["memory_dynamic_min"],
392 self["memory_dynamic_max"])
394 if not self["memory_static_min"] <= self["memory_static_max"]:
395 raise XendConfigError("memory_static_min must be less " \
396 "than or equal to memory_static_max")
397 if not self["memory_static_min"] <= self["memory_dynamic_min"]:
398 raise XendConfigError("memory_static_min must be less " \
399 "than or equal to memory_dynamic_min")
400 if not self["memory_dynamic_max"] <= self["memory_static_max"]:
401 raise XendConfigError("memory_dynamic_max must be less " \
402 "than or equal to memory_static_max")
403 if not self["memory_dynamic_max"] > 0:
404 raise XendConfigError("memory_dynamic_max must be greater " \
405 "than zero")
406 if not self["memory_static_max"] > 0:
407 raise XendConfigError("memory_static_max must be greater " \
408 "than zero")
410 def _actions_sanity_check(self):
411 for event in ['shutdown', 'reboot', 'crash']:
412 if self['actions_after_' + event] not in CONFIG_RESTART_MODES:
413 raise XendConfigError('Invalid event handling mode: ' +
414 event)
416 def _vcpus_sanity_check(self):
417 if 'VCPUs_max' in self and 'vcpu_avail' not in self:
418 self['vcpu_avail'] = (1 << self['VCPUs_max']) - 1
420 def _uuid_sanity_check(self):
421 """Make sure UUID is in proper string format with hyphens."""
422 if 'uuid' not in self or not self['uuid']:
423 self['uuid'] = uuid.createString()
424 else:
425 self['uuid'] = uuid.toString(uuid.fromString(self['uuid']))
427 def _name_sanity_check(self):
428 if 'name_label' not in self:
429 self['name_label'] = 'Domain-' + self['uuid']
431 def _platform_sanity_check(self):
432 if 'keymap' not in self['platform'] and XendOptions.instance().get_keymap():
433 self['platform']['keymap'] = XendOptions.instance().get_keymap()
435 if self.is_hvm() or self.has_rfb():
436 if 'device_model' not in self['platform']:
437 self['platform']['device_model'] = xen.util.auxbin.pathTo("qemu-dm")
439 if self.is_hvm():
440 if 'timer_mode' not in self['platform']:
441 self['platform']['timer_mode'] = 0
442 if 'rtc_timeoffset' not in self['platform']:
443 self['platform']['rtc_timeoffset'] = 0
444 if 'hpet' not in self['platform']:
445 self['platform']['hpet'] = 0
446 if 'loader' not in self['platform']:
447 # Old configs may have hvmloader set as PV_kernel param
448 if self.has_key('PV_kernel') and re.search('hvmloader', self['PV_kernel']):
449 self['platform']['loader'] = self['PV_kernel']
450 self['PV_kernel'] = ''
451 else:
452 self['platform']['loader'] = "/usr/lib/xen/boot/hvmloader"
453 log.debug("Loader is %s" % str(self['platform']['loader']))
455 # Compatibility hack, can go away soon.
456 if 'soundhw' not in self['platform'] and \
457 self['platform'].get('enable_audio'):
458 self['platform']['soundhw'] = 'sb16'
460 def validate(self):
461 self._uuid_sanity_check()
462 self._name_sanity_check()
463 self._memory_sanity_check()
464 self._actions_sanity_check()
465 self._vcpus_sanity_check()
466 self._platform_sanity_check()
468 def _dominfo_to_xapi(self, dominfo, update_mem = False):
469 self['domid'] = dominfo['domid']
470 self['online_vcpus'] = dominfo['online_vcpus']
471 self['VCPUs_max'] = dominfo['max_vcpu_id'] + 1
473 if update_mem:
474 self['memory_dynamic_min'] = dominfo['mem_kb'] * 1024
475 self['memory_dynamic_max'] = dominfo['mem_kb'] * 1024
476 self['memory_static_min'] = 0
477 self['memory_static_max'] = dominfo['maxmem_kb'] * 1024
478 self._memory_sanity_check()
480 self['cpu_time'] = dominfo['cpu_time']/1e9
481 if dominfo.get('ssidref'):
482 ssidref = int(dominfo.get('ssidref'))
483 import xen.util.xsm.xsm as security
484 self['security_label'] = security.ssidref2security_label(ssidref)
486 self['shutdown_reason'] = dominfo['shutdown_reason']
488 # parse state into Xen API states
489 self['running'] = dominfo['running']
490 self['crashed'] = dominfo['crashed']
491 self['dying'] = dominfo['dying']
492 self['shutdown'] = dominfo['shutdown']
493 self['paused'] = dominfo['paused']
494 self['blocked'] = dominfo['blocked']
496 if 'name' in dominfo:
497 self['name_label'] = dominfo['name']
499 if 'handle' in dominfo:
500 self['uuid'] = uuid.toString(dominfo['handle'])
502 def parse_cpuid(self, cfg, field):
503 def int2bin(n, count=32):
504 return "".join([str((n >> y) & 1) for y in range(count-1, -1, -1)])
506 for input, regs in cfg[field].iteritems():
507 if not regs is dict:
508 cfg[field][input] = dict(regs)
510 cpuid = {}
511 for input in cfg[field]:
512 inputs = input.split(',')
513 if inputs[0][0:2] == '0x':
514 inputs[0] = str(int(inputs[0], 16))
515 if len(inputs) == 2:
516 if inputs[1][0:2] == '0x':
517 inputs[1] = str(int(inputs[1], 16))
518 new_input = ','.join(inputs)
519 cpuid[new_input] = {} # new input
520 for reg in cfg[field][input]:
521 val = cfg[field][input][reg]
522 if val[0:2] == '0x':
523 cpuid[new_input][reg] = int2bin(int(val, 16))
524 else:
525 cpuid[new_input][reg] = val
526 cfg[field] = cpuid
528 def _parse_sxp(self, sxp_cfg):
529 """ Populate this XendConfig using the parsed SXP.
531 @param sxp_cfg: Parsed SXP Configuration
532 @type sxp_cfg: list of lists
533 @rtype: dictionary
534 @return: A dictionary containing the parsed options of the SXP.
535 """
536 cfg = {}
538 for key, typ in XENAPI_CFG_TYPES.items():
539 val = sxp.child_value(sxp_cfg, key)
540 if val is not None:
541 try:
542 cfg[key] = typ(val)
543 except (ValueError, TypeError), e:
544 log.warn('Unable to convert type value for key: %s' % key)
546 # Convert deprecated options to current equivalents.
548 restart = sxp.child_value(sxp_cfg, 'restart')
549 if restart:
550 if restart == 'onreboot':
551 cfg['on_poweroff'] = 'destroy'
552 cfg['on_reboot'] = 'restart'
553 cfg['on_crash'] = 'destroy'
554 elif restart == 'always':
555 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
556 cfg[opt] = 'restart'
557 elif restart == 'never':
558 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
559 cfg[opt] = 'never'
560 else:
561 log.warn('Ignoring unrecognised value for deprecated option:'
562 'restart = \'%s\'', restart)
564 # Handle memory, passed in as MiB
566 if sxp.child_value(sxp_cfg, "memory") != None:
567 cfg["memory"] = int(sxp.child_value(sxp_cfg, "memory"))
568 if sxp.child_value(sxp_cfg, "maxmem") != None:
569 cfg["maxmem"] = int(sxp.child_value(sxp_cfg, "maxmem"))
571 # Convert scheduling parameters to vcpus_params
572 if 'vcpus_params' not in cfg:
573 cfg['vcpus_params'] = {}
574 cfg["vcpus_params"]["weight"] = \
575 int(sxp.child_value(sxp_cfg, "cpu_weight", 256))
576 cfg["vcpus_params"]["cap"] = \
577 int(sxp.child_value(sxp_cfg, "cpu_cap", 0))
579 # Only extract options we know about.
580 extract_keys = LEGACY_UNSUPPORTED_BY_XENAPI_CFG + \
581 XENAPI_CFG_TO_LEGACY_CFG.values()
583 for key in extract_keys:
584 val = sxp.child_value(sxp_cfg, key)
585 if val != None:
586 try:
587 cfg[key] = LEGACY_CFG_TYPES[key](val)
588 except KeyError:
589 cfg[key] = val
590 except (TypeError, ValueError), e:
591 log.warn("Unable to parse key %s: %s: %s" %
592 (key, str(val), e))
594 if 'platform' not in cfg:
595 cfg['platform'] = {}
596 localtime = sxp.child_value(sxp_cfg, 'localtime')
597 if localtime is not None:
598 cfg['platform']['localtime'] = localtime
600 # Compatibility hack -- can go soon.
601 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
602 val = sxp.child_value(sxp_cfg, "platform_" + key, None)
603 if val is not None:
604 self['platform'][key] = val
606 # Compatibility hack -- can go soon.
607 boot_order = sxp.child_value(sxp_cfg, 'HVM_boot')
608 if boot_order:
609 cfg['HVM_boot_policy'] = 'BIOS order'
610 cfg['HVM_boot_params'] = { 'order' : boot_order }
613 # Parsing the device SXP's.
614 cfg['devices'] = {}
615 for dev in sxp.children(sxp_cfg, 'device'):
616 config = sxp.child0(dev)
617 dev_type = sxp.name(config)
618 self.device_add(dev_type, cfg_sxp = config, target = cfg)
620 # Extract missing data from configuration entries
621 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
622 if image_sxp:
623 image_vcpus = sxp.child_value(image_sxp, 'vcpus')
624 if image_vcpus != None:
625 try:
626 if 'VCPUs_max' not in cfg:
627 cfg['VCPUs_max'] = int(image_vcpus)
628 elif cfg['VCPUs_max'] != int(image_vcpus):
629 cfg['VCPUs_max'] = int(image_vcpus)
630 log.warn('Overriding vcpus from %d to %d using image'
631 'vcpus value.', cfg['VCPUs_max'])
632 except ValueError, e:
633 raise XendConfigError('integer expeceted: %s: %s' %
634 image_sxp, e)
636 # Deprecated cpu configuration
637 if 'cpu' in cfg:
638 if 'cpus' in cfg:
639 cfg['cpus'] = "%s,%s" % (str(cfg['cpu']), cfg['cpus'])
640 else:
641 cfg['cpus'] = str(cfg['cpu'])
643 # Convert 'cpus' to list of list of ints
644 cpus_list = []
645 if 'cpus' in cfg:
646 # Convert the following string to list of ints.
647 # The string supports a list of ranges (0-3),
648 # seperated by commas, and negation (^1).
649 # Precedence is settled by order of the string:
650 # "0-3,^1" -> [0,2,3]
651 # "0-3,^1,1" -> [0,1,2,3]
652 def cnv(s):
653 l = []
654 for c in s.split(','):
655 if c.find('-') != -1:
656 (x, y) = c.split('-')
657 for i in range(int(x), int(y)+1):
658 l.append(int(i))
659 else:
660 # remove this element from the list
661 if c[0] == '^':
662 l = [x for x in l if x != int(c[1:])]
663 else:
664 l.append(int(c))
665 return l
667 if type(cfg['cpus']) == list:
668 if len(cfg['cpus']) > 0 and type(cfg['cpus'][0]) == list:
669 # If sxp_cfg was created from config.sxp,
670 # the form of 'cpus' is list of list of string.
671 # Convert 'cpus' to list of list of ints.
672 # Conversion examples:
673 # [['1']] -> [[1]]
674 # [['0','2'],['1','3']] -> [[0,2],[1,3]]
675 try:
676 for c1 in cfg['cpus']:
677 cpus = []
678 for c2 in c1:
679 cpus.append(int(c2))
680 cpus_list.append(cpus)
681 except ValueError, e:
682 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
683 else:
684 # Conversion examples:
685 # ["1"] -> [[1]]
686 # ["0,2","1,3"] -> [[0,2],[1,3]]
687 # ["0-3,^1","1-4,^2"] -> [[0,2,3],[1,3,4]]
688 try:
689 for c in cfg['cpus']:
690 cpus = cnv(c)
691 cpus_list.append(cpus)
692 except ValueError, e:
693 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
695 if len(cpus_list) != cfg['vcpus']:
696 raise XendConfigError('vcpus and the item number of cpus are not same')
697 else:
698 # Conversion examples:
699 # vcpus=1:
700 # "1" -> [[1]]
701 # "0-3,^1" -> [[0,2,3]]
702 # vcpus=2:
703 # "1" -> [[1],[1]]
704 # "0-3,^1" -> [[0,2,3],[0,2,3]]
705 try:
706 cpus = cnv(cfg['cpus'])
707 for v in range(0, cfg['vcpus']):
708 cpus_list.append(cpus)
709 except ValueError, e:
710 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
711 else:
712 # Generation examples:
713 # vcpus=1:
714 # -> [[]]
715 # vcpus=2:
716 # -> [[],[]]
717 for v in range(0, cfg['vcpus']):
718 cpus_list.append(list())
720 cfg['cpus'] = cpus_list
722 # Parse cpuid
723 if 'cpuid' in cfg:
724 self.parse_cpuid(cfg, 'cpuid')
725 if 'cpuid_check' in cfg:
726 self.parse_cpuid(cfg, 'cpuid_check')
728 import xen.util.xsm.xsm as security
729 if security.on() == xsconstants.XS_POLICY_ACM:
730 from xen.util.acmpolicy import ACM_LABEL_UNLABELED
731 if not 'security' in cfg and sxp.child_value(sxp_cfg, 'security'):
732 cfg['security'] = sxp.child_value(sxp_cfg, 'security')
733 elif not cfg.get('security_label'):
734 cfg['security'] = [['access_control',
735 ['policy', security.get_active_policy_name() ],
736 ['label', ACM_LABEL_UNLABELED ]]]
738 if 'security' in cfg and not cfg.get('security_label'):
739 secinfo = cfg['security']
740 # The xm command sends a list formatted like this:
741 # [['access_control', ['policy', 'xm-test'],['label', 'red']],
742 # ['ssidref', 196611]]
743 policy = ""
744 label = ""
745 for idx in range(0, len(secinfo)):
746 if secinfo[idx][0] == "access_control":
747 for aidx in range(1, len(secinfo[idx])):
748 if secinfo[idx][aidx][0] == "policy":
749 policy = secinfo[idx][aidx][1]
750 if secinfo[idx][aidx][0] == "label":
751 label = secinfo[idx][aidx][1]
752 cfg['security_label'] = \
753 security.set_security_label(policy, label)
754 if not sxp.child_value(sxp_cfg, 'security_label'):
755 del cfg['security']
757 sec_lab = cfg['security_label'].split(":")
758 if len(sec_lab) != 3:
759 raise XendConfigError("Badly formatted security label: %s"
760 % cfg['security_label'])
762 old_state = sxp.child_value(sxp_cfg, 'state')
763 if old_state:
764 for i in range(len(CONFIG_OLD_DOM_STATES)):
765 cfg[CONFIG_OLD_DOM_STATES[i]] = int(old_state[i] != '-')
767 return cfg
770 def _sxp_to_xapi(self, sxp_cfg):
771 """Read in an SXP Configuration object and
772 populate at much of the Xen API with valid values.
773 """
774 log.debug('_sxp_to_xapi(%s)' % scrub_password(sxp_cfg))
776 cfg = self._parse_sxp(sxp_cfg)
778 for key, typ in XENAPI_CFG_TYPES.items():
779 val = cfg.get(key)
780 if val is not None:
781 self[key] = typ(val)
783 # Convert parameters that can be directly mapped from
784 # the Legacy Config to Xen API Config
786 for apikey, cfgkey in XENAPI_CFG_TO_LEGACY_CFG.items():
787 try:
788 type_conv = XENAPI_CFG_TYPES.get(apikey)
789 if callable(type_conv):
790 self[apikey] = type_conv(cfg[cfgkey])
791 else:
792 log.warn("Unconverted key: " + apikey)
793 self[apikey] = cfg[cfgkey]
794 except KeyError:
795 pass
797 # Lets try and handle memory correctly
799 MiB = 1024 * 1024
801 if "memory" in cfg:
802 self["memory_static_min"] = 0
803 self["memory_static_max"] = int(cfg["memory"]) * MiB
804 self["memory_dynamic_min"] = int(cfg["memory"]) * MiB
805 self["memory_dynamic_max"] = int(cfg["memory"]) * MiB
807 if "maxmem" in cfg:
808 self["memory_static_max"] = int(cfg["maxmem"]) * MiB
810 self._memory_sanity_check()
812 def update_with(n, o):
813 if not self.get(n):
814 self[n] = cfg.get(o, '')
816 update_with('PV_bootloader', 'bootloader')
817 update_with('PV_bootloader_args', 'bootloader_args')
819 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
820 if image_sxp:
821 self.update_with_image_sxp(image_sxp)
823 # Convert Legacy HVM parameters to Xen API configuration
824 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
825 if key in cfg:
826 self['platform'][key] = cfg[key]
828 # set device references in the configuration
829 self['devices'] = cfg.get('devices', {})
830 self['console_refs'] = cfg.get('console_refs', [])
831 self['vif_refs'] = cfg.get('vif_refs', [])
832 self['vbd_refs'] = cfg.get('vbd_refs', [])
833 self['vtpm_refs'] = cfg.get('vtpm_refs', [])
835 # coalesce hvm vnc frame buffer with vfb config
836 if self.is_hvm() and int(self['platform'].get('vnc', 0)) != 0:
837 # add vfb device if it isn't there already
838 if not self.has_rfb():
839 dev_config = ['vfb']
840 dev_config.append(['type', 'vnc'])
841 # copy VNC related params from platform config to vfb dev conf
842 for key in ['vncpasswd', 'vncunused', 'vncdisplay',
843 'vnclisten']:
844 if key in self['platform']:
845 dev_config.append([key, self['platform'][key]])
847 self.device_add('vfb', cfg_sxp = dev_config)
850 def has_rfb(self):
851 for console_uuid in self['console_refs']:
852 if self['devices'][console_uuid][1].get('protocol') == 'rfb':
853 return True
854 if self['devices'][console_uuid][0] == 'vfb':
855 return True
856 return False
858 def _sxp_to_xapi_unsupported(self, sxp_cfg):
859 """Read in an SXP configuration object and populate
860 values are that not related directly supported in
861 the Xen API.
862 """
864 log.debug('_sxp_to_xapi_unsupported(%s)' % scrub_password(sxp_cfg))
866 # Parse and convert parameters used to configure
867 # the image (as well as HVM images)
868 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
869 if image_sxp:
870 image_type = sxp.name(image_sxp)
871 if image_type != 'hvm' and image_type != 'linux':
872 self['platform']['image_type'] = image_type
874 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
875 val = sxp.child_value(image_sxp, key, None)
876 if val is not None and val != '':
877 self['platform'][key] = val
879 notes = sxp.children(image_sxp, 'notes')
880 if notes:
881 self['notes'] = self.notes_from_sxp(notes[0])
883 self._hvm_boot_params_from_sxp(image_sxp)
885 # extract backend value
887 backend = []
888 for c in sxp.children(sxp_cfg, 'backend'):
889 backend.append(sxp.name(sxp.child0(c)))
890 if backend:
891 self['backend'] = backend
893 # Parse and convert other Non Xen API parameters.
894 def _set_cfg_if_exists(sxp_arg):
895 val = sxp.child_value(sxp_cfg, sxp_arg)
896 if val != None:
897 if LEGACY_CFG_TYPES.get(sxp_arg):
898 self[sxp_arg] = LEGACY_CFG_TYPES[sxp_arg](val)
899 else:
900 self[sxp_arg] = val
902 _set_cfg_if_exists('shadow_memory')
903 _set_cfg_if_exists('features')
904 _set_cfg_if_exists('on_xend_stop')
905 _set_cfg_if_exists('on_xend_start')
906 _set_cfg_if_exists('vcpu_avail')
908 # Parse and store runtime configuration
909 _set_cfg_if_exists('start_time')
910 _set_cfg_if_exists('cpu_time')
911 _set_cfg_if_exists('shutdown_reason')
912 _set_cfg_if_exists('up_time')
913 _set_cfg_if_exists('status') # TODO, deprecated
915 def _get_old_state_string(self):
916 """Returns the old xm state string.
917 @rtype: string
918 @return: old state string
919 """
920 state_string = ''
921 for state_name in CONFIG_OLD_DOM_STATES:
922 on_off = self.get(state_name, 0)
923 if on_off:
924 state_string += state_name[0]
925 else:
926 state_string += '-'
928 return state_string
931 def update_config(self, dominfo):
932 """Update configuration with the output from xc.domain_getinfo().
934 @param dominfo: Domain information via xc.domain_getinfo()
935 @type dominfo: dict
936 """
937 self._dominfo_to_xapi(dominfo)
938 self.validate()
940 def update_with_xenapi_config(self, xapi):
941 """Update configuration with a Xen API VM struct
943 @param xapi: Xen API VM Struct
944 @type xapi: dict
945 """
947 log.debug('update_with_xenapi_config: %s' % scrub_password(xapi))
949 for key, val in xapi.items():
950 type_conv = XENAPI_CFG_TYPES.get(key)
951 if type_conv is None:
952 key = key.lower()
953 type_conv = XENAPI_CFG_TYPES.get(key)
954 if callable(type_conv):
955 self[key] = type_conv(val)
956 else:
957 self[key] = val
959 # XenAPI defines platform as a string-string map. If platform
960 # configuration exists, convert values to appropriate type.
961 if 'platform' in xapi:
962 for key, val in xapi['platform'].items():
963 type_conv = XENAPI_PLATFORM_CFG_TYPES.get(key)
964 if type_conv is None:
965 key = key.lower()
966 type_conv = XENAPI_PLATFORM_CFG_TYPES.get(key)
967 if callable(type_conv):
968 self['platform'][key] = type_conv(val)
969 else:
970 self['platform'][key] = val
972 self['vcpus_params']['weight'] = \
973 int(self['vcpus_params'].get('weight', 256))
974 self['vcpus_params']['cap'] = int(self['vcpus_params'].get('cap', 0))
976 def cpuid_to_sxp(self, sxpr, field):
977 regs_list = []
978 for input, regs in self[field].iteritems():
979 reg_list = []
980 for reg, val in regs.iteritems():
981 reg_list.append([reg, val])
982 regs_list.append([input, reg_list])
983 sxpr.append([field, regs_list])
986 def to_sxp(self, domain = None, ignore_devices = False, ignore = [],
987 legacy_only = True):
988 """ Get SXP representation of this config object.
990 Incompat: removed store_mfn, console_mfn
992 @keyword domain: (optional) XendDomainInfo to get extra information
993 from such as domid and running devices.
994 @type domain: XendDomainInfo
995 @keyword ignore: (optional) list of 'keys' that we do not want
996 to export.
997 @type ignore: list of strings
998 @rtype: list of list (SXP representation)
999 """
1000 sxpr = ['domain']
1002 # TODO: domid/dom is the same thing but called differently
1003 # depending if it is from xenstore or sxpr.
1005 if domain.getDomid() is not None:
1006 sxpr.append(['domid', domain.getDomid()])
1008 if not legacy_only:
1009 for name, typ in XENAPI_CFG_TYPES.items():
1010 if name in self and self[name] not in (None, []):
1011 if typ == dict:
1012 s = self[name].items()
1013 elif typ == list:
1014 s = self[name]
1015 else:
1016 s = str(self[name])
1017 sxpr.append([name, s])
1019 for xenapi, legacy in XENAPI_CFG_TO_LEGACY_CFG.items():
1020 if legacy in ('cpus'): # skip this
1021 continue
1022 if self.has_key(xenapi) and self[xenapi] not in (None, []):
1023 if type(self[xenapi]) == bool:
1024 # convert booleans to ints before making an sxp item
1025 sxpr.append([legacy, int(self[xenapi])])
1026 else:
1027 sxpr.append([legacy, self[xenapi]])
1029 MiB = 1024*1024
1031 sxpr.append(["maxmem", int(self["memory_static_max"])/MiB])
1032 sxpr.append(["memory", int(self["memory_dynamic_max"])/MiB])
1034 for legacy in LEGACY_UNSUPPORTED_BY_XENAPI_CFG:
1035 if legacy in ('domid', 'uuid', 'cpus'): # skip these
1036 continue
1037 if self.has_key(legacy) and self[legacy] not in (None, []):
1038 sxpr.append([legacy, self[legacy]])
1040 if self.has_key('security_label'):
1041 sxpr.append(['security_label', self['security_label']])
1043 sxpr.append(['image', self.image_sxpr()])
1044 sxpr.append(['status', domain._stateGet()])
1046 if domain.getDomid() is not None:
1047 sxpr.append(['state', self._get_old_state_string()])
1049 if domain:
1050 if domain.store_mfn:
1051 sxpr.append(['store_mfn', domain.store_mfn])
1052 if domain.console_mfn:
1053 sxpr.append(['console_mfn', domain.console_mfn])
1056 # Marshall devices (running or from configuration)
1057 if not ignore_devices:
1058 txn = xstransact()
1059 try:
1060 for cls in XendDevices.valid_devices():
1061 found = False
1063 # figure if there is a dev controller is valid and running
1064 if domain and domain.getDomid() != None:
1065 try:
1066 controller = domain.getDeviceController(cls)
1067 configs = controller.configurations(txn)
1068 for config in configs:
1069 if sxp.name(config) in ('vbd', 'tap'):
1070 # The bootable flag is never written to the
1071 # store as part of the device config.
1072 dev_uuid = sxp.child_value(config, 'uuid')
1073 dev_type, dev_cfg = self['devices'][dev_uuid]
1074 is_bootable = dev_cfg.get('bootable', 0)
1075 config.append(['bootable', int(is_bootable)])
1076 config.append(['VDI', dev_cfg.get('VDI', '')])
1078 sxpr.append(['device', config])
1080 found = True
1081 except:
1082 log.exception("dumping sxp from device controllers")
1083 pass
1085 # if we didn't find that device, check the existing config
1086 # for a device in the same class
1087 if not found:
1088 for dev_type, dev_info in self.all_devices_sxpr():
1089 if dev_type == cls:
1090 sxpr.append(['device', dev_info])
1092 txn.commit()
1093 except:
1094 txn.abort()
1095 raise
1097 if 'cpuid' in self:
1098 self.cpuid_to_sxp(sxpr, 'cpuid')
1099 if 'cpuid_check' in self:
1100 self.cpuid_to_sxp(sxpr, 'cpuid_check')
1102 log.debug(sxpr)
1104 return sxpr
1106 def _blkdev_name_to_number(self, dev):
1107 if 'ioemu:' in dev:
1108 _, dev = dev.split(':', 1)
1109 try:
1110 dev, _ = dev.split(':', 1)
1111 except ValueError:
1112 pass
1114 try:
1115 devid = int(dev)
1116 except ValueError:
1117 # devid is not a number but a string containing either device
1118 # name (e.g. xvda) or device_type/device_id (e.g. vbd/51728)
1119 dev2 = type(dev) is str and dev.split('/')[-1] or None
1120 if dev2 == None:
1121 log.debug("Could not check the device %s", dev)
1122 return None
1123 try:
1124 devid = int(dev2)
1125 except ValueError:
1126 devid = blkdev_name_to_number(dev2)
1127 if devid == None:
1128 log.debug("The device %s is not device name", dev2)
1129 return None
1130 return devid
1132 def device_duplicate_check(self, dev_type, dev_info, defined_config):
1133 defined_devices_sxpr = self.all_devices_sxpr(target = defined_config)
1135 if dev_type == 'vbd' or dev_type == 'tap':
1136 dev_uname = dev_info.get('uname')
1137 blkdev_name = dev_info.get('dev')
1138 devid = self._blkdev_name_to_number(blkdev_name)
1139 if devid == None or dev_uname == None:
1140 return
1142 for o_dev_type, o_dev_info in defined_devices_sxpr:
1143 if o_dev_type == 'vbd' or o_dev_type == 'tap':
1144 blkdev_file = blkdev_uname_to_file(dev_uname)
1145 o_dev_uname = sxp.child_value(o_dev_info, 'uname')
1146 o_blkdev_file = blkdev_uname_to_file(o_dev_uname)
1147 if blkdev_file == o_blkdev_file:
1148 raise XendConfigError('The file "%s" is already used' %
1149 blkdev_file)
1150 o_blkdev_name = sxp.child_value(o_dev_info, 'dev')
1151 o_devid = self._blkdev_name_to_number(o_blkdev_name)
1152 if o_devid != None and devid == o_devid:
1153 raise XendConfigError('The device "%s" is already defined' %
1154 blkdev_name)
1156 elif dev_type == 'vif':
1157 dev_mac = dev_info.get('mac')
1159 for o_dev_type, o_dev_info in defined_devices_sxpr:
1160 if dev_type == o_dev_type:
1161 if dev_mac.lower() == sxp.child_value(o_dev_info, 'mac').lower():
1162 raise XendConfigError('The mac "%s" is already defined' %
1163 dev_mac)
1165 def device_add(self, dev_type, cfg_sxp = None, cfg_xenapi = None,
1166 target = None):
1167 """Add a device configuration in SXP format or XenAPI struct format.
1169 For SXP, it could be either:
1171 [device, [vbd, [uname ...]]
1173 or:
1175 [vbd, [uname ..]]
1177 @type cfg_sxp: list of lists (parsed sxp object)
1178 @param cfg_sxp: SXP configuration object
1179 @type cfg_xenapi: dict
1180 @param cfg_xenapi: A device configuration from Xen API (eg. vbd,vif)
1181 @param target: write device information to
1182 @type target: None or a dictionary
1183 @rtype: string
1184 @return: Assigned UUID of the device.
1185 """
1186 if target == None:
1187 target = self
1189 if dev_type not in XendDevices.valid_devices():
1190 raise XendConfigError("XendConfig: %s not a valid device type" %
1191 dev_type)
1193 if cfg_sxp == None and cfg_xenapi == None:
1194 raise XendConfigError("XendConfig: device_add requires some "
1195 "config.")
1197 #if cfg_sxp:
1198 # log.debug("XendConfig.device_add: %s" % str(cfg_sxp))
1199 #if cfg_xenapi:
1200 # log.debug("XendConfig.device_add: %s" % str(cfg_xenapi))
1202 if cfg_sxp:
1203 if sxp.child0(cfg_sxp) == 'device':
1204 config = sxp.child0(cfg_sxp)
1205 else:
1206 config = cfg_sxp
1208 dev_type = sxp.name(config)
1209 dev_info = {}
1211 # Parsing the device SXP's. In most cases, the SXP looks
1212 # like this:
1214 # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]]
1216 # However, for PCI devices it looks like this:
1218 # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1]]]]
1220 # It seems the reasoning for this difference is because
1221 # pciif.py needs all the PCI device configurations at
1222 # the same time when creating the devices.
1224 # To further complicate matters, Xen 2.0 configuration format
1225 # uses the following for pci device configuration:
1227 # [device, [pci, [domain, 0], [bus, 0], [dev, 1], [func, 2]]]
1229 if dev_type == 'pci':
1230 pci_devs_uuid = sxp.child_value(config, 'uuid',
1231 uuid.createString())
1232 pci_devs = []
1233 for pci_dev in sxp.children(config, 'dev'):
1234 pci_dev_info = {}
1235 for opt_val in pci_dev[1:]:
1236 try:
1237 opt, val = opt_val
1238 pci_dev_info[opt] = val
1239 except TypeError:
1240 pass
1241 pci_devs.append(pci_dev_info)
1242 target['devices'][pci_devs_uuid] = (dev_type,
1243 {'devs': pci_devs,
1244 'uuid': pci_devs_uuid})
1246 log.debug("XendConfig: reading device: %s" % pci_devs)
1247 return pci_devs_uuid
1249 for opt_val in config[1:]:
1250 try:
1251 opt, val = opt_val
1252 dev_info[opt] = val
1253 except (TypeError, ValueError): # unpack error
1254 pass
1256 if dev_type == 'vbd':
1257 dev_info['bootable'] = 0
1258 if dev_info.get('dev', '').startswith('ioemu:'):
1259 dev_info['driver'] = 'ioemu'
1260 else:
1261 dev_info['driver'] = 'paravirtualised'
1263 if dev_type == 'tap':
1264 if dev_info['uname'].split(':')[1] not in blktap_disk_types:
1265 raise XendConfigError("tap:%s not a valid disk type" %
1266 dev_info['uname'].split(':')[1])
1268 if dev_type == 'vif':
1269 if not dev_info.get('mac'):
1270 dev_info['mac'] = randomMAC()
1272 self.device_duplicate_check(dev_type, dev_info, target)
1274 if dev_type == 'vif':
1275 if dev_info.get('policy') and dev_info.get('label'):
1276 dev_info['security_label'] = "%s:%s:%s" % \
1277 (xsconstants.ACM_POLICY_ID,
1278 dev_info['policy'],dev_info['label'])
1280 # create uuid if it doesn't exist
1281 dev_uuid = dev_info.get('uuid', None)
1282 if not dev_uuid:
1283 dev_uuid = uuid.createString()
1284 dev_info['uuid'] = dev_uuid
1286 # store dev references by uuid for certain device types
1287 target['devices'][dev_uuid] = (dev_type, dev_info)
1288 if dev_type in ('vif', 'vbd', 'vtpm'):
1289 param = '%s_refs' % dev_type
1290 if param not in target:
1291 target[param] = []
1292 if dev_uuid not in target[param]:
1293 if dev_type == 'vbd':
1294 # Compat hack -- mark first disk bootable
1295 dev_info['bootable'] = int(not target[param])
1296 target[param].append(dev_uuid)
1297 elif dev_type == 'tap':
1298 if 'vbd_refs' not in target:
1299 target['vbd_refs'] = []
1300 if dev_uuid not in target['vbd_refs']:
1301 # Compat hack -- mark first disk bootable
1302 dev_info['bootable'] = int(not target['vbd_refs'])
1303 target['vbd_refs'].append(dev_uuid)
1305 elif dev_type == 'vfb':
1306 # Populate other config with aux data that is associated
1307 # with vfb
1309 other_config = {}
1310 for key in XENAPI_CONSOLE_OTHER_CFG:
1311 if key in dev_info:
1312 other_config[key] = dev_info[key]
1313 target['devices'][dev_uuid][1]['other_config'] = other_config
1316 if 'console_refs' not in target:
1317 target['console_refs'] = []
1319 # Treat VFB devices as console devices so they are found
1320 # through Xen API
1321 if dev_uuid not in target['console_refs']:
1322 target['console_refs'].append(dev_uuid)
1324 elif dev_type == 'console':
1325 if 'console_refs' not in target:
1326 target['console_refs'] = []
1327 if dev_uuid not in target['console_refs']:
1328 target['console_refs'].append(dev_uuid)
1330 log.debug("XendConfig: reading device: %s" % scrub_password(dev_info))
1331 return dev_uuid
1333 if cfg_xenapi:
1334 dev_info = {}
1335 dev_uuid = ''
1336 if dev_type == 'vif':
1337 dev_info['mac'] = cfg_xenapi.get('MAC')
1338 if not dev_info['mac']:
1339 dev_info['mac'] = randomMAC()
1340 # vifname is the name on the guest, not dom0
1341 # TODO: we don't have the ability to find that out or
1342 # change it from dom0
1343 #if cfg_xenapi.get('device'): # don't add if blank
1344 # dev_info['vifname'] = cfg_xenapi.get('device')
1345 if cfg_xenapi.get('type'):
1346 dev_info['type'] = cfg_xenapi.get('type')
1347 if cfg_xenapi.get('name'):
1348 dev_info['name'] = cfg_xenapi.get('name')
1349 if cfg_xenapi.get('network'):
1350 network = XendAPIStore.get(
1351 cfg_xenapi.get('network'), 'network')
1352 dev_info['bridge'] = network.get_name_label()
1354 if cfg_xenapi.get('security_label'):
1355 dev_info['security_label'] = \
1356 cfg_xenapi.get('security_label')
1358 dev_uuid = cfg_xenapi.get('uuid', None)
1359 if not dev_uuid:
1360 dev_uuid = uuid.createString()
1361 dev_info['uuid'] = dev_uuid
1362 target['devices'][dev_uuid] = (dev_type, dev_info)
1363 target['vif_refs'].append(dev_uuid)
1365 elif dev_type in ('vbd', 'tap'):
1366 dev_info['type'] = cfg_xenapi.get('type', 'Disk')
1367 if dev_info['type'] == 'CD':
1368 old_vbd_type = 'cdrom'
1369 else:
1370 old_vbd_type = 'disk'
1372 dev_info['uname'] = cfg_xenapi.get('image', '')
1373 dev_info['dev'] = '%s:%s' % (cfg_xenapi.get('device'),
1374 old_vbd_type)
1375 dev_info['bootable'] = int(cfg_xenapi.get('bootable', 0))
1376 dev_info['driver'] = cfg_xenapi.get('driver', '')
1377 dev_info['VDI'] = cfg_xenapi.get('VDI', '')
1379 if cfg_xenapi.get('mode') == 'RW':
1380 dev_info['mode'] = 'w'
1381 else:
1382 dev_info['mode'] = 'r'
1384 dev_uuid = cfg_xenapi.get('uuid', None)
1385 if not dev_uuid:
1386 dev_uuid = uuid.createString()
1387 dev_info['uuid'] = dev_uuid
1388 target['devices'][dev_uuid] = (dev_type, dev_info)
1389 target['vbd_refs'].append(dev_uuid)
1391 elif dev_type == 'vtpm':
1392 if cfg_xenapi.get('type'):
1393 dev_info['type'] = cfg_xenapi.get('type')
1395 dev_uuid = cfg_xenapi.get('uuid', None)
1396 if not dev_uuid:
1397 dev_uuid = uuid.createString()
1398 dev_info['uuid'] = dev_uuid
1399 dev_info['other_config'] = cfg_xenapi.get('other_config', {})
1400 target['devices'][dev_uuid] = (dev_type, dev_info)
1401 target['vtpm_refs'].append(dev_uuid)
1403 elif dev_type == 'console':
1404 dev_uuid = cfg_xenapi.get('uuid', None)
1405 if not dev_uuid:
1406 dev_uuid = uuid.createString()
1407 dev_info['uuid'] = dev_uuid
1408 dev_info['protocol'] = cfg_xenapi.get('protocol', 'rfb')
1409 dev_info['other_config'] = cfg_xenapi.get('other_config', {})
1410 if dev_info['protocol'] == 'rfb':
1411 # collapse other config into devinfo for things
1412 # such as vncpasswd, vncunused, etc.
1413 dev_info.update(cfg_xenapi.get('other_config', {}))
1414 dev_info['type'] = 'vnc'
1415 target['devices'][dev_uuid] = ('vfb', dev_info)
1416 target['console_refs'].append(dev_uuid)
1418 # if console is rfb, set device_model ensuring qemu
1419 # is invoked for pvfb services
1420 if 'device_model' not in target['platform']:
1421 target['platform']['device_model'] = \
1422 xen.util.auxbin.pathTo("qemu-dm")
1424 # Finally, if we are a pvfb, we need to make a vkbd
1425 # as well that is not really exposed to Xen API
1426 vkbd_uuid = uuid.createString()
1427 target['devices'][vkbd_uuid] = ('vkbd', {})
1429 elif dev_info['protocol'] == 'vt100':
1430 # if someone tries to create a VT100 console
1431 # via the Xen API, we'll have to ignore it
1432 # because we create one automatically in
1433 # XendDomainInfo._update_consoles
1434 raise XendConfigError('Creating vt100 consoles via '
1435 'Xen API is unsupported')
1437 return dev_uuid
1439 # no valid device to add
1440 return ''
1442 def phantom_device_add(self, dev_type, cfg_xenapi = None,
1443 target = None):
1444 """Add a phantom tap device configuration in XenAPI struct format.
1445 """
1447 if target == None:
1448 target = self
1450 if dev_type not in XendDevices.valid_devices() and \
1451 dev_type not in XendDevices.pseudo_devices():
1452 raise XendConfigError("XendConfig: %s not a valid device type" %
1453 dev_type)
1455 if cfg_xenapi == None:
1456 raise XendConfigError("XendConfig: device_add requires some "
1457 "config.")
1459 if cfg_xenapi:
1460 log.debug("XendConfig.phantom_device_add: %s" % str(cfg_xenapi))
1462 if cfg_xenapi:
1463 dev_info = {}
1464 if dev_type in ('vbd', 'tap'):
1465 if dev_type == 'vbd':
1466 dev_info['uname'] = cfg_xenapi.get('image', '')
1467 dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
1468 elif dev_type == 'tap':
1469 if cfg_xenapi.get('image').find('tap:') == -1:
1470 dev_info['uname'] = 'tap:qcow:%s' % cfg_xenapi.get('image')
1471 dev_info['dev'] = '/dev/%s' % cfg_xenapi.get('device')
1472 dev_info['uname'] = cfg_xenapi.get('image')
1473 dev_info['mode'] = cfg_xenapi.get('mode')
1474 dev_info['backend'] = '0'
1475 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
1476 dev_info['uuid'] = dev_uuid
1477 self['devices'][dev_uuid] = (dev_type, dev_info)
1478 self['vbd_refs'].append(dev_uuid)
1479 return dev_uuid
1481 return ''
1483 def console_add(self, protocol, location, other_config = {}):
1484 dev_uuid = uuid.createString()
1485 if protocol == 'vt100':
1486 dev_info = {
1487 'uuid': dev_uuid,
1488 'protocol': protocol,
1489 'location': location,
1490 'other_config': other_config,
1493 if 'devices' not in self:
1494 self['devices'] = {}
1496 self['devices'][dev_uuid] = ('console', dev_info)
1497 self['console_refs'].append(dev_uuid)
1498 return dev_info
1500 return {}
1502 def console_update(self, console_uuid, key, value):
1503 for dev_uuid, (dev_type, dev_info) in self['devices'].items():
1504 if dev_uuid == console_uuid:
1505 dev_info[key] = value
1506 # collapse other_config into dev_info for things
1507 # such as vncpasswd, vncunused, etc.
1508 if key == 'other_config':
1509 for k in XENAPI_CONSOLE_OTHER_CFG:
1510 if k in dev_info and k not in value:
1511 del dev_info[k]
1512 dev_info.update(value)
1513 break
1515 def console_get_all(self, protocol):
1516 if protocol == 'vt100':
1517 consoles = [dinfo for dtype, dinfo in self['devices'].values()
1518 if dtype == 'console']
1519 return [c for c in consoles if c.get('protocol') == protocol]
1521 elif protocol == 'rfb':
1522 vfbs = [dinfo for dtype, dinfo in self['devices'].values()
1523 if dtype == 'vfb']
1525 # move all non-console key values to other_config before
1526 # returning console config
1527 valid_keys = ['uuid', 'location']
1528 for vfb in vfbs:
1529 other_config = {}
1530 for key, val in vfb.items():
1531 if key not in valid_keys:
1532 other_config[key] = vfb[key]
1533 del vfb[key]
1534 vfb['other_config'] = other_config
1535 vfb['protocol'] = 'rfb'
1537 return vfbs
1539 else:
1540 return []
1542 def device_update(self, dev_uuid, cfg_sxp = [], cfg_xenapi = {}):
1543 """Update an existing device with the new configuration.
1545 @rtype: boolean
1546 @return: Returns True if succesfully found and updated a device conf
1547 """
1548 if dev_uuid in self['devices'] and cfg_sxp:
1549 if sxp.child0(cfg_sxp) == 'device':
1550 config = sxp.child0(cfg_sxp)
1551 else:
1552 config = cfg_sxp
1554 dev_type, dev_info = self['devices'][dev_uuid]
1556 if dev_type == 'pci': # Special case for pci
1557 pci_devs = []
1558 for pci_dev in sxp.children(config, 'dev'):
1559 pci_dev_info = {}
1560 for opt_val in pci_dev[1:]:
1561 try:
1562 opt, val = opt_val
1563 pci_dev_info[opt] = val
1564 except TypeError:
1565 pass
1566 pci_devs.append(pci_dev_info)
1567 self['devices'][dev_uuid] = (dev_type,
1568 {'devs': pci_devs,
1569 'uuid': dev_uuid})
1570 return True
1572 for opt_val in config[1:]:
1573 try:
1574 opt, val = opt_val
1575 dev_info[opt] = val
1576 except (TypeError, ValueError):
1577 pass # no value for this config option
1579 self['devices'][dev_uuid] = (dev_type, dev_info)
1580 return True
1582 elif dev_uuid in self['devices'] and cfg_xenapi:
1583 dev_type, dev_info = self['devices'][dev_uuid]
1584 for key, val in cfg_xenapi.items():
1585 dev_info[key] = val
1586 self['devices'][dev_uuid] = (dev_type, dev_info)
1588 return False
1591 def device_sxpr(self, dev_uuid = None, dev_type = None, dev_info = None, target = None):
1592 """Get Device SXPR by either giving the device UUID or (type, config).
1594 @rtype: list of lists
1595 @return: device config sxpr
1596 """
1597 sxpr = []
1599 if target == None:
1600 target = self
1602 if dev_uuid != None and dev_uuid in target['devices']:
1603 dev_type, dev_info = target['devices'][dev_uuid]
1605 if dev_type == None or dev_info == None:
1606 raise XendConfigError("Required either UUID or device type and "
1607 "configuration dictionary.")
1609 sxpr.append(dev_type)
1610 if dev_type in ('console', 'vfb'):
1611 config = [(opt, val) for opt, val in dev_info.items()
1612 if opt != 'other_config']
1613 else:
1614 config = [(opt, val) for opt, val in dev_info.items()]
1616 sxpr += config
1618 return sxpr
1620 def ordered_device_refs(self, target = None):
1621 result = []
1623 if target == None:
1624 target = self
1626 # vkbd devices *must* be before vfb devices, otherwise
1627 # there is a race condition when setting up devices
1628 # where the daemon spawned for the vfb may write stuff
1629 # into xenstore vkbd backend, before DevController has
1630 # setup permissions on the vkbd backend path. This race
1631 # results in domain creation failing with 'device already
1632 # connected' messages
1633 result.extend([u for u in target['devices'].keys() if target['devices'][u][0] == 'vkbd'])
1635 result.extend(target.get('console_refs', []) +
1636 target.get('vbd_refs', []) +
1637 target.get('vif_refs', []) +
1638 target.get('vtpm_refs', []))
1640 result.extend([u for u in target['devices'].keys() if u not in result])
1641 return result
1643 def all_devices_sxpr(self, target = None):
1644 """Returns the SXPR for all devices in the current configuration."""
1645 sxprs = []
1646 pci_devs = []
1648 if target == None:
1649 target = self
1651 if 'devices' not in target:
1652 return sxprs
1654 ordered_refs = self.ordered_device_refs(target = target)
1655 for dev_uuid in ordered_refs:
1656 dev_type, dev_info = target['devices'][dev_uuid]
1657 if dev_type == 'pci': # special case for pci devices
1658 sxpr = ['pci', ['uuid', dev_info['uuid']]]
1659 for pci_dev_info in dev_info['devs']:
1660 pci_dev_sxpr = ['dev']
1661 for opt, val in pci_dev_info.items():
1662 pci_dev_sxpr.append([opt, val])
1663 sxpr.append(pci_dev_sxpr)
1664 sxprs.append((dev_type, sxpr))
1665 else:
1666 sxpr = self.device_sxpr(dev_type = dev_type,
1667 dev_info = dev_info,
1668 target = target)
1669 sxprs.append((dev_type, sxpr))
1671 return sxprs
1673 def image_sxpr(self):
1674 """Returns a backwards compatible image SXP expression that is
1675 used in xenstore's /vm/<uuid>/image value and xm list."""
1676 image = [self.image_type()]
1677 if self.has_key('PV_kernel'):
1678 image.append(['kernel', self['PV_kernel']])
1679 if self.has_key('PV_ramdisk') and self['PV_ramdisk']:
1680 image.append(['ramdisk', self['PV_ramdisk']])
1681 if self.has_key('PV_args') and self['PV_args']:
1682 image.append(['args', self['PV_args']])
1684 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
1685 if key in self['platform']:
1686 image.append([key, self['platform'][key]])
1688 if 'notes' in self:
1689 image.append(self.notes_sxp(self['notes']))
1691 return image
1693 def update_with_image_sxp(self, image_sxp, bootloader = False):
1694 # Convert Legacy "image" config to Xen API PV_*
1695 # configuration
1696 log.debug("update_with_image_sxp(%s)" % scrub_password(image_sxp))
1698 # user-specified args must come last: previous releases did this and
1699 # some domU kernels rely upon the ordering.
1700 kernel_args = sxp.child_value(image_sxp, 'args', '')
1702 # attempt to extract extra arguments from SXP config
1703 arg_ip = sxp.child_value(image_sxp, 'ip')
1704 if arg_ip and not re.search(r'ip=[^ ]+', kernel_args):
1705 kernel_args = 'ip=%s ' % arg_ip + kernel_args
1706 arg_root = sxp.child_value(image_sxp, 'root')
1707 if arg_root and not re.search(r'root=', kernel_args):
1708 kernel_args = 'root=%s ' % arg_root + kernel_args
1710 if bootloader:
1711 self['_temp_using_bootloader'] = '1'
1712 self['_temp_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1713 self['_temp_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1714 self['_temp_args'] = kernel_args
1715 else:
1716 self['PV_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1717 self['PV_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1718 self['PV_args'] = kernel_args
1720 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
1721 val = sxp.child_value(image_sxp, key, None)
1722 if val is not None and val != '':
1723 self['platform'][key] = val
1725 notes = sxp.children(image_sxp, 'notes')
1726 if notes:
1727 self['notes'] = self.notes_from_sxp(notes[0])
1729 self._hvm_boot_params_from_sxp(image_sxp)
1731 def set_notes(self, notes):
1732 'Add parsed elfnotes to image'
1733 self['notes'] = notes
1735 def get_notes(self):
1736 try:
1737 return self['notes'] or {}
1738 except KeyError:
1739 return {}
1741 def notes_from_sxp(self, nsxp):
1742 notes = {}
1743 for note in sxp.children(nsxp):
1744 notes[note[0]] = note[1]
1745 return notes
1747 def notes_sxp(self, notes):
1748 nsxp = ['notes']
1749 for k, v in notes.iteritems():
1750 nsxp.append([k, str(v)])
1751 return nsxp
1753 def _hvm_boot_params_from_sxp(self, image_sxp):
1754 boot = sxp.child_value(image_sxp, 'boot', None)
1755 if boot is not None:
1756 self['HVM_boot_policy'] = 'BIOS order'
1757 self['HVM_boot_params'] = { 'order' : boot }
1759 def is_hvm(self):
1760 return self['HVM_boot_policy'] != ''
1762 def target(self):
1763 return self['target']
1765 def image_type(self):
1766 stored_type = self['platform'].get('image_type')
1767 return stored_type or (self.is_hvm() and 'hvm' or 'linux')
1769 def is_hap(self):
1770 return self['platform'].get('hap', 0)