ia64/xen-unstable

changeset 13181:ae3f3dd40df4

Added support for state records in Xend, for keeping storage and network
details.

By Alastair Tse <atse@xensource.com>.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author Ewan Mellor <ewan@xensource.com>
date Mon Dec 25 14:38:25 2006 +0000 (2006-12-25)
parents 4e079a8496b7
children 362233086f66
files tools/python/xen/xend/XendRoot.py tools/python/xen/xend/XendStateStore.py
line diff
     1.1 --- a/tools/python/xen/xend/XendRoot.py	Mon Dec 25 14:32:41 2006 +0000
     1.2 +++ b/tools/python/xen/xend/XendRoot.py	Mon Dec 25 14:38:25 2006 +0000
     1.3 @@ -104,6 +104,9 @@ class XendRoot:
     1.4      """Default session storage path."""
     1.5      xend_domains_path_default = '/var/lib/xend/domains'
     1.6  
     1.7 +    """Default xend management state storage."""
     1.8 +    xend_state_path_default = '/var/lib/xend/state'
     1.9 +
    1.10      components = {}
    1.11  
    1.12      def __init__(self):
    1.13 @@ -263,6 +266,11 @@ class XendRoot:
    1.14          """
    1.15          return self.get_config_value("xend-domains-path", self.xend_domains_path_default)
    1.16  
    1.17 +    def get_xend_state_path(self):
    1.18 +        """ Get the path for persistent domain configuration storage
    1.19 +        """
    1.20 +        return self.get_config_value("xend-state-path", self.xend_state_path_default)    
    1.21 +
    1.22      def get_network_script(self):
    1.23          """@return the script used to alter the network configuration when
    1.24          Xend starts and stops, or None if no such script is specified."""
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/tools/python/xen/xend/XendStateStore.py	Mon Dec 25 14:38:25 2006 +0000
     2.3 @@ -0,0 +1,210 @@
     2.4 +#============================================================================
     2.5 +# This library is free software; you can redistribute it and/or
     2.6 +# modify it under the terms of version 2.1 of the GNU Lesser General Public
     2.7 +# License as published by the Free Software Foundation.
     2.8 +#
     2.9 +# This library is distributed in the hope that it will be useful,
    2.10 +# but WITHOUT ANY WARRANTY; without even the implied warranty of
    2.11 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    2.12 +# Lesser General Public License for more details.
    2.13 +#
    2.14 +# You should have received a copy of the GNU Lesser General Public
    2.15 +# License along with this library; if not, write to the Free Software
    2.16 +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    2.17 +#============================================================================
    2.18 +# Copyright (C) 2004, 2005 Mike Wray <mike.wray@hp.com>
    2.19 +# Copyright (c) 2006 Xensource Inc.
    2.20 +#============================================================================
    2.21 +
    2.22 +import os
    2.23 +
    2.24 +from xen.xend import uuid
    2.25 +from xen.xend.XendLogging import log
    2.26 +from xml.dom import minidom
    2.27 +from xml.dom import Node
    2.28 +
    2.29 +class XendStateStore:
    2.30 +    """Manages persistent storage of Xend's internal state, mainly
    2.31 +    relating to API objects.
    2.32 +
    2.33 +    It stores objects atomically in the file system as flat XML files
    2.34 +    categorised by their 'class'.
    2.35 +
    2.36 +    For example:
    2.37 +
    2.38 +    /var/lib/xend/state/cpu.xml will contain the host cpu state
    2.39 +    /var/lib/xend/state/sr.xml  will contain the storage repository state.
    2.40 +
    2.41 +    For the application, it will load the state via this class:
    2.42 +
    2.43 +    load_state('cpu') will return a marshalled dictionary object
    2.44 +    containing the cpu state.
    2.45 +
    2.46 +    save_state('cpu', dict) will save the state contained in the dictionary
    2.47 +    object about the 'cpu'.
    2.48 +
    2.49 +    The state is stored where each top level element has a UUID in its
    2.50 +    attributes. eg:
    2.51 +
    2.52 +    host['49c01812-3c28-1ad4-a59d-2a3f81b13ec2'] = {
    2.53 +       'name': 'norwich',
    2.54 +       'desc': 'Test Xen Host',
    2.55 +       'cpu': {'6fc2d1ed-7eb0-4c9d-8006-3657d5483ae0': <obj>,
    2.56 +               '669df3b8-62be-4e61-800b-bbe8ee63a760': <obj>}
    2.57 +    }
    2.58 +
    2.59 +    will turn into:
    2.60 +
    2.61 +    <hosts>
    2.62 +       <host uuid='49c01812-3c28-1ad4-a59d-2a3f81b13ec2'>
    2.63 +           <name type='string'>norwich</name>
    2.64 +           <description type='string'>Test Xen Host</description>
    2.65 +           <cpu uuid='6fc2d1ed-7eb0-4c9d-8006-3657d5483ae0' />
    2.66 +           <cpu uuid='669df3b8-62be-4e61-800b-bbe8ee63a760' />
    2.67 +       </host>
    2.68 +    </hosts>
    2.69 +
    2.70 +    Note that it only dumps one level, so the references to CPU are
    2.71 +    stored in a separate file.
    2.72 +
    2.73 +    """
    2.74 +
    2.75 +    def __init__(self, base = "/var/lib/xend/state"):
    2.76 +        self.base = base
    2.77 +        if not os.path.exists(self.base):
    2.78 +            os.makedirs(self.base)
    2.79 +
    2.80 +    def _xml_file(self, cls):
    2.81 +        """Return the absolute filename of the XML state storage file.
    2.82 +
    2.83 +        @param cls: name of the class.
    2.84 +        @type  cls: string
    2.85 +        @rtype: string
    2.86 +        @return absolute filename of XML file to write/read from.
    2.87 +        """
    2.88 +        return os.path.join(self.base, '%s.xml' % cls)
    2.89 +
    2.90 +    def load_state(self, cls):
    2.91 +        """Load the saved state of a class from persistent XML storage.
    2.92 +
    2.93 +        References loaded from the XML will just point to an empty
    2.94 +        dictionary which the caller will need to replace manually.
    2.95 +
    2.96 +        @param cls: name of the class to load.
    2.97 +        @type  cls: string
    2.98 +        @rtype: dict
    2.99 +        """
   2.100 +        
   2.101 +        xml_path = self._xml_file(cls)
   2.102 +        if not os.path.exists(xml_path):
   2.103 +            return {}
   2.104 +
   2.105 +        dom = minidom.parse(xml_path)
   2.106 +        root = dom.documentElement
   2.107 +        state = {}
   2.108 +
   2.109 +        for child in root.childNodes:
   2.110 +            if child.nodeType != Node.ELEMENT_NODE:
   2.111 +                continue # skip non element nodes
   2.112 +                
   2.113 +            uuid = child.getAttribute('uuid')
   2.114 +            cls_dict = {}
   2.115 +            for val_elem in child.childNodes:
   2.116 +                if val_elem.nodeType != Node.ELEMENT_NODE:
   2.117 +                    continue # skip non element nodes
   2.118 +                
   2.119 +                val_name = val_elem.tagName
   2.120 +                val_type = val_elem.getAttribute('type').encode('utf8')
   2.121 +                val_uuid = val_elem.getAttribute('uuid').encode('utf8')
   2.122 +                val_elem.normalize()
   2.123 +                val_text = ''
   2.124 +                if val_elem.firstChild:
   2.125 +                    val_text = val_elem.firstChild.nodeValue.strip()
   2.126 +                
   2.127 +                if val_type == '' and val_uuid != '':
   2.128 +                    # this is a reference
   2.129 +                    if val_name not in cls_dict:
   2.130 +                        cls_dict[val_name] = {}
   2.131 +                    cls_dict[val_name][val_uuid] = None
   2.132 +                elif val_type == 'string':
   2.133 +                    cls_dict[val_name] = val_text.encode('utf8')
   2.134 +                elif val_type == 'float':
   2.135 +                    cls_dict[val_name] = float(val_text)
   2.136 +                elif val_type == 'int':
   2.137 +                    cls_dict[val_name] = int(val_text)
   2.138 +                elif val_type == 'bool':
   2.139 +                    cls_dict[val_name] = bool(int(val_text))
   2.140 +            state[uuid] = cls_dict
   2.141 +
   2.142 +        return state
   2.143 +
   2.144 +    def save_state(self, cls, state):
   2.145 +        """Save a Xen API record struct into an XML persistent storage
   2.146 +        for future loading when Xend restarts.
   2.147 +
   2.148 +        If we encounter a dictionary or a list, we only store the
   2.149 +        keys because they are going to be UUID references to another
   2.150 +        object.
   2.151 +
   2.152 +        @param cls: Class name (singular) of the record
   2.153 +        @type  cls: string
   2.154 +        @param state: a Xen API struct of the state of the class.
   2.155 +        @type  state: dict
   2.156 +        @rtype: None
   2.157 +        """
   2.158 +        
   2.159 +        xml_path = self._xml_file(cls)
   2.160 +
   2.161 +        doc = minidom.getDOMImplementation().createDocument(None,
   2.162 +                                                            cls + 's',
   2.163 +                                                            None)
   2.164 +        root = doc.documentElement
   2.165 +
   2.166 +        # Marshall a dictionary into our custom XML file format.
   2.167 +        for uuid, info in state.items():
   2.168 +            node = doc.createElement(cls)
   2.169 +            root.appendChild(node)
   2.170 +            node.setAttribute('uuid', uuid)
   2.171 +            
   2.172 +            for key, val in info.items():
   2.173 +                store_val = val
   2.174 +                store_type = None
   2.175 +
   2.176 +                # deal with basic types
   2.177 +                if type(val) in (str, unicode):
   2.178 +                    store_val = val
   2.179 +                    store_type = 'string'
   2.180 +                elif type(val) == int:
   2.181 +                    store_val = str(val)
   2.182 +                    store_type = 'int'
   2.183 +                elif type(val) == float:
   2.184 +                    store_val = str(val)
   2.185 +                    store_type = 'float'
   2.186 +                elif type(val) == bool:
   2.187 +                    store_val = str(int(val))
   2.188 +                    store_type = 'bool'
   2.189 +
   2.190 +                if store_type != None:
   2.191 +                    val_node = doc.createElement(key)
   2.192 +                    val_node.setAttribute('type', store_type)
   2.193 +                    node.appendChild(val_node)
   2.194 +                    # attach the value
   2.195 +                    val_text = doc.createTextNode(store_val)
   2.196 +                    val_node.appendChild(val_text)
   2.197 +                    continue
   2.198 +
   2.199 +                # deal with dicts and lists
   2.200 +                if type(val) == dict:
   2.201 +                    for val_uuid in val.keys():
   2.202 +                        val_node = doc.createElement(key)
   2.203 +                        val_node.setAttribute('uuid', val_uuid)
   2.204 +                        node.appendChild(val_node)
   2.205 +                elif type(val) in (list, tuple):
   2.206 +                    for val_uuid in val:
   2.207 +                        val_node = doc.createElement(key)
   2.208 +                        val_node.setAttribute('uuid', val_uuid)
   2.209 +                        node.appendChild(val_node)
   2.210 +
   2.211 +        open(xml_path, 'w').write(doc.toprettyxml())
   2.212 +        
   2.213 +