debuggers.hg

view tools/python/xen/xend/XendStateStore.py @ 0:7d21f7218375

Exact replica of unstable on 051908 + README-this
author Mukesh Rathor
date Mon May 19 15:34:57 2008 -0700 (2008-05-19)
parents
children b22f9ab1716a
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 dom = minidom.parse(xml_path)
105 root = dom.documentElement
106 state = {}
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')
113 cls_dict = {}
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')
121 val_elem.normalize()
122 val_text = ''
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
150 return state
152 def save_state(self, cls, state):
153 """Save a Xen API record struct into an XML persistent storage
154 for future loading when Xend restarts.
156 If we encounter a dictionary or a list, we only store the
157 keys because they are going to be UUID references to another
158 object.
160 @param cls: Class name (singular) of the record
161 @type cls: string
162 @param state: a Xen API struct of the state of the class.
163 @type state: dict
164 @rtype: None
165 """
166 xml_path = self._xml_file(cls)
168 doc = minidom.getDOMImplementation().createDocument(None,
169 cls + 's',
170 None)
171 root = doc.documentElement
173 # Marshall a dictionary into our custom XML file format.
174 for uuid, info in state.items():
175 node = doc.createElement(cls)
176 root.appendChild(node)
177 node.setAttribute('uuid', uuid)
179 for key, val in info.items():
180 store_val = val
181 store_type = None
183 # deal with basic types
184 if type(val) in (str, unicode):
185 store_val = val
186 store_type = 'string'
187 elif type(val) == int:
188 store_val = str(val)
189 store_type = 'int'
190 elif type(val) == float:
191 store_val = str(val)
192 store_type = 'float'
193 elif type(val) == bool:
194 store_val = str(int(val))
195 store_type = 'bool'
197 if store_type is not None:
198 val_node = doc.createElement(key)
199 val_node.setAttribute('type', store_type)
200 node.appendChild(val_node)
201 # attach the value
202 val_text = doc.createTextNode(store_val)
203 val_node.appendChild(val_text)
204 continue
206 # deal with dicts and lists
207 if type(val) == dict:
208 val_node = doc.createElement(key)
209 val_node.setAttribute('type', 'dict')
210 for val_item in val.keys():
211 tmp = doc.createElement("item")
212 if key in ['other_config', 'device_config']:
213 tmp.setAttribute('key', str(val_item))
214 tmp.setAttribute('value', str(val[val_item]))
215 else:
216 tmp.setAttribute('uuid', val_uuid)
217 val_node.appendChild(tmp)
218 node.appendChild(val_node)
219 elif type(val) in (list, tuple):
220 val_node = doc.createElement(key)
221 val_node.setAttribute('type', 'list')
222 for val_uuid in val:
223 tmp = doc.createElement("item")
224 tmp.setAttribute('uuid', val_uuid)
225 val_node.appendChild(tmp)
226 node.appendChild(val_node)
228 open(xml_path, 'w').write(doc.toprettyxml())