debuggers.hg

view tools/python/xen/xend/XendConfig.py @ 16559:5255eac35270

Implement legacy XML-RPC interface for ACM commands.

This patch implements a (non Xen-API) legacy XML-RPC interface for the
ACM commands and funnels the calls into code introduced by the Xen-API
support for ACM security management. Since some of the functionality
has changed, also the xm applications have changed. In particular the
following old commands have been removed along with some tools the
have become obsolete now:

- loadpolicy (included in: setpolicy)
- makepolicy (included in: setpolicy)
- cfgbootpolicy (included in: setpolicy)

and the following commands been introduced:

- setpolicy
- getpolicy
- resetpolicy

All tools have been adapted to work in Xen-API and legacy XML-RPC
mode. Both modes support the same functionality.

Signed-off-by: Stefan Berger <stefanb@us.ibm.com>
author Keir Fraser <keir.fraser@citrix.com>
date Wed Dec 05 09:44:20 2007 +0000 (2007-12-05)
parents ad5fa636bc4e
children ca461349620a
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
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.
127 XENAPI_PLATFORM_CFG = [ 'acpi', 'apic', 'boot', 'device_model', 'display',
128 'fda', 'fdb', 'keymap', 'isa', 'localtime', 'monitor',
129 'nographic', 'pae', 'rtc_timeoffset', 'serial', 'sdl',
130 'soundhw','stdvga', 'usb', 'usbdevice', 'vnc',
131 'vncconsole', 'vncdisplay', 'vnclisten', 'timer_mode',
132 'vncpasswd', 'vncunused', 'xauthority', 'pci', 'vhpt',
133 'guest_os_type' ]
135 # Xen API console 'other_config' keys.
136 XENAPI_CONSOLE_OTHER_CFG = ['vncunused', 'vncdisplay', 'vnclisten',
137 'vncpasswd', 'type', 'display', 'xauthority',
138 'keymap']
140 # List of XendConfig configuration keys that have no direct equivalent
141 # in the old world.
143 XENAPI_CFG_TYPES = {
144 'uuid': str,
145 'name_label': str,
146 'name_description': str,
147 'user_version': str,
148 'is_a_template': bool0,
149 'resident_on': str,
150 'memory_static_min': int, # note these are stored in bytes, not KB!
151 'memory_static_max': int,
152 'memory_dynamic_min': int,
153 'memory_dynamic_max': int,
154 'cpus': list,
155 'vcpus_params': dict,
156 'VCPUs_max': int,
157 'VCPUs_at_startup': int,
158 'VCPUs_live': int,
159 'actions_after_shutdown': str,
160 'actions_after_reboot': str,
161 'actions_after_crash': str,
162 'PV_bootloader': str,
163 'PV_kernel': str,
164 'PV_ramdisk': str,
165 'PV_args': str,
166 'PV_bootloader_args': str,
167 'HVM_boot_policy': str,
168 'HVM_boot_params': dict,
169 'PCI_bus': str,
170 'platform': dict,
171 'tools_version': dict,
172 'other_config': dict,
173 'security_label': str,
174 'pci': str,
175 }
177 # List of legacy configuration keys that have no equivalent in the
178 # Xen API, but are still stored in XendConfig.
180 LEGACY_UNSUPPORTED_BY_XENAPI_CFG = [
181 # roundtripped (dynamic, unmodified)
182 'shadow_memory',
183 'vcpu_avail',
184 'features',
185 # read/write
186 'on_xend_start',
187 'on_xend_stop',
188 # read-only
189 'domid',
190 'start_time',
191 'cpu_time',
192 'online_vcpus',
193 # write-once
194 'cpu',
195 'cpus',
196 ]
198 LEGACY_CFG_TYPES = {
199 'uuid': str,
200 'name': str,
201 'vcpus': int,
202 'vcpu_avail': long,
203 'memory': int,
204 'shadow_memory': int,
205 'maxmem': int,
206 'start_time': float,
207 'cpu_time': float,
208 'features': str,
209 'localtime': int,
210 'name': str,
211 'on_poweroff': str,
212 'on_reboot': str,
213 'on_crash': str,
214 'on_xend_stop': str,
215 'on_xend_start': str,
216 'online_vcpus': int,
217 'rtc/timeoffset': str,
218 }
220 # Values that should be stored in xenstore's /vm/<uuid> that is used
221 # by Xend. Used in XendDomainInfo to restore running VM state from
222 # xenstore.
223 LEGACY_XENSTORE_VM_PARAMS = [
224 'uuid',
225 'name',
226 'vcpus',
227 'vcpu_avail',
228 'memory',
229 'shadow_memory',
230 'maxmem',
231 'start_time',
232 'name',
233 'on_poweroff',
234 'on_crash',
235 'on_reboot',
236 'on_xend_start',
237 'on_xend_stop',
238 ]
240 ##
241 ## Config Choices
242 ##
244 CONFIG_RESTART_MODES = ('restart', 'destroy', 'preserve', 'rename-restart')
245 CONFIG_OLD_DOM_STATES = ('running', 'blocked', 'paused', 'shutdown',
246 'crashed', 'dying')
248 class XendConfigError(VmError):
249 def __str__(self):
250 return 'Invalid Configuration: %s' % str(self.value)
252 ##
253 ## XendConfig Class (an extended dictionary)
254 ##
256 class XendConfig(dict):
257 """ The new Xend VM Configuration.
259 Stores the configuration in xenapi compatible format but retains
260 import and export functions for SXP.
261 """
262 def __init__(self, filename = None, sxp_obj = None,
263 xapi = None, dominfo = None):
265 dict.__init__(self)
266 self.update(self._defaults())
268 if filename:
269 try:
270 sxp_obj = sxp.parse(open(filename,'r'))
271 sxp_obj = sxp_obj[0]
272 except IOError, e:
273 raise XendConfigError("Unable to read file: %s" % filename)
275 if sxp_obj:
276 self._sxp_to_xapi(sxp_obj)
277 self._sxp_to_xapi_unsupported(sxp_obj)
278 elif xapi:
279 self.update_with_xenapi_config(xapi)
280 elif dominfo:
281 # output from xc.domain_getinfo
282 self._dominfo_to_xapi(dominfo, update_mem = True)
284 log.debug('XendConfig.init: %s' % scrub_password(self))
286 # validators go here
287 self.validate()
289 """ In time, we should enable this type checking addition. It is great
290 also for tracking bugs and unintended writes to XendDomainInfo.info
291 def __setitem__(self, key, value):
292 type_conv = XENAPI_CFG_TYPES.get(key)
293 if callable(type_conv):
294 try:
295 dict.__setitem__(self, key, type_conv(value))
296 except (ValueError, TypeError):
297 raise XendConfigError("Wrong type for configuration value " +
298 "%s. Expected %s" %
299 (key, type_conv.__name__))
300 else:
301 dict.__setitem__(self, key, value)
302 """
304 def _defaults(self):
305 defaults = {
306 'name_label': 'Domain-Unnamed',
307 'actions_after_shutdown': 'destroy',
308 'actions_after_reboot': 'restart',
309 'actions_after_crash': 'restart',
310 'actions_after_suspend': '',
311 'is_a_template': False,
312 'is_control_domain': False,
313 'features': '',
314 'PV_bootloader': '',
315 'PV_kernel': '',
316 'PV_ramdisk': '',
317 'PV_args': '',
318 'PV_bootloader_args': '',
319 'HVM_boot_policy': '',
320 'HVM_boot_params': {},
321 'memory_static_min': 0,
322 'memory_dynamic_min': 0,
323 'shadow_memory': 0,
324 'memory_static_max': 0,
325 'memory_dynamic_max': 0,
326 'devices': {},
327 'on_xend_start': 'ignore',
328 'on_xend_stop': 'ignore',
329 'cpus': [],
330 'VCPUs_max': 1,
331 'VCPUs_live': 1,
332 'VCPUs_at_startup': 1,
333 'vcpus_params': {},
334 'console_refs': [],
335 'vif_refs': [],
336 'vbd_refs': [],
337 'vtpm_refs': [],
338 'other_config': {},
339 'platform': {}
340 }
342 return defaults
344 #
345 # Here we assume these values exist in the dict.
346 # If they don't we have a bigger problem, lets not
347 # try and 'fix it up' but acutually fix the cause ;-)
348 #
349 def _memory_sanity_check(self):
350 log.trace("_memory_sanity_check memory_static_min: %s, "
351 "memory_static_max: %i, "
352 "memory_dynamic_min: %i, "
353 "memory_dynamic_max: %i",
354 self["memory_static_min"],
355 self["memory_static_max"],
356 self["memory_dynamic_min"],
357 self["memory_dynamic_max"])
359 if not self["memory_static_min"] <= self["memory_static_max"]:
360 raise XendConfigError("memory_static_min must be less " \
361 "than or equal to memory_static_max")
362 if not self["memory_static_min"] <= self["memory_dynamic_min"]:
363 raise XendConfigError("memory_static_min must be less " \
364 "than or equal to memory_dynamic_min")
365 if not self["memory_dynamic_max"] <= self["memory_static_max"]:
366 raise XendConfigError("memory_dynamic_max must be less " \
367 "than or equal to memory_static_max")
368 if not self["memory_dynamic_max"] > 0:
369 raise XendConfigError("memory_dynamic_max must be greater " \
370 "than zero")
371 if not self["memory_static_max"] > 0:
372 raise XendConfigError("memory_static_max must be greater " \
373 "than zero")
375 def _actions_sanity_check(self):
376 for event in ['shutdown', 'reboot', 'crash']:
377 if self['actions_after_' + event] not in CONFIG_RESTART_MODES:
378 raise XendConfigError('Invalid event handling mode: ' +
379 event)
381 def _vcpus_sanity_check(self):
382 if 'VCPUs_max' in self and 'vcpu_avail' not in self:
383 self['vcpu_avail'] = (1 << self['VCPUs_max']) - 1
385 def _uuid_sanity_check(self):
386 """Make sure UUID is in proper string format with hyphens."""
387 if 'uuid' not in self or not self['uuid']:
388 self['uuid'] = uuid.createString()
389 else:
390 self['uuid'] = uuid.toString(uuid.fromString(self['uuid']))
392 def _name_sanity_check(self):
393 if 'name_label' not in self:
394 self['name_label'] = 'Domain-' + self['uuid']
396 def _platform_sanity_check(self):
397 if 'keymap' not in self['platform'] and XendOptions.instance().get_keymap():
398 self['platform']['keymap'] = XendOptions.instance().get_keymap()
400 if self.is_hvm() or self.has_rfb():
401 if 'device_model' not in self['platform']:
402 self['platform']['device_model'] = xen.util.auxbin.pathTo("qemu-dm")
404 if self.is_hvm():
405 # Compatibility hack, can go away soon.
406 if 'soundhw' not in self['platform'] and \
407 self['platform'].get('enable_audio'):
408 self['platform']['soundhw'] = 'sb16'
410 def validate(self):
411 self._uuid_sanity_check()
412 self._name_sanity_check()
413 self._memory_sanity_check()
414 self._actions_sanity_check()
415 self._vcpus_sanity_check()
416 self._platform_sanity_check()
418 def _dominfo_to_xapi(self, dominfo, update_mem = False):
419 self['domid'] = dominfo['domid']
420 self['online_vcpus'] = dominfo['online_vcpus']
421 self['VCPUs_max'] = dominfo['max_vcpu_id'] + 1
423 if update_mem:
424 self['memory_dynamic_min'] = dominfo['mem_kb'] * 1024
425 self['memory_dynamic_max'] = dominfo['mem_kb'] * 1024
426 self['memory_static_min'] = 0
427 self['memory_static_max'] = dominfo['maxmem_kb'] * 1024
428 self._memory_sanity_check()
430 self['cpu_time'] = dominfo['cpu_time']/1e9
431 if dominfo.get('ssidref'):
432 ssidref = int(dominfo.get('ssidref'))
433 import xen.util.xsm.xsm as security
434 self['security_label'] = security.ssidref2security_label(ssidref)
436 self['shutdown_reason'] = dominfo['shutdown_reason']
438 # parse state into Xen API states
439 self['running'] = dominfo['running']
440 self['crashed'] = dominfo['crashed']
441 self['dying'] = dominfo['dying']
442 self['shutdown'] = dominfo['shutdown']
443 self['paused'] = dominfo['paused']
444 self['blocked'] = dominfo['blocked']
446 if 'name' in dominfo:
447 self['name_label'] = dominfo['name']
449 if 'handle' in dominfo:
450 self['uuid'] = uuid.toString(dominfo['handle'])
452 def _parse_sxp(self, sxp_cfg):
453 """ Populate this XendConfig using the parsed SXP.
455 @param sxp_cfg: Parsed SXP Configuration
456 @type sxp_cfg: list of lists
457 @rtype: dictionary
458 @return: A dictionary containing the parsed options of the SXP.
459 """
460 cfg = {}
462 for key, typ in XENAPI_CFG_TYPES.items():
463 val = sxp.child_value(sxp_cfg, key)
464 if val is not None:
465 try:
466 cfg[key] = typ(val)
467 except (ValueError, TypeError), e:
468 log.warn('Unable to convert type value for key: %s' % key)
470 # Convert deprecated options to current equivalents.
472 restart = sxp.child_value(sxp_cfg, 'restart')
473 if restart:
474 if restart == 'onreboot':
475 cfg['on_poweroff'] = 'destroy'
476 cfg['on_reboot'] = 'restart'
477 cfg['on_crash'] = 'destroy'
478 elif restart == 'always':
479 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
480 cfg[opt] = 'restart'
481 elif restart == 'never':
482 for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
483 cfg[opt] = 'never'
484 else:
485 log.warn('Ignoring unrecognised value for deprecated option:'
486 'restart = \'%s\'', restart)
488 # Handle memory, passed in as MiB
490 if sxp.child_value(sxp_cfg, "memory") != None:
491 cfg["memory"] = int(sxp.child_value(sxp_cfg, "memory"))
492 if sxp.child_value(sxp_cfg, "maxmem") != None:
493 cfg["maxmem"] = int(sxp.child_value(sxp_cfg, "maxmem"))
495 # Convert scheduling parameters to vcpus_params
496 if 'vcpus_params' not in cfg:
497 cfg['vcpus_params'] = {}
498 cfg["vcpus_params"]["weight"] = \
499 int(sxp.child_value(sxp_cfg, "cpu_weight", 256))
500 cfg["vcpus_params"]["cap"] = \
501 int(sxp.child_value(sxp_cfg, "cpu_cap", 0))
503 # Only extract options we know about.
504 extract_keys = LEGACY_UNSUPPORTED_BY_XENAPI_CFG
505 extract_keys += XENAPI_CFG_TO_LEGACY_CFG.values()
507 for key in extract_keys:
508 val = sxp.child_value(sxp_cfg, key)
509 if val != None:
510 try:
511 cfg[key] = LEGACY_CFG_TYPES[key](val)
512 except KeyError:
513 cfg[key] = val
514 except (TypeError, ValueError), e:
515 log.warn("Unable to parse key %s: %s: %s" %
516 (key, str(val), e))
518 if 'platform' not in cfg:
519 cfg['platform'] = {}
520 localtime = sxp.child_value(sxp_cfg, 'localtime')
521 if localtime is not None:
522 cfg['platform']['localtime'] = localtime
524 # Compatibility hack -- can go soon.
525 for key in XENAPI_PLATFORM_CFG:
526 val = sxp.child_value(sxp_cfg, "platform_" + key, None)
527 if val is not None:
528 self['platform'][key] = val
530 # Compatibility hack -- can go soon.
531 boot_order = sxp.child_value(sxp_cfg, 'HVM_boot')
532 if boot_order:
533 cfg['HVM_boot_policy'] = 'BIOS order'
534 cfg['HVM_boot_params'] = { 'order' : boot_order }
536 # Parsing the device SXP's. In most cases, the SXP looks
537 # like this:
538 #
539 # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]]
540 #
541 # However, for PCI devices it looks like this:
542 #
543 # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1]]]]
544 #
545 # It seems the reasoning for this difference is because
546 # pciif.py needs all the PCI device configurations at
547 # the same time when creating the devices.
548 #
549 # To further complicate matters, Xen 2.0 configuration format
550 # uses the following for pci device configuration:
551 #
552 # [device, [pci, [domain, 0], [bus, 0], [dev, 1], [func, 2]]]
553 #
554 # Hence we deal with pci device configurations outside of
555 # the regular device parsing.
557 cfg['devices'] = {}
558 for dev in sxp.children(sxp_cfg, 'device'):
559 config = sxp.child0(dev)
560 dev_type = sxp.name(config)
561 dev_info = {}
563 if dev_type == 'pci':
564 pci_devs_uuid = sxp.child_value(config, 'uuid',
565 uuid.createString())
566 pci_devs = []
567 for pci_dev in sxp.children(config, 'dev'):
568 pci_dev_info = {}
569 for opt_val in pci_dev[1:]:
570 try:
571 opt, val = opt_val
572 pci_dev_info[opt] = val
573 except TypeError:
574 pass
575 pci_devs.append(pci_dev_info)
577 cfg['devices'][pci_devs_uuid] = (dev_type,
578 {'devs': pci_devs,
579 'uuid': pci_devs_uuid})
581 log.debug("XendConfig: reading device: %s" % pci_devs)
582 else:
583 self.device_add(dev_type, cfg_sxp = config, target = cfg)
584 log.debug("XendConfig: reading device: %s" % scrub_password(dev_info))
586 # Extract missing data from configuration entries
587 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
588 if image_sxp:
589 image_vcpus = sxp.child_value(image_sxp, 'vcpus')
590 if image_vcpus != None:
591 try:
592 if 'VCPUs_max' not in cfg:
593 cfg['VCPUs_max'] = int(image_vcpus)
594 elif cfg['VCPUs_max'] != int(image_vcpus):
595 cfg['VCPUs_max'] = int(image_vcpus)
596 log.warn('Overriding vcpus from %d to %d using image'
597 'vcpus value.', cfg['VCPUs_max'])
598 except ValueError, e:
599 raise XendConfigError('integer expeceted: %s: %s' %
600 image_sxp, e)
602 # Deprecated cpu configuration
603 if 'cpu' in cfg:
604 if 'cpus' in cfg:
605 cfg['cpus'] = "%s,%s" % (str(cfg['cpu']), cfg['cpus'])
606 else:
607 cfg['cpus'] = str(cfg['cpu'])
609 # Convert 'cpus' to list of ints
610 if 'cpus' in cfg:
611 cpus = []
612 if type(cfg['cpus']) == list:
613 # If sxp_cfg was created from config.sxp,
614 # the form of 'cpus' is list of string.
615 # Convert 'cpus' to list of ints.
616 # ['1'] -> [1]
617 # ['0','2','3'] -> [0,2,3]
618 try:
619 for c in cfg['cpus']:
620 cpus.append(int(c))
622 cfg['cpus'] = cpus
623 except ValueError, e:
624 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
625 else:
626 # Convert 'cpus' string to list of ints
627 # 'cpus' supports a list of ranges (0-3),
628 # seperated by commas, and negation, (^1).
629 # Precedence is settled by order of the
630 # string:
631 # "0-3,^1" -> [0,2,3]
632 # "0-3,^1,1" -> [0,1,2,3]
633 try:
634 for c in cfg['cpus'].split(','):
635 if c.find('-') != -1:
636 (x, y) = c.split('-')
637 for i in range(int(x), int(y)+1):
638 cpus.append(int(i))
639 else:
640 # remove this element from the list
641 if c[0] == '^':
642 cpus = [x for x in cpus if x != int(c[1:])]
643 else:
644 cpus.append(int(c))
646 cfg['cpus'] = cpus
647 except ValueError, e:
648 raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
650 import xen.util.xsm.xsm as security
651 if security.on():
652 from xen.util.acmpolicy import ACM_LABEL_UNLABELED
653 if not 'security' in cfg and sxp.child_value(sxp_cfg, 'security'):
654 cfg['security'] = sxp.child_value(sxp_cfg, 'security')
655 elif not cfg.get('security_label'):
656 cfg['security'] = [['access_control',
657 ['policy', security.get_active_policy_name() ],
658 ['label', ACM_LABEL_UNLABELED ]]]
660 if 'security' in cfg and not cfg.get('security_label'):
661 secinfo = cfg['security']
662 # The xm command sends a list formatted like this:
663 # [['access_control', ['policy', 'xm-test'],['label', 'red']],
664 # ['ssidref', 196611]]
665 policy = ""
666 label = ""
667 for idx in range(0, len(secinfo)):
668 if secinfo[idx][0] == "access_control":
669 for aidx in range(1, len(secinfo[idx])):
670 if secinfo[idx][aidx][0] == "policy":
671 policy = secinfo[idx][aidx][1]
672 if secinfo[idx][aidx][0] == "label":
673 label = secinfo[idx][aidx][1]
674 cfg['security_label'] = \
675 security.set_security_label(policy, label)
676 if not sxp.child_value(sxp_cfg, 'security_label'):
677 del cfg['security']
679 sec_lab = cfg['security_label'].split(":")
680 if len(sec_lab) != 3:
681 raise XendConfigError("Badly formatted security label: %s"
682 % cfg['security_label'])
684 old_state = sxp.child_value(sxp_cfg, 'state')
685 if old_state:
686 for i in range(len(CONFIG_OLD_DOM_STATES)):
687 cfg[CONFIG_OLD_DOM_STATES[i]] = int(old_state[i] != '-')
689 return cfg
692 def _sxp_to_xapi(self, sxp_cfg):
693 """Read in an SXP Configuration object and
694 populate at much of the Xen API with valid values.
695 """
696 log.debug('_sxp_to_xapi(%s)' % scrub_password(sxp_cfg))
698 cfg = self._parse_sxp(sxp_cfg)
700 for key, typ in XENAPI_CFG_TYPES.items():
701 val = cfg.get(key)
702 if val is not None:
703 self[key] = typ(val)
705 # Convert parameters that can be directly mapped from
706 # the Legacy Config to Xen API Config
708 for apikey, cfgkey in XENAPI_CFG_TO_LEGACY_CFG.items():
709 try:
710 type_conv = XENAPI_CFG_TYPES.get(apikey)
711 if callable(type_conv):
712 self[apikey] = type_conv(cfg[cfgkey])
713 else:
714 log.warn("Unconverted key: " + apikey)
715 self[apikey] = cfg[cfgkey]
716 except KeyError:
717 pass
719 # Lets try and handle memory correctly
721 MiB = 1024 * 1024
723 if "memory" in cfg:
724 self["memory_static_min"] = 0
725 self["memory_static_max"] = int(cfg["memory"]) * MiB
726 self["memory_dynamic_min"] = int(cfg["memory"]) * MiB
727 self["memory_dynamic_max"] = int(cfg["memory"]) * MiB
729 if "maxmem" in cfg:
730 self["memory_static_max"] = int(cfg["maxmem"]) * MiB
732 self._memory_sanity_check()
734 def update_with(n, o):
735 if not self.get(n):
736 self[n] = cfg.get(o, '')
738 update_with('PV_bootloader', 'bootloader')
739 update_with('PV_bootloader_args', 'bootloader_args')
741 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
742 if image_sxp:
743 self.update_with_image_sxp(image_sxp)
745 # Convert Legacy HVM parameters to Xen API configuration
746 for key in XENAPI_PLATFORM_CFG:
747 if key in cfg:
748 self['platform'][key] = cfg[key]
750 # set device references in the configuration
751 self['devices'] = cfg.get('devices', {})
752 self['console_refs'] = cfg.get('console_refs', [])
753 self['vif_refs'] = cfg.get('vif_refs', [])
754 self['vbd_refs'] = cfg.get('vbd_refs', [])
755 self['vtpm_refs'] = cfg.get('vtpm_refs', [])
757 # coalesce hvm vnc frame buffer with vfb config
758 if self.is_hvm() and int(self['platform'].get('vnc', 0)) != 0:
759 # add vfb device if it isn't there already
760 if not self.has_rfb():
761 dev_config = ['vfb']
762 dev_config.append(['type', 'vnc'])
763 # copy VNC related params from platform config to vfb dev conf
764 for key in ['vncpasswd', 'vncunused', 'vncdisplay',
765 'vnclisten']:
766 if key in self['platform']:
767 dev_config.append([key, self['platform'][key]])
769 self.device_add('vfb', cfg_sxp = dev_config)
772 def has_rfb(self):
773 for console_uuid in self['console_refs']:
774 if self['devices'][console_uuid][1].get('protocol') == 'rfb':
775 return True
776 if self['devices'][console_uuid][0] == 'vfb':
777 return True
778 return False
780 def _sxp_to_xapi_unsupported(self, sxp_cfg):
781 """Read in an SXP configuration object and populate
782 values are that not related directly supported in
783 the Xen API.
784 """
786 log.debug('_sxp_to_xapi_unsupported(%s)' % scrub_password(sxp_cfg))
788 # Parse and convert parameters used to configure
789 # the image (as well as HVM images)
790 image_sxp = sxp.child_value(sxp_cfg, 'image', [])
791 if image_sxp:
792 image_type = sxp.name(image_sxp)
793 if image_type != 'hvm' and image_type != 'linux':
794 self['platform']['image_type'] = image_type
796 for key in XENAPI_PLATFORM_CFG:
797 val = sxp.child_value(image_sxp, key, None)
798 if val is not None and val != '':
799 self['platform'][key] = val
801 notes = sxp.children(image_sxp, 'notes')
802 if notes:
803 self['notes'] = self.notes_from_sxp(notes[0])
805 self._hvm_boot_params_from_sxp(image_sxp)
807 # extract backend value
809 backend = []
810 for c in sxp.children(sxp_cfg, 'backend'):
811 backend.append(sxp.name(sxp.child0(c)))
812 if backend:
813 self['backend'] = backend
815 # Parse and convert other Non Xen API parameters.
816 def _set_cfg_if_exists(sxp_arg):
817 val = sxp.child_value(sxp_cfg, sxp_arg)
818 if val != None:
819 if LEGACY_CFG_TYPES.get(sxp_arg):
820 self[sxp_arg] = LEGACY_CFG_TYPES[sxp_arg](val)
821 else:
822 self[sxp_arg] = val
824 _set_cfg_if_exists('shadow_memory')
825 _set_cfg_if_exists('features')
826 _set_cfg_if_exists('on_xend_stop')
827 _set_cfg_if_exists('on_xend_start')
828 _set_cfg_if_exists('vcpu_avail')
830 # Parse and store runtime configuration
831 _set_cfg_if_exists('start_time')
832 _set_cfg_if_exists('cpu_time')
833 _set_cfg_if_exists('shutdown_reason')
834 _set_cfg_if_exists('up_time')
835 _set_cfg_if_exists('status') # TODO, deprecated
837 def _get_old_state_string(self):
838 """Returns the old xm state string.
839 @rtype: string
840 @return: old state string
841 """
842 state_string = ''
843 for state_name in CONFIG_OLD_DOM_STATES:
844 on_off = self.get(state_name, 0)
845 if on_off:
846 state_string += state_name[0]
847 else:
848 state_string += '-'
850 return state_string
853 def update_config(self, dominfo):
854 """Update configuration with the output from xc.domain_getinfo().
856 @param dominfo: Domain information via xc.domain_getinfo()
857 @type dominfo: dict
858 """
859 self._dominfo_to_xapi(dominfo)
860 self.validate()
862 def update_with_xenapi_config(self, xapi):
863 """Update configuration with a Xen API VM struct
865 @param xapi: Xen API VM Struct
866 @type xapi: dict
867 """
869 log.debug('update_with_xenapi_config: %s' % scrub_password(xapi))
871 for key, val in xapi.items():
872 type_conv = XENAPI_CFG_TYPES.get(key)
873 if type_conv is None:
874 key = key.lower()
875 type_conv = XENAPI_CFG_TYPES.get(key)
876 if callable(type_conv):
877 self[key] = type_conv(val)
878 else:
879 self[key] = val
881 self['vcpus_params']['weight'] = \
882 int(self['vcpus_params'].get('weight', 256))
883 self['vcpus_params']['cap'] = int(self['vcpus_params'].get('cap', 0))
885 def to_sxp(self, domain = None, ignore_devices = False, ignore = [],
886 legacy_only = True):
887 """ Get SXP representation of this config object.
889 Incompat: removed store_mfn, console_mfn
891 @keyword domain: (optional) XendDomainInfo to get extra information
892 from such as domid and running devices.
893 @type domain: XendDomainInfo
894 @keyword ignore: (optional) list of 'keys' that we do not want
895 to export.
896 @type ignore: list of strings
897 @rtype: list of list (SXP representation)
898 """
899 sxpr = ['domain']
901 # TODO: domid/dom is the same thing but called differently
902 # depending if it is from xenstore or sxpr.
904 if domain.getDomid() is not None:
905 sxpr.append(['domid', domain.getDomid()])
907 if not legacy_only:
908 for name, typ in XENAPI_CFG_TYPES.items():
909 if name in self and self[name] not in (None, []):
910 if typ == dict:
911 s = self[name].items()
912 elif typ == list:
913 s = self[name]
914 else:
915 s = str(self[name])
916 sxpr.append([name, s])
918 for xenapi, legacy in XENAPI_CFG_TO_LEGACY_CFG.items():
919 if legacy in ('cpus'): # skip this
920 continue
921 if self.has_key(xenapi) and self[xenapi] not in (None, []):
922 if type(self[xenapi]) == bool:
923 # convert booleans to ints before making an sxp item
924 sxpr.append([legacy, int(self[xenapi])])
925 else:
926 sxpr.append([legacy, self[xenapi]])
928 MiB = 1024*1024
930 sxpr.append(["maxmem", int(self["memory_static_max"])/MiB])
931 sxpr.append(["memory", int(self["memory_dynamic_max"])/MiB])
933 for legacy in LEGACY_UNSUPPORTED_BY_XENAPI_CFG:
934 if legacy in ('domid', 'uuid', 'cpus'): # skip these
935 continue
936 if self.has_key(legacy) and self[legacy] not in (None, []):
937 sxpr.append([legacy, self[legacy]])
939 if self.has_key('security_label'):
940 sxpr.append(['security_label', self['security_label']])
942 sxpr.append(['image', self.image_sxpr()])
943 sxpr.append(['status', domain._stateGet()])
945 if domain.getDomid() is not None:
946 sxpr.append(['state', self._get_old_state_string()])
948 if domain:
949 if domain.store_mfn:
950 sxpr.append(['store_mfn', domain.store_mfn])
951 if domain.console_mfn:
952 sxpr.append(['console_mfn', domain.console_mfn])
955 # Marshall devices (running or from configuration)
956 if not ignore_devices:
957 txn = xstransact()
958 try:
959 for cls in XendDevices.valid_devices():
960 found = False
962 # figure if there is a dev controller is valid and running
963 if domain and domain.getDomid() != None:
964 try:
965 controller = domain.getDeviceController(cls)
966 configs = controller.configurations(txn)
967 for config in configs:
968 if sxp.name(config) in ('vbd', 'tap'):
969 # The bootable flag is never written to the
970 # store as part of the device config.
971 dev_uuid = sxp.child_value(config, 'uuid')
972 dev_type, dev_cfg = self['devices'][dev_uuid]
973 is_bootable = dev_cfg.get('bootable', 0)
974 config.append(['bootable', int(is_bootable)])
976 sxpr.append(['device', config])
978 found = True
979 except:
980 log.exception("dumping sxp from device controllers")
981 pass
983 # if we didn't find that device, check the existing config
984 # for a device in the same class
985 if not found:
986 for dev_type, dev_info in self.all_devices_sxpr():
987 if dev_type == cls:
988 sxpr.append(['device', dev_info])
990 txn.commit()
991 except:
992 txn.abort()
993 raise
995 return sxpr
997 def _blkdev_name_to_number(self, dev):
998 if 'ioemu:' in dev:
999 _, dev = dev.split(':', 1)
1000 try:
1001 dev, _ = dev.split(':', 1)
1002 except ValueError:
1003 pass
1005 try:
1006 devid = int(dev)
1007 except ValueError:
1008 # devid is not a number but a string containing either device
1009 # name (e.g. xvda) or device_type/device_id (e.g. vbd/51728)
1010 dev2 = type(dev) is str and dev.split('/')[-1] or None
1011 if dev2 == None:
1012 log.debug("Could not check the device %s", dev)
1013 return None
1014 try:
1015 devid = int(dev2)
1016 except ValueError:
1017 devid = blkdev_name_to_number(dev2)
1018 if devid == None:
1019 log.debug("The device %s is not device name", dev2)
1020 return None
1021 return devid
1023 def device_duplicate_check(self, dev_type, dev_info, defined_config):
1024 defined_devices_sxpr = self.all_devices_sxpr(target = defined_config)
1026 if dev_type == 'vbd':
1027 dev_uname = dev_info.get('uname')
1028 blkdev_name = dev_info.get('dev')
1029 devid = self._blkdev_name_to_number(blkdev_name)
1030 if devid == None:
1031 return
1033 for o_dev_type, o_dev_info in defined_devices_sxpr:
1034 if dev_type == o_dev_type:
1035 if dev_uname == sxp.child_value(o_dev_info, 'uname'):
1036 raise XendConfigError('The uname "%s" is already defined' %
1037 dev_uname)
1038 o_blkdev_name = sxp.child_value(o_dev_info, 'dev')
1039 o_devid = self._blkdev_name_to_number(o_blkdev_name)
1040 if o_devid != None and devid == o_devid:
1041 raise XendConfigError('The device "%s" is already defined' %
1042 blkdev_name)
1044 elif dev_type == 'vif':
1045 dev_mac = dev_info.get('mac')
1047 for o_dev_type, o_dev_info in defined_devices_sxpr:
1048 if dev_type == o_dev_type:
1049 if dev_mac == sxp.child_value(o_dev_info, 'mac'):
1050 raise XendConfigError('The mac "%s" is already defined' %
1051 dev_mac)
1053 def device_add(self, dev_type, cfg_sxp = None, cfg_xenapi = None,
1054 target = None):
1055 """Add a device configuration in SXP format or XenAPI struct format.
1057 For SXP, it could be either:
1059 [device, [vbd, [uname ...]]
1061 or:
1063 [vbd, [uname ..]]
1065 @type cfg_sxp: list of lists (parsed sxp object)
1066 @param cfg_sxp: SXP configuration object
1067 @type cfg_xenapi: dict
1068 @param cfg_xenapi: A device configuration from Xen API (eg. vbd,vif)
1069 @param target: write device information to
1070 @type target: None or a dictionary
1071 @rtype: string
1072 @return: Assigned UUID of the device.
1073 """
1074 if target == None:
1075 target = self
1077 if dev_type not in XendDevices.valid_devices():
1078 raise XendConfigError("XendConfig: %s not a valid device type" %
1079 dev_type)
1081 if cfg_sxp == None and cfg_xenapi == None:
1082 raise XendConfigError("XendConfig: device_add requires some "
1083 "config.")
1085 #if cfg_sxp:
1086 # log.debug("XendConfig.device_add: %s" % str(cfg_sxp))
1087 #if cfg_xenapi:
1088 # log.debug("XendConfig.device_add: %s" % str(cfg_xenapi))
1090 if cfg_sxp:
1091 if sxp.child0(cfg_sxp) == 'device':
1092 config = sxp.child0(cfg_sxp)
1093 else:
1094 config = cfg_sxp
1096 dev_type = sxp.name(config)
1097 dev_info = {}
1099 for opt_val in config[1:]:
1100 try:
1101 opt, val = opt_val
1102 dev_info[opt] = val
1103 except (TypeError, ValueError): # unpack error
1104 pass
1106 if dev_type == 'vbd':
1107 dev_info['bootable'] = 0
1108 if dev_info.get('dev', '').startswith('ioemu:'):
1109 dev_info['driver'] = 'ioemu'
1110 else:
1111 dev_info['driver'] = 'paravirtualised'
1113 if dev_type == 'tap':
1114 if dev_info['uname'].split(':')[1] not in blktap_disk_types:
1115 raise XendConfigError("tap:%s not a valid disk type" %
1116 dev_info['uname'].split(':')[1])
1118 if dev_type == 'vif':
1119 if not dev_info.get('mac'):
1120 dev_info['mac'] = randomMAC()
1122 self.device_duplicate_check(dev_type, dev_info, target)
1124 if dev_type == 'vif':
1125 if dev_info.get('policy') and dev_info.get('label'):
1126 dev_info['security_label'] = "%s:%s:%s" % \
1127 (xsconstants.ACM_POLICY_ID,
1128 dev_info['policy'],dev_info['label'])
1130 # create uuid if it doesn't exist
1131 dev_uuid = dev_info.get('uuid', None)
1132 if not dev_uuid:
1133 dev_uuid = uuid.createString()
1134 dev_info['uuid'] = dev_uuid
1136 # store dev references by uuid for certain device types
1137 target['devices'][dev_uuid] = (dev_type, dev_info)
1138 if dev_type in ('vif', 'vbd', 'vtpm'):
1139 param = '%s_refs' % dev_type
1140 if param not in target:
1141 target[param] = []
1142 if dev_uuid not in target[param]:
1143 if dev_type == 'vbd':
1144 # Compat hack -- mark first disk bootable
1145 dev_info['bootable'] = int(not target[param])
1146 target[param].append(dev_uuid)
1147 elif dev_type == 'tap':
1148 if 'vbd_refs' not in target:
1149 target['vbd_refs'] = []
1150 if dev_uuid not in target['vbd_refs']:
1151 # Compat hack -- mark first disk bootable
1152 dev_info['bootable'] = int(not target['vbd_refs'])
1153 target['vbd_refs'].append(dev_uuid)
1155 elif dev_type == 'vfb':
1156 # Populate other config with aux data that is associated
1157 # with vfb
1159 other_config = {}
1160 for key in XENAPI_CONSOLE_OTHER_CFG:
1161 if key in dev_info:
1162 other_config[key] = dev_info[key]
1163 target['devices'][dev_uuid][1]['other_config'] = other_config
1166 if 'console_refs' not in target:
1167 target['console_refs'] = []
1169 # Treat VFB devices as console devices so they are found
1170 # through Xen API
1171 if dev_uuid not in target['console_refs']:
1172 target['console_refs'].append(dev_uuid)
1174 elif dev_type == 'console':
1175 if 'console_refs' not in target:
1176 target['console_refs'] = []
1177 if dev_uuid not in target['console_refs']:
1178 target['console_refs'].append(dev_uuid)
1180 return dev_uuid
1182 if cfg_xenapi:
1183 dev_info = {}
1184 dev_uuid = ''
1185 if dev_type == 'vif':
1186 dev_info['mac'] = cfg_xenapi.get('MAC')
1187 if not dev_info['mac']:
1188 dev_info['mac'] = randomMAC()
1189 # vifname is the name on the guest, not dom0
1190 # TODO: we don't have the ability to find that out or
1191 # change it from dom0
1192 #if cfg_xenapi.get('device'): # don't add if blank
1193 # dev_info['vifname'] = cfg_xenapi.get('device')
1194 if cfg_xenapi.get('type'):
1195 dev_info['type'] = cfg_xenapi.get('type')
1196 if cfg_xenapi.get('name'):
1197 dev_info['name'] = cfg_xenapi.get('name')
1198 if cfg_xenapi.get('network'):
1199 network = XendAPIStore.get(
1200 cfg_xenapi.get('network'), 'network')
1201 dev_info['bridge'] = network.get_name_label()
1203 if cfg_xenapi.get('security_label'):
1204 dev_info['security_label'] = \
1205 cfg_xenapi.get('security_label')
1207 dev_uuid = cfg_xenapi.get('uuid', None)
1208 if not dev_uuid:
1209 dev_uuid = uuid.createString()
1210 dev_info['uuid'] = dev_uuid
1211 target['devices'][dev_uuid] = (dev_type, dev_info)
1212 target['vif_refs'].append(dev_uuid)
1214 elif dev_type in ('vbd', 'tap'):
1215 dev_info['type'] = cfg_xenapi.get('type', 'Disk')
1216 if dev_info['type'] == 'CD':
1217 old_vbd_type = 'cdrom'
1218 else:
1219 old_vbd_type = 'disk'
1221 dev_info['uname'] = cfg_xenapi.get('image', '')
1222 dev_info['dev'] = '%s:%s' % (cfg_xenapi.get('device'),
1223 old_vbd_type)
1224 dev_info['bootable'] = int(cfg_xenapi.get('bootable', 0))
1225 dev_info['driver'] = cfg_xenapi.get('driver', '')
1226 dev_info['VDI'] = cfg_xenapi.get('VDI', '')
1228 if cfg_xenapi.get('mode') == 'RW':
1229 dev_info['mode'] = 'w'
1230 else:
1231 dev_info['mode'] = 'r'
1233 dev_uuid = cfg_xenapi.get('uuid', None)
1234 if not dev_uuid:
1235 dev_uuid = uuid.createString()
1236 dev_info['uuid'] = dev_uuid
1237 target['devices'][dev_uuid] = (dev_type, dev_info)
1238 target['vbd_refs'].append(dev_uuid)
1240 elif dev_type == 'vtpm':
1241 if cfg_xenapi.get('type'):
1242 dev_info['type'] = cfg_xenapi.get('type')
1244 dev_uuid = cfg_xenapi.get('uuid', None)
1245 if not dev_uuid:
1246 dev_uuid = uuid.createString()
1247 dev_info['uuid'] = dev_uuid
1248 dev_info['other_config'] = cfg_xenapi.get('other_config', {})
1249 target['devices'][dev_uuid] = (dev_type, dev_info)
1250 target['vtpm_refs'].append(dev_uuid)
1252 elif dev_type == 'console':
1253 dev_uuid = cfg_xenapi.get('uuid', None)
1254 if not dev_uuid:
1255 dev_uuid = uuid.createString()
1256 dev_info['uuid'] = dev_uuid
1257 dev_info['protocol'] = cfg_xenapi.get('protocol', 'rfb')
1258 dev_info['other_config'] = cfg_xenapi.get('other_config', {})
1259 if dev_info['protocol'] == 'rfb':
1260 # collapse other config into devinfo for things
1261 # such as vncpasswd, vncunused, etc.
1262 dev_info.update(cfg_xenapi.get('other_config', {}))
1263 dev_info['type'] = 'vnc'
1264 target['devices'][dev_uuid] = ('vfb', dev_info)
1265 target['console_refs'].append(dev_uuid)
1267 # Finally, if we are a pvfb, we need to make a vkbd
1268 # as well that is not really exposed to Xen API
1269 vkbd_uuid = uuid.createString()
1270 target['devices'][vkbd_uuid] = ('vkbd', {})
1272 elif dev_info['protocol'] == 'vt100':
1273 # if someone tries to create a VT100 console
1274 # via the Xen API, we'll have to ignore it
1275 # because we create one automatically in
1276 # XendDomainInfo._update_consoles
1277 raise XendConfigError('Creating vt100 consoles via '
1278 'Xen API is unsupported')
1280 return dev_uuid
1282 # no valid device to add
1283 return ''
1285 def phantom_device_add(self, dev_type, cfg_xenapi = None,
1286 target = None):
1287 """Add a phantom tap device configuration in XenAPI struct format.
1288 """
1290 if target == None:
1291 target = self
1293 if dev_type not in XendDevices.valid_devices() and \
1294 dev_type not in XendDevices.pseudo_devices():
1295 raise XendConfigError("XendConfig: %s not a valid device type" %
1296 dev_type)
1298 if cfg_xenapi == None:
1299 raise XendConfigError("XendConfig: device_add requires some "
1300 "config.")
1302 if cfg_xenapi:
1303 log.debug("XendConfig.phantom_device_add: %s" % str(cfg_xenapi))
1305 if cfg_xenapi:
1306 dev_info = {}
1307 if dev_type in ('vbd', 'tap'):
1308 if dev_type == 'vbd':
1309 dev_info['uname'] = cfg_xenapi.get('image', '')
1310 dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
1311 elif dev_type == 'tap':
1312 if cfg_xenapi.get('image').find('tap:') == -1:
1313 dev_info['uname'] = 'tap:qcow:%s' % cfg_xenapi.get('image')
1314 dev_info['dev'] = '/dev/%s' % cfg_xenapi.get('device')
1315 dev_info['uname'] = cfg_xenapi.get('image')
1316 dev_info['mode'] = cfg_xenapi.get('mode')
1317 dev_info['backend'] = '0'
1318 dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
1319 dev_info['uuid'] = dev_uuid
1320 self['devices'][dev_uuid] = (dev_type, dev_info)
1321 self['vbd_refs'].append(dev_uuid)
1322 return dev_uuid
1324 return ''
1326 def console_add(self, protocol, location, other_config = {}):
1327 dev_uuid = uuid.createString()
1328 if protocol == 'vt100':
1329 dev_info = {
1330 'uuid': dev_uuid,
1331 'protocol': protocol,
1332 'location': location,
1333 'other_config': other_config,
1336 if 'devices' not in self:
1337 self['devices'] = {}
1339 self['devices'][dev_uuid] = ('console', dev_info)
1340 self['console_refs'].append(dev_uuid)
1341 return dev_info
1343 return {}
1345 def console_update(self, console_uuid, key, value):
1346 for dev_uuid, (dev_type, dev_info) in self['devices'].items():
1347 if dev_uuid == console_uuid:
1348 dev_info[key] = value
1349 # collapse other_config into dev_info for things
1350 # such as vncpasswd, vncunused, etc.
1351 if key == 'other_config':
1352 for k in XENAPI_CONSOLE_OTHER_CFG:
1353 if k in dev_info and k not in value:
1354 del dev_info[k]
1355 dev_info.update(value)
1356 break
1358 def console_get_all(self, protocol):
1359 if protocol == 'vt100':
1360 consoles = [dinfo for dtype, dinfo in self['devices'].values()
1361 if dtype == 'console']
1362 return [c for c in consoles if c.get('protocol') == protocol]
1364 elif protocol == 'rfb':
1365 vfbs = [dinfo for dtype, dinfo in self['devices'].values()
1366 if dtype == 'vfb']
1368 # move all non-console key values to other_config before
1369 # returning console config
1370 valid_keys = ['uuid', 'location']
1371 for vfb in vfbs:
1372 other_config = {}
1373 for key, val in vfb.items():
1374 if key not in valid_keys:
1375 other_config[key] = vfb[key]
1376 del vfb[key]
1377 vfb['other_config'] = other_config
1378 vfb['protocol'] = 'rfb'
1380 return vfbs
1382 else:
1383 return []
1385 def device_update(self, dev_uuid, cfg_sxp = [], cfg_xenapi = {}):
1386 """Update an existing device with the new configuration.
1388 @rtype: boolean
1389 @return: Returns True if succesfully found and updated a device conf
1390 """
1391 if dev_uuid in self['devices'] and cfg_sxp:
1392 if sxp.child0(cfg_sxp) == 'device':
1393 config = sxp.child0(cfg_sxp)
1394 else:
1395 config = cfg_sxp
1397 dev_type, dev_info = self['devices'][dev_uuid]
1398 for opt_val in config[1:]:
1399 try:
1400 opt, val = opt_val
1401 dev_info[opt] = val
1402 except (TypeError, ValueError):
1403 pass # no value for this config option
1405 self['devices'][dev_uuid] = (dev_type, dev_info)
1406 return True
1408 elif dev_uuid in self['devices'] and cfg_xenapi:
1409 dev_type, dev_info = self['devices'][dev_uuid]
1410 for key, val in cfg_xenapi.items():
1411 dev_info[key] = val
1412 self['devices'][dev_uuid] = (dev_type, dev_info)
1414 return False
1417 def device_sxpr(self, dev_uuid = None, dev_type = None, dev_info = None, target = None):
1418 """Get Device SXPR by either giving the device UUID or (type, config).
1420 @rtype: list of lists
1421 @return: device config sxpr
1422 """
1423 sxpr = []
1425 if target == None:
1426 target = self
1428 if dev_uuid != None and dev_uuid in target['devices']:
1429 dev_type, dev_info = target['devices'][dev_uuid]
1431 if dev_type == None or dev_info == None:
1432 raise XendConfigError("Required either UUID or device type and "
1433 "configuration dictionary.")
1435 sxpr.append(dev_type)
1436 if dev_type in ('console', 'vfb'):
1437 config = [(opt, val) for opt, val in dev_info.items()
1438 if opt != 'other_config']
1439 else:
1440 config = [(opt, val) for opt, val in dev_info.items()]
1442 sxpr += config
1444 return sxpr
1446 def ordered_device_refs(self, target = None):
1447 result = []
1449 if target == None:
1450 target = self
1452 # vkbd devices *must* be before vfb devices, otherwise
1453 # there is a race condition when setting up devices
1454 # where the daemon spawned for the vfb may write stuff
1455 # into xenstore vkbd backend, before DevController has
1456 # setup permissions on the vkbd backend path. This race
1457 # results in domain creation failing with 'device already
1458 # connected' messages
1459 result.extend([u for u in target['devices'].keys() if target['devices'][u][0] == 'vkbd'])
1461 result.extend(target.get('console_refs', []) +
1462 target.get('vbd_refs', []) +
1463 target.get('vif_refs', []) +
1464 target.get('vtpm_refs', []))
1466 result.extend([u for u in target['devices'].keys() if u not in result])
1467 return result
1469 def all_devices_sxpr(self, target = None):
1470 """Returns the SXPR for all devices in the current configuration."""
1471 sxprs = []
1472 pci_devs = []
1474 if target == None:
1475 target = self
1477 if 'devices' not in target:
1478 return sxprs
1480 ordered_refs = self.ordered_device_refs(target = target)
1481 for dev_uuid in ordered_refs:
1482 dev_type, dev_info = target['devices'][dev_uuid]
1483 if dev_type == 'pci': # special case for pci devices
1484 sxpr = ['pci', ['uuid', dev_info['uuid']]]
1485 for pci_dev_info in dev_info['devs']:
1486 pci_dev_sxpr = ['dev']
1487 for opt, val in pci_dev_info.items():
1488 pci_dev_sxpr.append([opt, val])
1489 sxpr.append(pci_dev_sxpr)
1490 sxprs.append((dev_type, sxpr))
1491 else:
1492 sxpr = self.device_sxpr(dev_type = dev_type,
1493 dev_info = dev_info,
1494 target = target)
1495 sxprs.append((dev_type, sxpr))
1497 return sxprs
1499 def image_sxpr(self):
1500 """Returns a backwards compatible image SXP expression that is
1501 used in xenstore's /vm/<uuid>/image value and xm list."""
1502 image = [self.image_type()]
1503 if self.has_key('PV_kernel'):
1504 image.append(['kernel', self['PV_kernel']])
1505 if self.has_key('PV_ramdisk') and self['PV_ramdisk']:
1506 image.append(['ramdisk', self['PV_ramdisk']])
1507 if self.has_key('PV_args') and self['PV_args']:
1508 image.append(['args', self['PV_args']])
1510 for key in XENAPI_PLATFORM_CFG:
1511 if key in self['platform']:
1512 image.append([key, self['platform'][key]])
1514 if 'notes' in self:
1515 image.append(self.notes_sxp(self['notes']))
1517 return image
1519 def update_with_image_sxp(self, image_sxp, bootloader = False):
1520 # Convert Legacy "image" config to Xen API PV_*
1521 # configuration
1522 log.debug("update_with_image_sxp(%s)" % scrub_password(image_sxp))
1524 # user-specified args must come last: previous releases did this and
1525 # some domU kernels rely upon the ordering.
1526 kernel_args = sxp.child_value(image_sxp, 'args', '')
1528 # attempt to extract extra arguments from SXP config
1529 arg_ip = sxp.child_value(image_sxp, 'ip')
1530 if arg_ip and not re.search(r'ip=[^ ]+', kernel_args):
1531 kernel_args = 'ip=%s ' % arg_ip + kernel_args
1532 arg_root = sxp.child_value(image_sxp, 'root')
1533 if arg_root and not re.search(r'root=', kernel_args):
1534 kernel_args = 'root=%s ' % arg_root + kernel_args
1536 if bootloader:
1537 self['_temp_using_bootloader'] = '1'
1538 self['_temp_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1539 self['_temp_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1540 self['_temp_args'] = kernel_args
1541 else:
1542 self['PV_kernel'] = sxp.child_value(image_sxp, 'kernel','')
1543 self['PV_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
1544 self['PV_args'] = kernel_args
1546 for key in XENAPI_PLATFORM_CFG:
1547 val = sxp.child_value(image_sxp, key, None)
1548 if val is not None and val != '':
1549 self['platform'][key] = val
1551 notes = sxp.children(image_sxp, 'notes')
1552 if notes:
1553 self['notes'] = self.notes_from_sxp(notes[0])
1555 self._hvm_boot_params_from_sxp(image_sxp)
1557 def set_notes(self, notes):
1558 'Add parsed elfnotes to image'
1559 self['notes'] = notes
1561 def get_notes(self):
1562 try:
1563 return self['notes'] or {}
1564 except KeyError:
1565 return {}
1567 def notes_from_sxp(self, nsxp):
1568 notes = {}
1569 for note in sxp.children(nsxp):
1570 notes[note[0]] = note[1]
1571 return notes
1573 def notes_sxp(self, notes):
1574 nsxp = ['notes']
1575 for k, v in notes.iteritems():
1576 nsxp.append([k, str(v)])
1577 return nsxp
1579 def _hvm_boot_params_from_sxp(self, image_sxp):
1580 boot = sxp.child_value(image_sxp, 'boot', None)
1581 if boot is not None:
1582 self['HVM_boot_policy'] = 'BIOS order'
1583 self['HVM_boot_params'] = { 'order' : boot }
1585 def is_hvm(self):
1586 return self['HVM_boot_policy'] != ''
1588 def image_type(self):
1589 stored_type = self['platform'].get('image_type')
1590 return stored_type or (self.is_hvm() and 'hvm' or 'linux')