xcp-1.6-updates/xen-4.1.hg

view tools/python/xen/xend/XendStateStore.py @ 23275:369bf6c946b9

tools: xend: tolerate empty state/*.xml

Bugzilla 1680: Xend fails to start if /var/lib/xend/state/*.xml are empty
which I get often when replacing the Xen hypervisor with a newer version.

This can be easily be reproduced under Fedora Core 16 by installing
xen RPMs and then replacing the xen.gz with a newer version.

Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Signed-off-by: Anthony Low <shinji@pikopiko.org>
Committed-by: Ian Jackson <ian.jackson@eu.citrix.com>

xen-unstable changeset: 24140:a3a2e300951a
Backport-requested-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Committed-by: Ian Jackson <ian.jackson@eu.citrix.com>
author Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
date Tue Apr 03 16:06:10 2012 +0100 (2012-04-03)
parents e41d42ef4cd2
children
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) 2004, 2005 Mike Wray <mike.wray@hp.com>
16 # Copyright (c) 2006 Xensource Inc.
17 #============================================================================
19 import os
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
26 class XendStateStore:
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'.
33 For example:
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
47 attributes. eg:
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 }
56 will turn into:
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>
69 Note that it only dumps one level, so the references to CPU are
70 stored in a separate file.
72 """
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)
79 def _xml_file(self, cls):
80 """Return the absolute filename of the XML state storage file.
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)
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.
96 @type cls: string
97 @rtype: dict
98 """
100 xml_path = self._xml_file(cls)
101 if not os.path.exists(xml_path):
102 return {}
104 if not os.path.getsize(xml_path) == 0:
105 return {}
107 dom = minidom.parse(xml_path)
108 root = dom.documentElement
109 state = {}
111 for child in root.childNodes:
112 if child.nodeType != Node.ELEMENT_NODE:
113 continue # skip non element nodes
115 uuid = child.getAttribute('uuid').encode('utf8')
116 cls_dict = {}
117 for val_elem in child.childNodes:
118 if val_elem.nodeType != Node.ELEMENT_NODE:
119 continue # skip non element nodes
121 val_name = val_elem.tagName
122 val_type = val_elem.getAttribute('type').encode('utf8')
123 val_uuid = val_elem.getAttribute('uuid').encode('utf8')
124 val_elem.normalize()
125 val_text = ''
126 if val_elem.firstChild:
127 val_text = val_elem.firstChild.nodeValue.strip()
129 if val_type == 'list':
130 cls_dict[val_name] = []
131 for item in val_elem.childNodes:
132 if item.nodeType != Node.ELEMENT_NODE:
133 continue # skip non element nodes
134 cls_dict[val_name].append(item.getAttribute('uuid'))
135 elif val_type == 'dict':
136 cls_dict[val_name] = {}
137 for item in val_elem.childNodes:
138 if item.nodeType != Node.ELEMENT_NODE:
139 continue # skip non element nodes
140 k = item.getAttribute('key').encode('utf8')
141 v = item.getAttribute('value').encode('utf8')
142 cls_dict[val_name][k] = v
143 elif val_type == 'string':
144 cls_dict[val_name] = val_text.encode('utf8')
145 elif val_type == 'float':
146 cls_dict[val_name] = float(val_text)
147 elif val_type == 'int':
148 cls_dict[val_name] = int(val_text)
149 elif val_type == 'bool':
150 cls_dict[val_name] = bool(int(val_text))
151 state[uuid] = cls_dict
153 dom.unlink()
154 return state
156 def save_state(self, cls, state):
157 """Save a Xen API record struct into an XML persistent storage
158 for future loading when Xend restarts.
160 If we encounter a dictionary or a list, we only store the
161 keys because they are going to be UUID references to another
162 object.
164 @param cls: Class name (singular) of the record
165 @type cls: string
166 @param state: a Xen API struct of the state of the class.
167 @type state: dict
168 @rtype: None
169 """
170 xml_path = self._xml_file(cls)
172 doc = minidom.getDOMImplementation().createDocument(None,
173 cls + 's',
174 None)
175 root = doc.documentElement
177 # Marshall a dictionary into our custom XML file format.
178 for uuid, info in state.items():
179 node = doc.createElement(cls)
180 root.appendChild(node)
181 node.setAttribute('uuid', uuid)
183 for key, val in info.items():
184 store_val = val
185 store_type = None
187 # deal with basic types
188 if type(val) in (str, unicode):
189 store_val = val
190 store_type = 'string'
191 elif type(val) == int:
192 store_val = str(val)
193 store_type = 'int'
194 elif type(val) == float:
195 store_val = str(val)
196 store_type = 'float'
197 elif type(val) == bool:
198 store_val = str(int(val))
199 store_type = 'bool'
201 if store_type is not None:
202 val_node = doc.createElement(key)
203 val_node.setAttribute('type', store_type)
204 node.appendChild(val_node)
205 # attach the value
206 val_text = doc.createTextNode(store_val)
207 val_node.appendChild(val_text)
208 continue
210 # deal with dicts and lists
211 if type(val) == dict:
212 val_node = doc.createElement(key)
213 val_node.setAttribute('type', 'dict')
214 for val_item in val.keys():
215 tmp = doc.createElement("item")
216 if key in ['other_config', 'device_config']:
217 tmp.setAttribute('key', str(val_item))
218 tmp.setAttribute('value', str(val[val_item]))
219 else:
220 tmp.setAttribute('uuid', val_uuid)
221 val_node.appendChild(tmp)
222 node.appendChild(val_node)
223 elif type(val) in (list, tuple):
224 val_node = doc.createElement(key)
225 val_node.setAttribute('type', 'list')
226 for val_uuid in val:
227 tmp = doc.createElement("item")
228 tmp.setAttribute('uuid', val_uuid)
229 val_node.appendChild(tmp)
230 node.appendChild(val_node)
232 open(xml_path, 'w').write(doc.toprettyxml())
233 doc.unlink()