~ubuntu-branches/ubuntu/utopic/xen/utopic

« back to all changes in this revision

Viewing changes to tools/python/xen/xend/XendStateStore.py

  • Committer: Bazaar Package Importer
  • Author(s): Bastian Blank
  • Date: 2010-05-06 15:47:38 UTC
  • mto: (1.3.1) (15.1.1 sid) (4.1.1 experimental)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20100506154738-agoz0rlafrh1fnq7
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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) 2004, 2005 Mike Wray <mike.wray@hp.com>
 
16
# Copyright (c) 2006 Xensource Inc.
 
17
#============================================================================
 
18
 
 
19
import os
 
20
 
 
21
from xen.xend import uuid
 
22
from xen.xend.XendLogging import log
 
23
from xml.dom import minidom
 
24
from xml.dom import Node
 
25
 
 
26
class XendStateStore:
 
27
    """Manages persistent storage of Xend's internal state, mainly
 
28
    relating to API objects.
 
29
 
 
30
    It stores objects atomically in the file system as flat XML files
 
31
    categorised by their 'class'.
 
32
 
 
33
    For example:
 
34
 
 
35
    /var/lib/xend/state/cpu.xml will contain the host cpu state
 
36
    /var/lib/xend/state/sr.xml  will contain the storage repository state.
 
37
 
 
38
    For the application, it will load the state via this class:
 
39
 
 
40
    load_state('cpu') will return a marshalled dictionary object
 
41
    containing the cpu state.
 
42
 
 
43
    save_state('cpu', dict) will save the state contained in the dictionary
 
44
    object about the 'cpu'.
 
45
 
 
46
    The state is stored where each top level element has a UUID in its
 
47
    attributes. eg:
 
48
 
 
49
    host['49c01812-3c28-1ad4-a59d-2a3f81b13ec2'] = {
 
50
       'name': 'norwich',
 
51
       'desc': 'Test Xen Host',
 
52
       'cpu': {'6fc2d1ed-7eb0-4c9d-8006-3657d5483ae0': <obj>,
 
53
               '669df3b8-62be-4e61-800b-bbe8ee63a760': <obj>}
 
54
    }
 
55
 
 
56
    will turn into:
 
57
 
 
58
    <hosts>
 
59
       <host uuid='49c01812-3c28-1ad4-a59d-2a3f81b13ec2'>
 
60
          <name type='string'>norwich</name>
 
61
          <description type='string'>Test Xen Host</description>
 
62
          <cpu type='dict'>
 
63
             <item uuid='6fc2d1ed-7eb0-4c9d-8006-3657d5483ae0' />
 
64
             <item uuid='669df3b8-62be-4e61-800b-bbe8ee63a760' />
 
65
          </cpu>
 
66
       </host>
 
67
    </hosts>
 
68
 
 
69
    Note that it only dumps one level, so the references to CPU are
 
70
    stored in a separate file.
 
71
 
 
72
    """
 
73
 
 
74
    def __init__(self, base = "/var/lib/xend/state"):
 
75
        self.base = base
 
76
        if not os.path.exists(self.base):
 
77
            os.makedirs(self.base)
 
78
 
 
79
    def _xml_file(self, cls):
 
80
        """Return the absolute filename of the XML state storage file.
 
81
 
 
82
        @param cls: name of the class.
 
83
        @type  cls: string
 
84
        @rtype: string
 
85
        @return absolute filename of XML file to write/read from.
 
86
        """
 
87
        return os.path.join(self.base, '%s.xml' % cls)
 
88
 
 
89
    def load_state(self, cls):
 
90
        """Load the saved state of a class from persistent XML storage.
 
91
 
 
92
        References loaded from the XML will just point to an empty
 
93
        dictionary which the caller will need to replace manually.
 
94
 
 
95
        @param cls: name of the class to load.
 
96
        @type  cls: string
 
97
        @rtype: dict
 
98
        """
 
99
        
 
100
        xml_path = self._xml_file(cls)
 
101
        if not os.path.exists(xml_path):
 
102
            return {}
 
103
 
 
104
        dom = minidom.parse(xml_path)
 
105
        root = dom.documentElement
 
106
        state = {}
 
107
 
 
108
        for child in root.childNodes:
 
109
            if child.nodeType != Node.ELEMENT_NODE:
 
110
                continue # skip non element nodes
 
111
                
 
112
            uuid = child.getAttribute('uuid').encode('utf8')
 
113
            cls_dict = {}
 
114
            for val_elem in child.childNodes:
 
115
                if val_elem.nodeType != Node.ELEMENT_NODE:
 
116
                    continue # skip non element nodes
 
117
                
 
118
                val_name = val_elem.tagName
 
119
                val_type = val_elem.getAttribute('type').encode('utf8')
 
120
                val_uuid = val_elem.getAttribute('uuid').encode('utf8')
 
121
                val_elem.normalize()
 
122
                val_text = ''
 
123
                if val_elem.firstChild:
 
124
                    val_text = val_elem.firstChild.nodeValue.strip()
 
125
                
 
126
                if val_type == 'list':
 
127
                    cls_dict[val_name] = []
 
128
                    for item in val_elem.childNodes:
 
129
                        if item.nodeType != Node.ELEMENT_NODE:
 
130
                            continue # skip non element nodes
 
131
                        cls_dict[val_name].append(item.getAttribute('uuid'))
 
132
                elif val_type == 'dict':
 
133
                    cls_dict[val_name] = {}
 
134
                    for item in val_elem.childNodes:
 
135
                        if item.nodeType != Node.ELEMENT_NODE:
 
136
                            continue # skip non element nodes
 
137
                        k = item.getAttribute('key').encode('utf8')
 
138
                        v = item.getAttribute('value').encode('utf8')
 
139
                        cls_dict[val_name][k] = v
 
140
                elif val_type == 'string':
 
141
                    cls_dict[val_name] = val_text.encode('utf8')
 
142
                elif val_type == 'float':
 
143
                    cls_dict[val_name] = float(val_text)
 
144
                elif val_type == 'int':
 
145
                    cls_dict[val_name] = int(val_text)
 
146
                elif val_type == 'bool':
 
147
                    cls_dict[val_name] = bool(int(val_text))
 
148
            state[uuid] = cls_dict
 
149
 
 
150
        dom.unlink()
 
151
        return state
 
152
 
 
153
    def save_state(self, cls, state):
 
154
        """Save a Xen API record struct into an XML persistent storage
 
155
        for future loading when Xend restarts.
 
156
 
 
157
        If we encounter a dictionary or a list, we only store the
 
158
        keys because they are going to be UUID references to another
 
159
        object.
 
160
 
 
161
        @param cls: Class name (singular) of the record
 
162
        @type  cls: string
 
163
        @param state: a Xen API struct of the state of the class.
 
164
        @type  state: dict
 
165
        @rtype: None
 
166
        """        
 
167
        xml_path = self._xml_file(cls)
 
168
 
 
169
        doc = minidom.getDOMImplementation().createDocument(None,
 
170
                                                            cls + 's',
 
171
                                                            None)
 
172
        root = doc.documentElement
 
173
 
 
174
        # Marshall a dictionary into our custom XML file format.
 
175
        for uuid, info in state.items():
 
176
            node = doc.createElement(cls)
 
177
            root.appendChild(node)
 
178
            node.setAttribute('uuid', uuid)
 
179
            
 
180
            for key, val in info.items():
 
181
                store_val = val
 
182
                store_type = None
 
183
 
 
184
                # deal with basic types
 
185
                if type(val) in (str, unicode):
 
186
                    store_val = val
 
187
                    store_type = 'string'
 
188
                elif type(val) == int:
 
189
                    store_val = str(val)
 
190
                    store_type = 'int'
 
191
                elif type(val) == float:
 
192
                    store_val = str(val)
 
193
                    store_type = 'float'
 
194
                elif type(val) == bool:
 
195
                    store_val = str(int(val))
 
196
                    store_type = 'bool'
 
197
 
 
198
                if store_type is not None:
 
199
                    val_node = doc.createElement(key)
 
200
                    val_node.setAttribute('type', store_type)
 
201
                    node.appendChild(val_node)
 
202
                    # attach the value
 
203
                    val_text = doc.createTextNode(store_val)
 
204
                    val_node.appendChild(val_text)
 
205
                    continue
 
206
 
 
207
                # deal with dicts and lists
 
208
                if type(val) == dict:
 
209
                    val_node = doc.createElement(key)
 
210
                    val_node.setAttribute('type', 'dict')
 
211
                    for val_item in val.keys():
 
212
                        tmp = doc.createElement("item")
 
213
                        if key in ['other_config', 'device_config']:
 
214
                            tmp.setAttribute('key', str(val_item))
 
215
                            tmp.setAttribute('value', str(val[val_item]))
 
216
                        else:
 
217
                            tmp.setAttribute('uuid', val_uuid)
 
218
                        val_node.appendChild(tmp)
 
219
                    node.appendChild(val_node)
 
220
                elif type(val) in (list, tuple):
 
221
                    val_node = doc.createElement(key)
 
222
                    val_node.setAttribute('type', 'list')
 
223
                    for val_uuid in val:
 
224
                        tmp = doc.createElement("item")
 
225
                        tmp.setAttribute('uuid', val_uuid)
 
226
                        val_node.appendChild(tmp)
 
227
                    node.appendChild(val_node)
 
228
 
 
229
        open(xml_path, 'w').write(doc.toprettyxml())
 
230
        doc.unlink()
 
231