debuggers.hg

view tools/python/xen/xend/XendConfig.py @ 17952:c5875621d79a

Fix up python breakage for blkdev_name_to_number

Signed-off-by: Chris Lalancette <clalance@redhat.com>
author Keir Fraser <keir.fraser@citrix.com>
date Mon Jun 30 10:01:44 2008 +0100 (2008-06-30)
parents 37392df51f0b
children 40e7329105fa
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 (xenbus, 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 if o_dev_uname != None:
1147 o_blkdev_file = blkdev_uname_to_file(o_dev_uname)
1148 if blkdev_file == o_blkdev_file:
1149 raise XendConfigError('The file "%s" is already used' %
1150 blkdev_file)
1151 o_blkdev_name = sxp.child_value(o_dev_info, 'dev')
1152 o_devid = self._blkdev_name_to_number(o_blkdev_name)
1153 if o_devid != None and devid == o_devid:
1154 raise XendConfigError('The device "%s" is already defined' %
1155 blkdev_name)
1157 elif dev_type == 'vif':
1158 dev_mac = dev_info.get('mac')
1160 for o_dev_type, o_dev_info in defined_devices_sxpr:
1161 if dev_type == o_dev_type:
1162 if dev_mac.lower() == sxp.child_value(o_dev_info, 'mac').lower():
1163 raise XendConfigError('The mac "%s" is already defined' %
1164 dev_mac)
1166 def device_add(self, dev_type, cfg_sxp = None, cfg_xenapi = None,
1167 target = None):
1168 """Add a device configuration in SXP format or XenAPI struct format.
1170 For SXP, it could be either:
1172 [device, [vbd, [uname ...]]
1174 or:
1176 [vbd, [uname ..]]
1178 @type cfg_sxp: list of lists (parsed sxp object)
1179 @param cfg_sxp: SXP configuration object
1180 @type cfg_xenapi: dict
1181 @param cfg_xenapi: A device configuration from Xen API (eg. vbd,vif)
1182 @param target: write device information to
1183 @type target: None or a dictionary
1184 @rtype: string
1185 @return: Assigned UUID of the device.
1186 """
1187 if target == None:
1188 target = self
1190 if dev_type not in XendDevices.valid_devices():
1191 raise XendConfigError("XendConfig: %s not a valid device type" %
1192 dev_type)
1194 if cfg_sxp == None and cfg_xenapi == None:
1195 raise XendConfigError("XendConfig: device_add requires some "
1196 "config.")
1198 #if cfg_sxp:
1199 # log.debug("XendConfig.device_add: %s" % str(cfg_sxp))
1200 #if cfg_xenapi:
1201 # log.debug("XendConfig.device_add: %s" % str(cfg_xenapi))
1203 if cfg_sxp:
1204 if sxp.child0(cfg_sxp) == 'device':
1205 config = sxp.child0(cfg_sxp)
1206 else:
1207 config = cfg_sxp
1209 dev_type = sxp.name(config)
1210 dev_info = {}
1212 # Parsing the device SXP's. In most cases, the SXP looks
1213 # like this:
1215 # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]]
1217 # However, for PCI devices it looks like this:
1219 # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1]]]]
1221 # It seems the reasoning for this difference is because
1222 # pciif.py needs all the PCI device configurations at
1223 # the same time when creating the devices.
1225 # To further complicate matters, Xen 2.0 configuration format
1226 # uses the following for pci device configuration:
1228 # [device, [pci, [domain, 0], [bus, 0], [dev, 1], [func, 2]]]
1230 if dev_type == 'pci':
1231 pci_devs_uuid = sxp.child_value(config, 'uuid',
1232 uuid.createString())
1233 pci_devs = []
1234 for pci_dev in sxp.children(config, 'dev'):
1235 pci_dev_info = {}
1236 for opt_val in pci_dev[1:]:
1237 try:
1238 opt, val = opt_val
1239 pci_dev_info[opt] = val
1240 except TypeError:
1241 pass
1242 pci_devs.append(pci_dev_info)
1243 target['devices'][pci_devs_uuid] = (dev_type,
1244 {'devs': pci_devs,
1245 'uuid': pci_devs_uuid})
1247 log.debug("XendConfig: reading device: %s" % pci_devs)
1248 return pci_devs_uuid
1250 for opt_val in config[1:]:
1251 try:
1252 opt, val = opt_val
1253 dev_info[opt] = val
1254 except (TypeError, ValueError): # unpack error
1255 pass
1257 if dev_type == 'vbd':
1258 dev_info['bootable'] = 0
1259 if dev_info.get('dev', '').startswith('ioemu:'):
1260 dev_info['driver'] = 'ioemu'
1261 else:
1262 dev_info['driver'] = 'paravirtualised'
1264 if dev_type == 'tap':
1265 if dev_info['uname'].split(':')[1] not in blktap_disk_types:
1266 raise XendConfigError("tap:%s not a valid disk type" %
1267 dev_info['uname'].split(':')[1])
1269 if dev_type == 'vif':
1270 if not dev_info.get('mac'):
1271 dev_info['mac'] = randomMAC()
1273 self.device_duplicate_check(dev_type, dev_info, target)
1275 if dev_type == 'vif':
1276 if dev_info.get('policy') and dev_info.get('label'):
1277 dev_info['security_label'] = "%s:%s:%s" % \
1278 (xsconstants.ACM_POLICY_ID,
1279 dev_info['policy'],dev_info['label'])
1281 # create uuid if it doesn't exist
1282 dev_uuid = dev_info.get('uuid', None)
1283 if not dev_uuid:
1284 dev_uuid = uuid.createString()
1285 dev_info['uuid'] = dev_uuid
1287 # store dev references by uuid for certain device types
1288 target['devices'][dev_uuid] = (dev_type, dev_info)
1289 if dev_type in ('vif', 'vbd', 'vtpm'):
1290 param = '%s_refs' % dev_type
1291 if param not in target:
1292 target[param] = []
1293 if dev_uuid not in target[param]:
1294 if dev_type == 'vbd':
1295 # Compat hack -- mark first disk bootable
1296 dev_info['bootable'] = int(not target[param])
1297 target[param].append(dev_uuid)
1298 elif dev_type == 'tap':
1299 if 'vbd_refs' not in target:
1300 target['vbd_refs'] = []
1301 if dev_uuid not in target['vbd_refs']:
1302 # Compat hack -- mark first disk bootable
1303 dev_info['bootable'] = int(not target['vbd_refs'])
1304 target['vbd_refs'].append(dev_uuid)
1306 elif dev_type == 'vfb':
1307 # Populate other config with aux data that is associated
1308 # with vfb
1310 other_config = {}
1311 for key in XENAPI_CONSOLE_OTHER_CFG:
1312 if key in dev_info:
1313 other_config[key] = dev_info[key]
1314 target['devices'][dev_uuid][1]['other_config'] = other_config
1317 if 'console_refs' not in target:
1318 target['console_refs'] = []
1320 # Treat VFB devices as console devices so they are found
1321 # through Xen API
1322 if dev_uuid not in target['console_refs']:
1323 target['console_refs'].append(dev_uuid)
1325 elif dev_type == 'console':
1326 if 'console_refs' not in target:
1327 target['console_refs'] = []
1328 if dev_uuid not in target['console_refs']:
1329 target['console_refs'].append(dev_uuid)
1331 log.debug("XendConfig: reading device: %s" % scrub_password(dev_info))
1332 return dev_uuid
1334 if cfg_xenapi:
1335 dev_info = {}
1336 dev_uuid = ''
1337 if dev_type == 'vif':
1338 dev_info['mac'] = cfg_xenapi.get('MAC')
1339 if not dev_info['mac']:
1340 dev_info['mac'] = randomMAC()
1341 # vifname is the name on the guest, not dom0
1342 # TODO: we don't have the ability to find that out or
1343 # change it from dom0
1344 #if cfg_xenapi.get('device'): # don't add if blank
1345 # dev_info['vifname'] = cfg_xenapi.get('device')
1346 if cfg_xenapi.get('type'):
1347 dev_info['type'] = cfg_xenapi.get('type')
1348 if cfg_xenapi.get('name'):
1349 dev_info['name'] = cfg_xenapi.get('name')
1350 if cfg_xenapi.get('network'):
1351 network = XendAPIStore.get(
1352 cfg_xenapi.get('network'), 'network')
1353 dev_info['bridge'] = network.get_name_label()
1355 if cfg_xenapi.get('security_label'):
1356 dev_info['security_label'] = \
1357 cfg_xenapi.get('security_label')
1359 dev_uuid = cfg_xenapi.get('uuid', None)
1360 if not dev_uuid:
1361 dev_uuid = uuid.createString()
1362 dev_info['uuid'] = dev_uuid
1363 target['devices'][dev_uuid] = (dev_type, dev_info)
1364 target['vif_refs'].append(dev_uuid)
1366 elif dev_type in ('vbd', 'tap'):
1367 dev_info['type'] = cfg_xenapi.get('type', 'Disk')
1368 if dev_info['type'] == 'CD':
1369 old_vbd_type = 'cdrom'
1370 else:
1371 old_vbd_type = 'disk'
1373 dev_info['uname'] = cfg_xenapi.get('image', '')
1374 dev_info['dev'] = '%s:%s' % (cfg_xenapi.get('device'),
1375 old_vbd_type)
1376 dev_info['bootable'] = int(cfg_xenapi.get('bootable', 0))
1377 dev_info['driver'] = cfg_xenapi.get('driver', '')
1378 dev_info['VDI'] = cfg_xenapi.get('VDI', '')
1380 if cfg_xenapi.get('mode') == 'RW':
1381 dev_info['mode'] = 'w'
1382 else:
1383 dev_info['mode'] = 'r'
1385 dev_uuid = cfg_xenapi.get('uuid', None)
1386 if not dev_uuid:
1387 dev_uuid = uuid.createString()
1388 dev_info['uuid'] = dev_uuid
1389 target['devices'][dev_uuid] = (dev_type, dev_info)
1390 target['vbd_refs'].append(dev_uuid)
1392 elif dev_type == 'vtpm':
1393 if cfg_xenapi.get('type'):
1394 dev_info['type'] = cfg_xenapi.get('type')
1396 dev_uuid = cfg_xenapi.get('uuid', None)
1397 if not dev_uuid:
1398 dev_uuid = uuid.createString()
1399 dev_info['uuid'] = dev_uuid
1400 dev_info['other_config'] = cfg_xenapi.get('other_config', {})
1401 target['devices'][dev_uuid] = (dev_type, dev_info)
1402 target['vtpm_refs'].append(dev_uuid)
1404 elif dev_type == 'console':
1405 dev_uuid = cfg_xenapi.get('uuid', None)
1406 if not dev_uuid:
1407 dev_uuid = uuid.createString()
1408 dev_info['uuid'] = dev_uuid
1409 dev_info['protocol'] = cfg_xenapi.get('protocol', 'rfb')
1410 console_other_config = cfg_xenapi.get('other_config', {})
1411 dev_info['other_config'] = console_other_config
1412 if dev_info['protocol'] == 'rfb':
1413 # collapse other config into devinfo for things
1414 # such as vncpasswd, vncunused, etc.
1415 dev_info.update(console_other_config)
1416 dev_info['type'] = console_other_config.get('type', 'vnc')
1417 target['devices'][dev_uuid] = ('vfb', dev_info)
1418 target['console_refs'].append(dev_uuid)
1420 # if console is rfb, set device_model ensuring qemu
1421 # is invoked for pvfb services
1422 if 'device_model' not in target['platform']:
1423 target['platform']['device_model'] = \
1424 xen.util.auxbin.pathTo("qemu-dm")
1426 # Finally, if we are a pvfb, we need to make a vkbd
1427 # as well that is not really exposed to Xen API
1428 vkbd_uuid = uuid.createString()
1429 target['devices'][vkbd_uuid] = ('vkbd', {})
1431 elif dev_info['protocol'] == 'vt100':
1432 # if someone tries to create a VT100 console
1433 # via the Xen API, we'll have to ignore it
1434 # because we create one automatically in
1435 # XendDomainInfo._update_consoles
1436 raise XendConfigError('Creating vt100 consoles via '
1437 'Xen API is unsupported')
1439 return dev_uuid
1441 # no valid device to add
1442 return ''
1444 def phantom_device_add(self, dev_type, cfg_xenapi = None,
1445 target = None):
1446 """Add a phantom tap device configuration in XenAPI struct format.
1447 """
1449 if target == None:
1450 target = self
1452 if dev_type not in XendDevices.valid_devices() and \
1453 dev_type not in XendDevices.pseudo_devices():
1454 raise XendConfigError("XendConfig: %s not a valid device type" %
1455 dev_type)
1457 if cfg_xenapi == None:
1458 raise XendConfigError("XendConfig: device_add requires some "
1459 "config.")
1461 if cfg_xenapi:
1462 log.debug("XendConfig.phantom_device_add: %s" % str(cfg_xenapi))
1464 if cfg_xenapi:
1465 dev_info = {}
1466 if dev_type in ('vbd', 'tap'):
1467 if dev_type == 'vbd':
1468 dev_info['uname'] = cfg_xenapi.get('image', '')
1469 dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
1470 elif dev_type == 'tap':
1471 if cfg_xenapi.get('image').find('tap:') == -1:
1472 dev_info['uname'] = 'tap:qcow:%s' % cfg_xenapi.get('image')
1473 dev_info['dev'] = '/dev/%s' % cfg_xenapi.get('device')
1474 dev_info['uname'] = cfg_xenapi.get('image')
1475 dev_info['mode'] = cfg_xenapi.get('mode')
1476 dev_info['backend'] = '0'
1477 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
1478 dev_info['uuid'] = dev_uuid
1479 self['devices'][dev_uuid] = (dev_type, dev_info)
1480 self['vbd_refs'].append(dev_uuid)
1481 return dev_uuid
1483 return ''
1485 def console_add(self, protocol, location, other_config = {}):
1486 dev_uuid = uuid.createString()
1487 if protocol == 'vt100':
1488 dev_info = {
1489 'uuid': dev_uuid,
1490 'protocol': protocol,
1491 'location': location,
1492 'other_config': other_config,
1495 if 'devices' not in self:
1496 self['devices'] = {}
1498 self['devices'][dev_uuid] = ('console', dev_info)
1499 self['console_refs'].append(dev_uuid)
1500 return dev_info
1502 return {}
1504 def console_update(self, console_uuid, key, value):
1505 for dev_uuid, (dev_type, dev_info) in self['devices'].items():
1506 if dev_uuid == console_uuid:
1507 dev_info[key] = value
1508 # collapse other_config into dev_info for things
1509 # such as vncpasswd, vncunused, etc.
1510 if key == 'other_config':
1511 for k in XENAPI_CONSOLE_OTHER_CFG:
1512 if k in dev_info and k not in value:
1513 del dev_info[k]
1514 dev_info.update(value)
1515 break
1517 def console_get_all(self, protocol):
1518 if protocol == 'vt100':
1519 consoles = [dinfo for dtype, dinfo in self['devices'].values()
1520 if dtype == 'console']
1521 return [c for c in consoles if c.get('protocol') == protocol]
1523 elif protocol == 'rfb':
1524 vfbs = [dinfo for dtype, dinfo in self['devices'].values()
1525 if dtype == 'vfb']
1527 # move all non-console key values to other_config before
1528 # returning console config
1529 valid_keys = ['uuid', 'location']
1530 for vfb in vfbs:
1531 other_config = {}
1532 for key, val in vfb.items():
1533 if key not in valid_keys:
1534 other_config[key] = vfb[key]
1535 del vfb[key]
1536 vfb['other_config'] = other_config
1537 vfb['protocol'] = 'rfb'
1539 return vfbs
1541 else:
1542 return []
1544 def device_update(self, dev_uuid, cfg_sxp = [], cfg_xenapi = {}):
1545 """Update an existing device with the new configuration.
1547 @rtype: boolean
1548 @return: Returns True if succesfully found and updated a device conf
1549 """
1550 if dev_uuid in self['devices'] and cfg_sxp:
1551 if sxp.child0(cfg_sxp) == 'device':
1552 config = sxp.child0(cfg_sxp)
1553 else:
1554 config = cfg_sxp
1556 dev_type, dev_info = self['devices'][dev_uuid]
1558 if dev_type == 'pci': # Special case for pci
1559 pci_devs = []
1560 for pci_dev in sxp.children(config, 'dev'):
1561 pci_dev_info = {}
1562 for opt_val in pci_dev[1:]:
1563 try:
1564 opt, val = opt_val
1565 pci_dev_info[opt] = val
1566 except TypeError:
1567 pass
1568 pci_devs.append(pci_dev_info)
1569 self['devices'][dev_uuid] = (dev_type,
1570 {'devs': pci_devs,
1571 'uuid': dev_uuid})
1572 return True
1574 for opt_val in config[1:]:
1575 try:
1576 opt, val = opt_val
1577 dev_info[opt] = val
1578 except (TypeError, ValueError):
1579 pass # no value for this config option
1581 self['devices'][dev_uuid] = (dev_type, dev_info)
1582 return True
1584 elif dev_uuid in self['devices'] and cfg_xenapi:
1585 dev_type, dev_info = self['devices'][dev_uuid]
1586 for key, val in cfg_xenapi.items():
1587 dev_info[key] = val
1588 self['devices'][dev_uuid] = (dev_type, dev_info)
1590 return False
1593 def device_sxpr(self, dev_uuid = None, dev_type = None, dev_info = None, target = None):
1594 """Get Device SXPR by either giving the device UUID or (type, config).
1596 @rtype: list of lists
1597 @return: device config sxpr
1598 """
1599 sxpr = []
1601 if target == None:
1602 target = self
1604 if dev_uuid != None and dev_uuid in target['devices']:
1605 dev_type, dev_info = target['devices'][dev_uuid]
1607 if dev_type == None or dev_info == None:
1608 raise XendConfigError("Required either UUID or device type and "
1609 "configuration dictionary.")
1611 sxpr.append(dev_type)
1612 if dev_type in ('console', 'vfb'):
1613 config = [(opt, val) for opt, val in dev_info.items()
1614 if opt != 'other_config']
1615 else:
1616 config = [(opt, val) for opt, val in dev_info.items()]
1618 sxpr += config
1620 return sxpr
1622 def ordered_device_refs(self, target = None):
1623 result = []
1625 if target == None:
1626 target = self
1628 # vkbd devices *must* be before vfb devices, otherwise
1629 # there is a race condition when setting up devices
1630 # where the daemon spawned for the vfb may write stuff
1631 # into xenstore vkbd backend, before DevController has
1632 # setup permissions on the vkbd backend path. This race
1633 # results in domain creation failing with 'device already
1634 # connected' messages
1635 result.extend([u for u in target['devices'].keys() if target['devices'][u][0] == 'vkbd'])
1637 result.extend(target.get('console_refs', []) +
1638 target.get('vbd_refs', []) +
1639 target.get('vif_refs', []) +
1640 target.get('vtpm_refs', []))
1642 result.extend([u for u in target['devices'].keys() if u not in result])
1643 return result
1645 def all_devices_sxpr(self, target = None):
1646 """Returns the SXPR for all devices in the current configuration."""
1647 sxprs = []
1648 pci_devs = []
1650 if target == None:
1651 target = self
1653 if 'devices' not in target:
1654 return sxprs
1656 ordered_refs = self.ordered_device_refs(target = target)
1657 for dev_uuid in ordered_refs:
1658 dev_type, dev_info = target['devices'][dev_uuid]
1659 if dev_type == 'pci': # special case for pci devices
1660 sxpr = ['pci', ['uuid', dev_info['uuid']]]
1661 for pci_dev_info in dev_info['devs']:
1662 pci_dev_sxpr = ['dev']
1663 for opt, val in pci_dev_info.items():
1664 pci_dev_sxpr.append([opt, val])
1665 sxpr.append(pci_dev_sxpr)
1666 sxprs.append((dev_type, sxpr))
1667 else:
1668 sxpr = self.device_sxpr(dev_type = dev_type,
1669 dev_info = dev_info,
1670 target = target)
1671 sxprs.append((dev_type, sxpr))
1673 return sxprs
1675 def image_sxpr(self):
1676 """Returns a backwards compatible image SXP expression that is
1677 used in xenstore's /vm/<uuid>/image value and xm list."""
1678 image = [self.image_type()]
1679 if self.has_key('PV_kernel'):
1680 image.append(['kernel', self['PV_kernel']])
1681 if self.has_key('PV_ramdisk') and self['PV_ramdisk']:
1682 image.append(['ramdisk', self['PV_ramdisk']])
1683 if self.has_key('PV_args') and self['PV_args']:
1684 image.append(['args', self['PV_args']])
1686 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
1687 if key in self['platform']:
1688 image.append([key, self['platform'][key]])
1690 if 'notes' in self:
1691 image.append(self.notes_sxp(self['notes']))
1693 return image
1695 def update_with_image_sxp(self, image_sxp, bootloader = False):
1696 # Convert Legacy "image" config to Xen API PV_*
1697 # configuration
1698 log.debug("update_with_image_sxp(%s)" % scrub_password(image_sxp))
1700 # user-specified args must come last: previous releases did this and
1701 # some domU kernels rely upon the ordering.
1702 kernel_args = sxp.child_value(image_sxp, 'args', '')
1704 # attempt to extract extra arguments from SXP config
1705 arg_ip = sxp.child_value(image_sxp, 'ip')
1706 if arg_ip and not re.search(r'ip=[^ ]+', kernel_args):
1707 kernel_args = 'ip=%s ' % arg_ip + kernel_args
1708 arg_root = sxp.child_value(image_sxp, 'root')
1709 if arg_root and not re.search(r'root=', kernel_args):
1710 kernel_args = 'root=%s ' % arg_root + kernel_args
1712 if bootloader:
1713 self['_temp_using_bootloader'] = '1'
1714 self['_temp_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1715 self['_temp_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1716 self['_temp_args'] = kernel_args
1717 else:
1718 self['PV_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1719 self['PV_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1720 self['PV_args'] = kernel_args
1722 for key in XENAPI_PLATFORM_CFG_TYPES.keys():
1723 val = sxp.child_value(image_sxp, key, None)
1724 if val is not None and val != '':
1725 self['platform'][key] = val
1727 notes = sxp.children(image_sxp, 'notes')
1728 if notes:
1729 self['notes'] = self.notes_from_sxp(notes[0])
1731 self._hvm_boot_params_from_sxp(image_sxp)
1733 def set_notes(self, notes):
1734 'Add parsed elfnotes to image'
1735 self['notes'] = notes
1737 def get_notes(self):
1738 try:
1739 return self['notes'] or {}
1740 except KeyError:
1741 return {}
1743 def notes_from_sxp(self, nsxp):
1744 notes = {}
1745 for note in sxp.children(nsxp):
1746 notes[note[0]] = note[1]
1747 return notes
1749 def notes_sxp(self, notes):
1750 nsxp = ['notes']
1751 for k, v in notes.iteritems():
1752 nsxp.append([k, str(v)])
1753 return nsxp
1755 def _hvm_boot_params_from_sxp(self, image_sxp):
1756 boot = sxp.child_value(image_sxp, 'boot', None)
1757 if boot is not None:
1758 self['HVM_boot_policy'] = 'BIOS order'
1759 self['HVM_boot_params'] = { 'order' : boot }
1761 def is_hvm(self):
1762 return self['HVM_boot_policy'] != ''
1764 def target(self):
1765 return self['target']
1767 def image_type(self):
1768 stored_type = self['platform'].get('image_type')
1769 return stored_type or (self.is_hvm() and 'hvm' or 'linux')
1771 def is_hap(self):
1772 return self['platform'].get('hap', 0)