ia64/xen-unstable

changeset 12084:ec29b6262a8b

[XEND] XendConfig is an extended python dictionary that is used to exchange
VM configuration information to allow easy import and export to different
file types.

Signed-off-by: Alastair Tse <atse@xensource.com>
author Alastair Tse <atse@xensource.com>
date Thu Oct 05 17:29:19 2006 +0100 (2006-10-05)
parents ea65d8be211f
children 9a932b5c7947
files tools/python/xen/xend/XendConfig.py
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/tools/python/xen/xend/XendConfig.py	Thu Oct 05 17:29:19 2006 +0100
     1.3 @@ -0,0 +1,819 @@
     1.4 +#===========================================================================
     1.5 +# This library is free software; you can redistribute it and/or
     1.6 +# modify it under the terms of version 2.1 of the GNU Lesser General Public
     1.7 +# License as published by the Free Software Foundation.
     1.8 +#
     1.9 +# This library is distributed in the hope that it will be useful,
    1.10 +# but WITHOUT ANY WARRANTY; without even the implied warranty of
    1.11 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    1.12 +# Lesser General Public License for more details.
    1.13 +#
    1.14 +# You should have received a copy of the GNU Lesser General Public
    1.15 +# License along with this library; if not, write to the Free Software
    1.16 +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    1.17 +#============================================================================
    1.18 +# Copyright (C) 2006 XenSource Ltd
    1.19 +#============================================================================
    1.20 +
    1.21 +import re
    1.22 +import time
    1.23 +
    1.24 +from xen.xend import sxp
    1.25 +from xen.xend import uuid
    1.26 +from xen.xend.XendError import VmError
    1.27 +from xen.xend.XendDevices import XendDevices
    1.28 +from xen.xend.XendLogging import log
    1.29 +from xen.xend.PrettyPrint import prettyprintstring
    1.30 +
    1.31 +"""
    1.32 +XendConfig API
    1.33 +
    1.34 +  XendConfig will try to mirror as closely the Xen API VM Struct
    1.35 +  providing a backwards compatibility mode for SXP dumping, loading.
    1.36 +
    1.37 +XendConfig is a subclass of the python dict in order to emulate the
    1.38 +previous behaviour of the XendDomainInfo.info dictionary. However,
    1.39 +the new dictionary also exposes a set of attributes that implement
    1.40 +the Xen API VM configuration interface.
    1.41 +
    1.42 +Example:
    1.43 +
    1.44 +>>> cfg = XendConfig(cfg = dict_from_xc_domain_getinfo)
    1.45 +>>> cfg.name_label
    1.46 +Domain-0
    1.47 +>>> cfg['name']
    1.48 +Domain-0
    1.49 +>>> cfg.kernel_kernel
    1.50 +/boot/vmlinuz-xen
    1.51 +>>> cfg.kernel_initrd
    1.52 +/root/initrd
    1.53 +>>> cfg.kernel_args
    1.54 +root=/dev/sda1 ro
    1.55 +>>> cfg['image']
    1.56 +(linux
    1.57 +  (kernel /boot/vmlinuz-xen)
    1.58 +  (ramdisk /root/initrd)
    1.59 +  (root '/dev/sda1 ro'))
    1.60 +>>>  
    1.61 +
    1.62 +Internally, XendConfig will make sure changes via the old 'dict'
    1.63 +interface get reflected, if possible, to the attribute store.
    1.64 +
    1.65 +It does this by overriding __setitem__, __getitem__, __hasitem__,
    1.66 +__getattr__, __setattr__, __hasattr__.
    1.67 +
    1.68 +What this means is that as code is moved from the SXP interface to
    1.69 +the Xen API interface, we can spot unported code by tracing calls
    1.70 +to  __getitem__ and __setitem__.
    1.71 +
    1.72 +"""
    1.73 +
    1.74 +
    1.75 +LEGACY_CFG_TO_XENAPI_CFG = {
    1.76 +    'uuid': 'uuid',
    1.77 +    'vcpus': 'vcpus_number',
    1.78 +    'maxmem': 'memory_static_max',
    1.79 +    'memory': 'memory_static_min',
    1.80 +    'name': 'name_label',
    1.81 +    'on_poweroff': 'actions_after_shutdown',            
    1.82 +    'on_reboot': 'actions_after_reboot',
    1.83 +    'on_crash': 'actions_after_crash',
    1.84 +    'bootloader': 'boot_method',
    1.85 +    'kernel_kernel': 'kernel_kernel',
    1.86 +    'kernel_initrd': 'kernel_initrd',
    1.87 +    'kernel_args': 'kernel_args',
    1.88 +    }
    1.89 +
    1.90 +XENAPI_CFG_CUSTOM_TRANSLATE = [
    1.91 +    'vifs',
    1.92 +    'vbds',
    1.93 +    ]
    1.94 +
    1.95 +XENAPI_UNSUPPORTED_IN_LEGACY_CFG = [
    1.96 +    'name_description',
    1.97 +    'user_version',
    1.98 +    'is_a_template',
    1.99 +    'memory_dynamic_min',
   1.100 +    'memory_dynamic_max',
   1.101 +    'memory_actual',
   1.102 +    'vcpus_policy',
   1.103 +    'vcpus_params',
   1.104 +    'vcpus_features_required',
   1.105 +    'vcpus_features_can_use',
   1.106 +    'vcpus_features_force_on',
   1.107 +    'vcpus_features_force_off',
   1.108 +    'actions_after_suspend',
   1.109 +    'tpm_instance',
   1.110 +    'tpm_backends',
   1.111 +    'bios_boot',
   1.112 +    'platform_std_vga',
   1.113 +    'platform_serial',
   1.114 +    'platform_localtime',
   1.115 +    'platform_clock_offset',
   1.116 +    'platform_enable_audio',
   1.117 +    'builder',
   1.118 +    'grub_cmdline',
   1.119 +    'pci_bus',
   1.120 +    'otherconfig'
   1.121 +    ]
   1.122 +
   1.123 +##
   1.124 +## Xend Configuration Parameters
   1.125 +##
   1.126 +
   1.127 +
   1.128 +# All parameters of VMs that may be configured on-the-fly, or at start-up.
   1.129 +VM_CONFIG_ENTRIES = [
   1.130 +    ('autostart',  int),
   1.131 +    ('autostop',   int),    
   1.132 +    ('name',        str),
   1.133 +    ('on_crash',    str),
   1.134 +    ('on_poweroff', str),
   1.135 +    ('on_reboot',   str),
   1.136 +    ('on_xend_stop', str),        
   1.137 +]
   1.138 +
   1.139 +# All entries written to the store.  This is VM_CONFIG_ENTRIES, plus those
   1.140 +# entries written to the store that cannot be reconfigured on-the-fly.
   1.141 +VM_STORE_ENTRIES = [
   1.142 +    ('uuid',       str),
   1.143 +    ('vcpus',      int),
   1.144 +    ('vcpu_avail', int),
   1.145 +    ('memory',     int),
   1.146 +    ('maxmem',     int),
   1.147 +    ('start_time', float),
   1.148 +]
   1.149 +
   1.150 +VM_STORED_ENTRIES = VM_CONFIG_ENTRIES + VM_STORE_ENTRIES
   1.151 +
   1.152 +# Configuration entries that we expect to round-trip -- be read from the
   1.153 +# config file or xc, written to save-files (i.e. through sxpr), and reused as
   1.154 +# config on restart or restore, all without munging.  Some configuration
   1.155 +# entries are munged for backwards compatibility reasons, or because they
   1.156 +# don't come out of xc in the same form as they are specified in the config
   1.157 +# file, so those are handled separately.
   1.158 +
   1.159 +ROUNDTRIPPING_CONFIG_ENTRIES = [
   1.160 +    ('uuid',       str),
   1.161 +    ('vcpus',      int),
   1.162 +    ('vcpu_avail', int),
   1.163 +    ('cpu_weight', float),
   1.164 +    ('memory',     int),
   1.165 +    ('shadow_memory', int),
   1.166 +    ('maxmem',     int),
   1.167 +    ('bootloader', str),
   1.168 +    ('bootloader_args', str),
   1.169 +    ('features', str),
   1.170 +    ('localtime', int),
   1.171 +]
   1.172 +ROUNDTRIPPING_CONFIG_ENTRIES += VM_CONFIG_ENTRIES
   1.173 +
   1.174 +## Static Configuration
   1.175 +
   1.176 +STATIC_CONFIG_ENTRIES = [
   1.177 +    ('cpu',      int),
   1.178 +    ('cpus',     str),
   1.179 +    ('image',    list),
   1.180 +    ('security', list), # TODO: what if null?
   1.181 +]
   1.182 +
   1.183 +DEPRECATED_ENTRIES = [
   1.184 +    ('restart', str),
   1.185 +]
   1.186 +
   1.187 +##
   1.188 +## Config Choices
   1.189 +##
   1.190 +
   1.191 +CONFIG_RESTART_MODES = ('restart', 'destroy', 'preserve', 'rename-restart')
   1.192 +CONFIG_OLD_DOM_STATES = ('running', 'blocked', 'paused', 'shutdown',
   1.193 +                         'crashed', 'dying')
   1.194 +
   1.195 +##
   1.196 +## Defaults
   1.197 +##
   1.198 +
   1.199 +def DEFAULT_VCPUS(info):
   1.200 +    if 'max_vcpu_id' in info: return int(info['max_vcpu_id']) + 1
   1.201 +    else: return 1
   1.202 +
   1.203 +DEFAULT_CONFIGURATION = (
   1.204 +    ('uuid',         lambda info: uuid.createString()),
   1.205 +    ('name',         lambda info: 'Domain-' + info['uuid']),
   1.206 +
   1.207 +    ('on_poweroff',  lambda info: 'destroy'),
   1.208 +    ('on_reboot',    lambda info: 'restart'),
   1.209 +    ('on_crash',     lambda info: 'restart'),
   1.210 +    ('features',     lambda info: ''),
   1.211 +
   1.212 +    
   1.213 +    ('memory',       lambda info: 0),
   1.214 +    ('shadow_memory',lambda info: 0),
   1.215 +    ('maxmem',       lambda info: 0),
   1.216 +    ('bootloader',   lambda info: None),
   1.217 +    ('bootloader_args', lambda info: None),            
   1.218 +    ('backend',      lambda info: []),
   1.219 +    ('device',       lambda info: {}),
   1.220 +    ('image',        lambda info: None),
   1.221 +    ('security',     lambda info: []),
   1.222 +    ('autostart',    lambda info: 0),
   1.223 +    ('autostop',     lambda info: 0),
   1.224 +    ('on_xend_stop', lambda info: 'shutdown'),
   1.225 +
   1.226 +    ('cpus',         lambda info: []),
   1.227 +    ('cpu_weight',   lambda info: 1.0),
   1.228 +    ('vcpus',        lambda info: DEFAULT_VCPUS(info)),
   1.229 +    ('online_vcpus', lambda info: info['vcpus']),
   1.230 +    ('max_vcpu_id',  lambda info: info['vcpus']-1),
   1.231 +    ('vcpu_avail',   lambda info: (1<<info['vcpus'])-1),
   1.232 +
   1.233 +    # New for Xen API
   1.234 +    ('kernel_kernel', lambda info: ''),
   1.235 +    ('kernel_initrd', lambda info: ''),
   1.236 +    ('kernel_args',   lambda info: ''),
   1.237 +    
   1.238 +)
   1.239 +    
   1.240 +class XendConfigError(VmError):
   1.241 +    def __str__(self):
   1.242 +        return 'Invalid Configuration: %s' % str(self.value)
   1.243 +
   1.244 +##
   1.245 +## XendConfig SXP Config Compat
   1.246 +##
   1.247 +
   1.248 +class XendSXPConfig:
   1.249 +    def get_domid(self):
   1.250 +        pass
   1.251 +    def get_handle(self):
   1.252 +        return self['uuid']
   1.253 +        
   1.254 +
   1.255 +##
   1.256 +## XendConfig Class (an extended dictionary)
   1.257 +##
   1.258 +
   1.259 +class XendConfig(dict):
   1.260 +    """ Generic Configuration Parser accepting SXP, Python or XML.
   1.261 +    This is a dictionary-like object that is populated.
   1.262 +
   1.263 +    @ivar legacy: dictionary holding legacy xen domain info
   1.264 +    @ivar xenapi: dictionary holding xen api config info
   1.265 +    """
   1.266 +
   1.267 +    def __init__(self, filename = None, fd = None,
   1.268 +                 sxp = None, xml = None, pycfg = None, xenapi_vm = None,
   1.269 +                 cfg = {}):
   1.270 +        """Constructor. Provide either the filename, fd or sxp.
   1.271 +
   1.272 +        @keyword filename: filename of an SXP file
   1.273 +        @keyword fd: file descriptor of an SXP file
   1.274 +        @keyword sxp: a list of list of a parsed SXP
   1.275 +        @keyword xml: an XML tree object
   1.276 +        @keyword xenapi_vm: a struct passed from an XMLRPC call (Xen API)
   1.277 +        @keyword cfg: a dictionary of configuration (eg. from xc)
   1.278 +        """
   1.279 +        format = 'unknown'
   1.280 +
   1.281 +        self.xenapi = {}
   1.282 +
   1.283 +        if filename and not fd:
   1.284 +            fd = open(filename, 'r')
   1.285 +
   1.286 +        if fd:
   1.287 +            format = self._detect_format(fd)
   1.288 +        
   1.289 +        if fd:
   1.290 +            if format == 'sxp':
   1.291 +                sxp = self._read_sxp(fd)
   1.292 +            elif format == 'python' and filename != None:
   1.293 +                pycfg = self._read_python(filename)
   1.294 +            elif format == 'python' and filename == None:
   1.295 +                raise XendConfigError("Python files must be passed as a "
   1.296 +                                      "filename rather than file descriptor.")
   1.297 +            elif format == 'xml':
   1.298 +                xml = self._read_xml(fd)
   1.299 +            else:
   1.300 +                raise XendConfigError("Unable to determine format of file")
   1.301 +                
   1.302 +        if sxp:
   1.303 +            cfg = self._populate_from_sxp(sxp)
   1.304 +        if xml:
   1.305 +            cfg = self._populate_from_xml(xml)
   1.306 +        if pycfg:
   1.307 +            cfg = self._populate_from_python_config(pycfg)
   1.308 +        if xenapi_vm:
   1.309 +            cfg = self._populate_from_xenapi_vm(xenapi_vm)
   1.310 +            
   1.311 +        if cfg:
   1.312 +            self.update(cfg)
   1.313 +            
   1.314 +        if xenapi_vm:
   1.315 +            self.xenapi.update(xenapi_vm)
   1.316 +
   1.317 +        log.debug('XendConfig: %s' % str(self))
   1.318 +        self.validate()
   1.319 +
   1.320 +    #
   1.321 +    # Xen API Attribute Access
   1.322 +    #
   1.323 +
   1.324 +    def __getattr__(self, name):
   1.325 +        try:
   1.326 +            return dict.__getattr__(self, name)
   1.327 +        except AttributeError:
   1.328 +            try:
   1.329 +                return  self.__dict__['xenapi'][name]
   1.330 +            except KeyError:
   1.331 +                raise AttributeError("XendConfig Xen API has no attribute "
   1.332 +                                     "'%s'" % name)
   1.333 +            
   1.334 +
   1.335 +    def __setattr__(self, name, value):
   1.336 +        try:
   1.337 +            return dict.__setattr__(self, name, value)
   1.338 +        except AttributeError:
   1.339 +            self.xenapi[name] = value
   1.340 +            #self.set_legacy_api_with_xen_api_value(name, value)
   1.341 +
   1.342 +    def __delattr__(self, name):
   1.343 +        try:
   1.344 +            dict.__delattr__(self, name)
   1.345 +        except AttributeError:
   1.346 +            del self.xenapi[name]
   1.347 +        #self.del_legacy_api_with_xen_api_key(name)
   1.348 +
   1.349 +
   1.350 +    """
   1.351 +    #
   1.352 +    # Legacy API Attribute Access
   1.353 +    #
   1.354 +
   1.355 +    def __getitem__(self, key):
   1.356 +        try:
   1.357 +            return self.legacy[key]
   1.358 +        except KeyError:
   1.359 +            raise AttributeError, "XendConfig Legacy has no attribute '%s'"\
   1.360 +                  % key
   1.361 +
   1.362 +    def __setitem__(self, key, value):
   1.363 +        self.legacy[key] = value
   1.364 +        self.set_xen_api_with_legacy_api_value(key, value)
   1.365 +
   1.366 +    def __delitem__(self, key):
   1.367 +        del self.legacy[key]
   1.368 +        self.del_xen_api_with_legacy_api_key(key)
   1.369 +    """
   1.370 +    
   1.371 +
   1.372 +    def _detect_format(self, fd):
   1.373 +        """Detect the format of the configuration passed.
   1.374 +
   1.375 +        @param fd: file descriptor of contents to detect
   1.376 +        @rtype: string, 'sxp', 'xml', 'python' or 'unknown'
   1.377 +        """
   1.378 +        format = 'unknown'
   1.379 +        
   1.380 +        fd.seek(0)
   1.381 +        for line in fd:
   1.382 +            stripped = line.strip()
   1.383 +            if stripped:
   1.384 +                if re.search(r'^\(', stripped): 
   1.385 +                    format = 'sxp'
   1.386 +                elif re.search(r'^\<?xml', stripped):
   1.387 +                    format = 'xml'
   1.388 +                else:
   1.389 +                    format = 'python'
   1.390 +                break
   1.391 +
   1.392 +        fd.seek(0)
   1.393 +        return format
   1.394 +
   1.395 +    def _read_sxp(self, fd):
   1.396 +        """ Read and parse SXP (from SXP to list of lists)
   1.397 +
   1.398 +        @rtype: list of lists.
   1.399 +        """
   1.400 +        try:
   1.401 +            parsed = sxp.parse(fd)[0]
   1.402 +            return parsed
   1.403 +        except:
   1.404 +            raise
   1.405 +            return None
   1.406 +
   1.407 +    def _read_xml(self, fd):
   1.408 +        """TODO: Read and parse XML (from XML to dict)
   1.409 +
   1.410 +        @rtype: dict
   1.411 +        """
   1.412 +        raise NotImplementedError
   1.413 +
   1.414 +    def _read_python(self, filename):
   1.415 +        """Read and parse python module that represents the config.
   1.416 +
   1.417 +        @rtype: dict
   1.418 +        """
   1.419 +        cfg_globals = {}
   1.420 +        execfile(filename, cfg_globals, {})
   1.421 +        return cfg_globals
   1.422 +
   1.423 +    def _populate_from_sxp(self, parsed):
   1.424 +        """ Populate this XendConfig using the parsed SXP.
   1.425 +
   1.426 +        @rtype: dictionary
   1.427 +        """
   1.428 +        cfg = {}
   1.429 +
   1.430 +        # First step is to convert deprecated options to
   1.431 +        # current equivalents.
   1.432 +        
   1.433 +        restart = sxp.child_value(parsed, 'restart')
   1.434 +        if restart:
   1.435 +            if restart == 'onreboot':
   1.436 +                cfg['on_poweroff'] = 'destroy'
   1.437 +                cfg['on_reboot'] = 'restart'
   1.438 +                cfg['on_crash'] = 'destroy'
   1.439 +            elif restart == 'always':
   1.440 +                for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
   1.441 +                    cfg[opt] = 'restart'
   1.442 +            elif restart == 'never':
   1.443 +                for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
   1.444 +                    cfg[opt] = 'never'                
   1.445 +            else:
   1.446 +                log.warn('Ignoring unrecognised value for deprecated option:'
   1.447 +                         'restart = \'%s\'', restart)
   1.448 +
   1.449 +        # Only extract options we know about.
   1.450 +        all_params = VM_CONFIG_ENTRIES + ROUNDTRIPPING_CONFIG_ENTRIES + \
   1.451 +                     STATIC_CONFIG_ENTRIES
   1.452 +                     
   1.453 +        for key, typeconv in all_params:
   1.454 +            val = sxp.child_value(parsed, key)
   1.455 +            if val:
   1.456 +                try:
   1.457 +                    cfg[key] = typeconv(val)
   1.458 +                except ValueError:
   1.459 +                    pass
   1.460 +
   1.461 +        # Manually extract other complex configuration
   1.462 +        # options.
   1.463 +
   1.464 +        cfg['backend'] = []
   1.465 +        for c in sxp.children(parsed, 'backend'):
   1.466 +            cfg['backend'].append(sxp.name(sxp.child0(c)))
   1.467 +
   1.468 +        cfg['device'] = {}
   1.469 +        for dev in sxp.children(parsed, 'device'):
   1.470 +            config = sxp.child0(dev)
   1.471 +            dev_type = sxp.name(config)
   1.472 +            dev_info = {}
   1.473 +            for opt, val in config[1:]:
   1.474 +                dev_info[opt] = val
   1.475 +
   1.476 +            # create uuid if it doesn't
   1.477 +            dev_uuid = dev_info.get('uuid', uuid.createString())
   1.478 +            dev_info['uuid'] = dev_uuid
   1.479 +            cfg['device'][dev_uuid] = (dev_type, dev_info)
   1.480 +            
   1.481 +            #cfg['device'].append((sxp.name(config), config))
   1.482 +
   1.483 +
   1.484 +        # Extract missing data from configuration entries
   1.485 +        if 'image' in cfg:
   1.486 +            image_vcpus = sxp.child_value(cfg['image'], 'vcpus')
   1.487 +            if image_vcpus is not None:
   1.488 +                try:
   1.489 +                    if 'vcpus' not in cfg:
   1.490 +                        cfg['vcpus'] = int(image_vcpus)
   1.491 +                    elif cfg['vcpus'] != int(image_vcpus):
   1.492 +                        cfg['vcpus'] = int(image_vcpus)
   1.493 +                        log.warn('Overriding vcpus from %d to %d using image'
   1.494 +                                 'vcpus value.', cfg['vcpus'])
   1.495 +                except ValueError, e:
   1.496 +                    raise XendConfigError('integer expeceted: %s: %s' %
   1.497 +                                        str(cfg['image']), e)
   1.498 +
   1.499 +
   1.500 +        # Deprecated cpu configuration
   1.501 +        if 'cpu' in cfg:
   1.502 +            if 'cpus' in cfg:
   1.503 +                cfg['cpus'] = "%s,%s" % (str(cfg['cpu']), cfg['cpus'])
   1.504 +            else:
   1.505 +                cfg['cpus'] = str(cfg['cpu'])
   1.506 +
   1.507 +        # convert 'cpus' string to list of ints
   1.508 +        # 'cpus' supports a list of ranges (0-3), seperated by
   1.509 +        # commas, and negation, (^1).  
   1.510 +        # Precedence is settled by  order of the string:
   1.511 +        #     "0-3,^1"   -> [0,2,3]
   1.512 +        #     "0-3,^1,1" -> [0,1,2,3]
   1.513 +        try:
   1.514 +            if 'cpus' in cfg:
   1.515 +                cpus = []
   1.516 +                for c in cfg['cpus'].split(','):
   1.517 +                    if c.find('-') != -1:             
   1.518 +                        (x, y) = c.split('-')
   1.519 +                        for i in range(int(x), int(y)+1):
   1.520 +                            cpus.append(int(i))
   1.521 +                    else:
   1.522 +                        # remove this element from the list 
   1.523 +                        if c[0] == '^':
   1.524 +                            cpus = [x for x in cpus if x != int(c[1:])]
   1.525 +                        else:
   1.526 +                            cpus.append(int(c))
   1.527 +
   1.528 +                cfg['cpus'] = cpus
   1.529 +        except ValueError, e:
   1.530 +            raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
   1.531 +
   1.532 +        # Parse image SXP outside of image.py
   1.533 +        # - used to be only done in image.py
   1.534 +        if 'image' in cfg:
   1.535 +            cfg['kernel_kernel'] = sxp.child_value(cfg['image'], 'kernel','')
   1.536 +            cfg['kernel_initrd'] = sxp.child_value(cfg['image'], 'ramdisk','')
   1.537 +            kernel_args = sxp.child_value(cfg['image'], 'args', '')
   1.538 +
   1.539 +            # attempt to extract extra arguments from SXP config
   1.540 +            arg_ip = sxp.child_value(cfg['image'], 'ip')
   1.541 +            if arg_ip: kernel_args += ' ip=%s' % arg_ip
   1.542 +            arg_root = sxp.child_value(cfg['image'], 'root')
   1.543 +            if arg_root: kernel_args += ' root=%s' % arg_root
   1.544 +            
   1.545 +            cfg['kernel_args'] = kernel_args
   1.546 +
   1.547 +        # TODO: get states
   1.548 +        old_state = sxp.child_value(parsed, 'state')
   1.549 +        if old_state:
   1.550 +            for i in range(len(CONFIG_OLD_DOM_STATES)):
   1.551 +                cfg[CONFIG_OLD_DOM_STATES[i]] = (old_state[i] != '-')
   1.552 +
   1.553 +        # Xen API extra cfgs
   1.554 +        # ------------------
   1.555 +        cfg['vif_refs'] = []
   1.556 +        cfg['vbd_refs'] = []
   1.557 +        for dev_uuid, (dev_type, dev_info) in cfg['device'].items():
   1.558 +            if dev_type == 'vif':
   1.559 +                cfg['vif_refs'].append(dev_uuid)
   1.560 +            elif dev_type == 'vbd':
   1.561 +                cfg['vbd_refs'].append(dev_uuid)
   1.562 +                
   1.563 +        return cfg
   1.564 +
   1.565 +
   1.566 +    def _populate_from_xenapi_vm(self, xenapi_vm):
   1.567 +        cfg = {}
   1.568 +
   1.569 +        for cfgkey, apikey in LEGACY_CFG_TO_XENAPI_CFG.items():
   1.570 +            try:
   1.571 +                cfg[cfgkey] = xenapi_vm[apikey]
   1.572 +            except KeyError:
   1.573 +                pass
   1.574 +
   1.575 +        # Reconstruct image SXP 
   1.576 +        # TODO: get rid of SXP altogether from here
   1.577 +        sxp_image = ['linux']
   1.578 +        if xenapi_vm['kernel_kernel']:
   1.579 +            sxp_image.append(['kernel', xenapi_vm['kernel_kernel']])
   1.580 +        if xenapi_vm['kernel_initrd']:
   1.581 +            sxp_image.append(['ramdisk', xenapi_vm['kernel_initrd']])
   1.582 +        if xenapi_vm['kernel_args']:
   1.583 +            sxp_image.append(['args', xenapi_vm['kernel_args']])
   1.584 +        cfg['image'] = prettyprintstring(sxp_image)
   1.585 +
   1.586 +        # make sure device structures are there.
   1.587 +        if 'device' not in cfg:
   1.588 +            cfg['device'] = {}
   1.589 +        if 'vif_refs' not in cfg:
   1.590 +            cfg['vif_refs'] = []
   1.591 +        if 'vbd_refs' not in cfg:
   1.592 +            cfg['vbd_refs'] = []
   1.593 +
   1.594 +        return cfg
   1.595 +
   1.596 +
   1.597 +    def _sync_xen_api_from_legacy_api(self):
   1.598 +        """ Sync all the attributes that is supported by the Xen API
   1.599 +        from the legacy API configuration.
   1.600 +        """
   1.601 +        for cfgkey, apikey in LEGACY_CFG_TO_XENAPI_CFG.items():        
   1.602 +            if cfgkey in self:
   1.603 +                self.xenapi[apikey] = self[cfgkey]
   1.604 +
   1.605 +    def _sync_legacy_api_from_xen_api(self):
   1.606 +        for cfgkey, apikey in LEGACY_CFG_TO_XENAPI_CFG.items():
   1.607 +            if apikey in self.xenapi:
   1.608 +                self[cfgkey] = self.xenapi[apikey]
   1.609 +
   1.610 +
   1.611 +    def _populate_from_xml(self, parsed_xml):
   1.612 +        raise NotImplementedError
   1.613 +
   1.614 +    def _populate_from_python_config(self, parsed_py):
   1.615 +        raise NotImplementedError
   1.616 +        
   1.617 +
   1.618 +    def get_sxp(self, domain = None, ignore_devices = False, ignore = []):
   1.619 +        """ Get SXP representation of this config object.
   1.620 +
   1.621 +        Incompat: removed store_mfn, console_mfn
   1.622 +
   1.623 +        @keyword domain: (optional) XendDomainInfo to get extra information
   1.624 +                         from such as domid and running devices.
   1.625 +        @type    domain: XendDomainInfo
   1.626 +        @keyword ignore: (optional) list of 'keys' that we do not want
   1.627 +                         to export.
   1.628 +        @type    ignore: list of strings
   1.629 +        @rtype: list of list (SXP representation)
   1.630 +        """
   1.631 +        sxpr = ['domain']
   1.632 +
   1.633 +        # TODO: domid/dom is the same thing but called differently
   1.634 +        #       depending if it is from xenstore or sxpr.
   1.635 +
   1.636 +        if domain.getDomid() != None:
   1.637 +            sxpr.append(['domid', domain.getDomid()])
   1.638 +
   1.639 +        for cfg, typefunc in ROUNDTRIPPING_CONFIG_ENTRIES:
   1.640 +            if cfg in self:
   1.641 +                if self[cfg] != None:
   1.642 +                    sxpr.append([cfg, self[cfg]])
   1.643 +
   1.644 +        if 'image' in self:
   1.645 +            sxpr.append(['image', self['image']])
   1.646 +        if 'security' in self:
   1.647 +            sxpr.append(['security', self['security']])
   1.648 +        if 'shutdown_reason' in self:
   1.649 +            sxpr.append(['shutdown_reason', self['shutdown_reason']])
   1.650 +        if 'cpu_time' in self:
   1.651 +            sxpr.append(['cpu_time', self['cpu_time']/1e9])
   1.652 +
   1.653 +        sxpr.append(['online_vcpus', self['online_vcpus']])
   1.654 +
   1.655 +        if 'start_time' in self:
   1.656 +            uptime = time.time() - self['start_time']
   1.657 +            sxpr.append(['up_time', str(uptime)])
   1.658 +            sxpr.append(['start_time', str(self['start_time'])])
   1.659 +
   1.660 +        sxpr.append(['autostart', self.get('autostart', 0)])
   1.661 +        sxpr.append(['autostop', self.get('autostop', 0)])
   1.662 +        sxpr.append(['on_xend_stop', self.get('on_xend_stop', 'shutdown')])
   1.663 +
   1.664 +        sxpr.append(['status', domain.state])
   1.665 +
   1.666 +        # Marshall devices (running or from configuration)
   1.667 +        if not ignore_devices:
   1.668 +            for cls in XendDevices.valid_devices():
   1.669 +                found = False
   1.670 +                
   1.671 +                # figure if there is a device that is running
   1.672 +                if domain:
   1.673 +                    try:
   1.674 +                        controller = domain.getDeviceController(cls)
   1.675 +                        configs = controller.configurations()
   1.676 +                        for config in configs:
   1.677 +                            sxpr.append(['device', config])
   1.678 +                            found = True
   1.679 +                    except:
   1.680 +                        log.exception("dumping sxp from device controllers")
   1.681 +                        pass
   1.682 +                        
   1.683 +                # if we didn't find that device, check the existing config
   1.684 +                # for a device in the same class
   1.685 +                if not found:
   1.686 +                    for dev_type, dev_info in self.all_devices_sxpr():
   1.687 +                        if dev_type == cls:
   1.688 +                            sxpr.append(['device', dev_info])
   1.689 +
   1.690 +        return sxpr
   1.691 +
   1.692 +    def validate(self):
   1.693 +        """ Validate the configuration and fill in missing configuration
   1.694 +        with defaults.
   1.695 +        """
   1.696 +
   1.697 +        # Fill in default values
   1.698 +        for key, default_func in DEFAULT_CONFIGURATION:
   1.699 +            if key not in self:
   1.700 +                self[key] = default_func(self)
   1.701 +
   1.702 +        # Basic sanity checks
   1.703 +        if 'image' in self and isinstance(self['image'], str):
   1.704 +            self['image'] = sxp.from_string(self['image'])
   1.705 +        if 'security' in self and isinstance(self['security'], str):
   1.706 +            self['security'] = sxp.from_string(self['security'])
   1.707 +        if self['memory'] == 0 and 'mem_kb' in self:
   1.708 +            self['memory'] = (self['mem_kb'] + 1023)/1024
   1.709 +        if self['memory'] <= 0:
   1.710 +            raise XendConfigError('Invalid memory size: %s' %
   1.711 +                                  str(self['memory']))
   1.712 +
   1.713 +        self['maxmem'] = max(self['memory'], self['maxmem'])
   1.714 +
   1.715 +        # Verify devices
   1.716 +        for d_uuid, (d_type, d_info) in self['device'].items():
   1.717 +            if d_type not in XendDevices.valid_devices():
   1.718 +                raise XendConfigError('Invalid device (%s)' % d_type)
   1.719 +
   1.720 +        # Verify restart modes
   1.721 +        for event in ('on_poweroff', 'on_reboot', 'on_crash'):
   1.722 +            if self[event] not in CONFIG_RESTART_MODES:
   1.723 +                raise XendConfigError('Invalid restart event: %s = %s' % \
   1.724 +                                      (event, str(self[event])))
   1.725 +
   1.726 +    def device_add(self, dev_type, cfg_sxp = None, cfg_xenapi = None):
   1.727 +        if dev_type not in XendDevices.valid_devices():
   1.728 +            raise XendConfigError("XendConfig: %s not a valid device type" %
   1.729 +                            dev_type)
   1.730 +
   1.731 +        if cfg_sxp == None and cfg_xenapi == None:
   1.732 +            raise XendConfigError("XendConfig: device_add requires some "
   1.733 +                                  "config.")
   1.734 +
   1.735 +        if cfg_sxp:
   1.736 +            config = sxp.child0(cfg_sxp)
   1.737 +            dev_type = sxp.name(config)
   1.738 +            dev_info = {}
   1.739 +
   1.740 +            try:
   1.741 +                for opt, val in config[1:]:
   1.742 +                    dev_info[opt] = val
   1.743 +            except ValueError:
   1.744 +                log.debug('XendConfig.device_add: %s' % config)
   1.745 +                pass # SXP has no options for this device
   1.746 +
   1.747 +            # create uuid if it doesn't exist
   1.748 +            dev_uuid = dev_info.get('uuid', uuid.createString())
   1.749 +            dev_info['uuid'] = dev_uuid
   1.750 +            self['device'][dev_uuid] = (dev_type, dev_info)
   1.751 +            return dev_uuid
   1.752 +
   1.753 +        if cfg_xenapi:
   1.754 +            dev_info = {}            
   1.755 +            if dev_type == 'vif':
   1.756 +                if cfg_xenapi.get('MAC'): # don't add if blank
   1.757 +                    dev_info['mac'] = cfg_xenapi.get('MAC')
   1.758 +                # vifname is the name on the guest, not dom0
   1.759 +                # TODO: we don't have the ability to find that out or
   1.760 +                #       change it from dom0
   1.761 +                #if cfg_xenapi.get('device'):  # don't add if blank
   1.762 +                #    dev_info['vifname'] = cfg_xenapi.get('device')
   1.763 +                if cfg_xenapi.get('type'):
   1.764 +                    dev_info['type'] = cfg_xenapi.get('type')
   1.765 +                
   1.766 +                dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
   1.767 +                dev_info['uuid'] = dev_uuid
   1.768 +                self['device'][dev_uuid] = (dev_type, dev_info)
   1.769 +                return dev_uuid
   1.770 +            
   1.771 +            elif dev_type == 'vbd':
   1.772 +                dev_info['uname'] = cfg_xenapi.get('image', None)
   1.773 +                dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
   1.774 +                if cfg_xenapi.get('mode') == 'RW':
   1.775 +                    dev_info['mode'] = 'w'
   1.776 +                else:
   1.777 +                    dev_info['mode'] = 'r'
   1.778 +
   1.779 +                dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
   1.780 +                dev_info['uuid'] = dev_uuid
   1.781 +                self['device'][dev_uuid] = (dev_type, dev_info)
   1.782 +                return dev_uuid
   1.783 +                
   1.784 +                
   1.785 +        return ''
   1.786 +
   1.787 +    def device_sxpr(self, dev_uuid = None, dev_type = None, dev_info = None):
   1.788 +        """Get Device SXPR by either giving the device UUID or (type, config).
   1.789 +
   1.790 +        @rtype: list of lists
   1.791 +        @return: device config sxpr
   1.792 +        """
   1.793 +        sxpr = []
   1.794 +        if dev_uuid != None and dev_uuid in self['device']:
   1.795 +            dev_type, dev_info = self['device']
   1.796 +
   1.797 +        if dev_type == None or dev_info == None:
   1.798 +            raise XendConfigError("Required either UUID or device type and "
   1.799 +                                  "configuration dictionary.")
   1.800 +            
   1.801 +        sxpr.append(dev_type)
   1.802 +        config = [(opt, val) for opt, val in dev_info.items() \
   1.803 +                  if opt != 'type']
   1.804 +        sxpr += config
   1.805 +
   1.806 +        return sxpr
   1.807 +
   1.808 +    def all_devices_sxpr(self):
   1.809 +        sxprs = []
   1.810 +        for dev_type, dev_info in self['device'].values():
   1.811 +            sxpr =  self.device_sxpr(dev_type = dev_type, dev_info = dev_info)
   1.812 +            sxprs.append((dev_type, sxpr))
   1.813 +        return sxprs
   1.814 +
   1.815 +                     
   1.816 +#
   1.817 +# debugging 
   1.818 +#
   1.819 +
   1.820 +if __name__ == "__main__":
   1.821 +    pass
   1.822 +