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.
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.
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
#============================================================================
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
27
"""Manages persistent storage of Xend's internal state, mainly
28
relating to API objects.
30
It stores objects atomically in the file system as flat XML files
31
categorised by their 'class'.
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.
38
For the application, it will load the state via this class:
40
load_state('cpu') will return a marshalled dictionary object
41
containing the cpu state.
43
save_state('cpu', dict) will save the state contained in the dictionary
44
object about the 'cpu'.
46
The state is stored where each top level element has a UUID in its
49
host['49c01812-3c28-1ad4-a59d-2a3f81b13ec2'] = {
51
'desc': 'Test Xen Host',
52
'cpu': {'6fc2d1ed-7eb0-4c9d-8006-3657d5483ae0': <obj>,
53
'669df3b8-62be-4e61-800b-bbe8ee63a760': <obj>}
59
<host uuid='49c01812-3c28-1ad4-a59d-2a3f81b13ec2'>
60
<name type='string'>norwich</name>
61
<description type='string'>Test Xen Host</description>
63
<item uuid='6fc2d1ed-7eb0-4c9d-8006-3657d5483ae0' />
64
<item uuid='669df3b8-62be-4e61-800b-bbe8ee63a760' />
69
Note that it only dumps one level, so the references to CPU are
70
stored in a separate file.
74
def __init__(self, base = "/var/lib/xend/state"):
76
if not os.path.exists(self.base):
77
os.makedirs(self.base)
79
def _xml_file(self, cls):
80
"""Return the absolute filename of the XML state storage file.
82
@param cls: name of the class.
85
@return absolute filename of XML file to write/read from.
87
return os.path.join(self.base, '%s.xml' % cls)
89
def load_state(self, cls):
90
"""Load the saved state of a class from persistent XML storage.
92
References loaded from the XML will just point to an empty
93
dictionary which the caller will need to replace manually.
95
@param cls: name of the class to load.
100
xml_path = self._xml_file(cls)
101
if not os.path.exists(xml_path):
104
dom = minidom.parse(xml_path)
105
root = dom.documentElement
108
for child in root.childNodes:
109
if child.nodeType != Node.ELEMENT_NODE:
110
continue # skip non element nodes
112
uuid = child.getAttribute('uuid').encode('utf8')
114
for val_elem in child.childNodes:
115
if val_elem.nodeType != Node.ELEMENT_NODE:
116
continue # skip non element nodes
118
val_name = val_elem.tagName
119
val_type = val_elem.getAttribute('type').encode('utf8')
120
val_uuid = val_elem.getAttribute('uuid').encode('utf8')
123
if val_elem.firstChild:
124
val_text = val_elem.firstChild.nodeValue.strip()
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
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.
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
161
@param cls: Class name (singular) of the record
163
@param state: a Xen API struct of the state of the class.
167
xml_path = self._xml_file(cls)
169
doc = minidom.getDOMImplementation().createDocument(None,
172
root = doc.documentElement
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)
180
for key, val in info.items():
184
# deal with basic types
185
if type(val) in (str, unicode):
187
store_type = 'string'
188
elif type(val) == int:
191
elif type(val) == float:
194
elif type(val) == bool:
195
store_val = str(int(val))
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)
203
val_text = doc.createTextNode(store_val)
204
val_node.appendChild(val_text)
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]))
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')
224
tmp = doc.createElement("item")
225
tmp.setAttribute('uuid', val_uuid)
226
val_node.appendChild(tmp)
227
node.appendChild(val_node)
229
open(xml_path, 'w').write(doc.toprettyxml())