direct-io.hg

view tools/python/xen/xend/XendStateStore.py @ 13584:6a54b1d8d105

[XEND] Make sure UUID in state store are not stored as unicode
objects.

Signed-off-by: Alastair Tse <atse@xensource.com>
author Alastair Tse <atse@xensource.com>
date Wed Jan 24 13:58:06 2007 +0000 (2007-01-24)
parents ae3f3dd40df4
children 03d0dda70a8f
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 uuid='6fc2d1ed-7eb0-4c9d-8006-3657d5483ae0' />
63 <cpu uuid='669df3b8-62be-4e61-800b-bbe8ee63a760' />
64 </host>
65 </hosts>
67 Note that it only dumps one level, so the references to CPU are
68 stored in a separate file.
70 """
72 def __init__(self, base = "/var/lib/xend/state"):
73 self.base = base
74 if not os.path.exists(self.base):
75 os.makedirs(self.base)
77 def _xml_file(self, cls):
78 """Return the absolute filename of the XML state storage file.
80 @param cls: name of the class.
81 @type cls: string
82 @rtype: string
83 @return absolute filename of XML file to write/read from.
84 """
85 return os.path.join(self.base, '%s.xml' % cls)
87 def load_state(self, cls):
88 """Load the saved state of a class from persistent XML storage.
90 References loaded from the XML will just point to an empty
91 dictionary which the caller will need to replace manually.
93 @param cls: name of the class to load.
94 @type cls: string
95 @rtype: dict
96 """
98 xml_path = self._xml_file(cls)
99 if not os.path.exists(xml_path):
100 return {}
102 dom = minidom.parse(xml_path)
103 root = dom.documentElement
104 state = {}
106 for child in root.childNodes:
107 if child.nodeType != Node.ELEMENT_NODE:
108 continue # skip non element nodes
110 uuid = child.getAttribute('uuid').encode('utf8')
111 cls_dict = {}
112 for val_elem in child.childNodes:
113 if val_elem.nodeType != Node.ELEMENT_NODE:
114 continue # skip non element nodes
116 val_name = val_elem.tagName
117 val_type = val_elem.getAttribute('type').encode('utf8')
118 val_uuid = val_elem.getAttribute('uuid').encode('utf8')
119 val_elem.normalize()
120 val_text = ''
121 if val_elem.firstChild:
122 val_text = val_elem.firstChild.nodeValue.strip()
124 if val_type == '' and val_uuid != '':
125 # this is a reference
126 if val_name not in cls_dict:
127 cls_dict[val_name] = {}
128 cls_dict[val_name][val_uuid] = None
129 elif val_type == 'string':
130 cls_dict[val_name] = val_text.encode('utf8')
131 elif val_type == 'float':
132 cls_dict[val_name] = float(val_text)
133 elif val_type == 'int':
134 cls_dict[val_name] = int(val_text)
135 elif val_type == 'bool':
136 cls_dict[val_name] = bool(int(val_text))
137 state[uuid] = cls_dict
139 return state
141 def save_state(self, cls, state):
142 """Save a Xen API record struct into an XML persistent storage
143 for future loading when Xend restarts.
145 If we encounter a dictionary or a list, we only store the
146 keys because they are going to be UUID references to another
147 object.
149 @param cls: Class name (singular) of the record
150 @type cls: string
151 @param state: a Xen API struct of the state of the class.
152 @type state: dict
153 @rtype: None
154 """
156 xml_path = self._xml_file(cls)
158 doc = minidom.getDOMImplementation().createDocument(None,
159 cls + 's',
160 None)
161 root = doc.documentElement
163 # Marshall a dictionary into our custom XML file format.
164 for uuid, info in state.items():
165 node = doc.createElement(cls)
166 root.appendChild(node)
167 node.setAttribute('uuid', uuid)
169 for key, val in info.items():
170 store_val = val
171 store_type = None
173 # deal with basic types
174 if type(val) in (str, unicode):
175 store_val = val
176 store_type = 'string'
177 elif type(val) == int:
178 store_val = str(val)
179 store_type = 'int'
180 elif type(val) == float:
181 store_val = str(val)
182 store_type = 'float'
183 elif type(val) == bool:
184 store_val = str(int(val))
185 store_type = 'bool'
187 if store_type != None:
188 val_node = doc.createElement(key)
189 val_node.setAttribute('type', store_type)
190 node.appendChild(val_node)
191 # attach the value
192 val_text = doc.createTextNode(store_val)
193 val_node.appendChild(val_text)
194 continue
196 # deal with dicts and lists
197 if type(val) == dict:
198 for val_uuid in val.keys():
199 val_node = doc.createElement(key)
200 val_node.setAttribute('uuid', val_uuid)
201 node.appendChild(val_node)
202 elif type(val) in (list, tuple):
203 for val_uuid in val:
204 val_node = doc.createElement(key)
205 val_node.setAttribute('uuid', val_uuid)
206 node.appendChild(val_node)
208 open(xml_path, 'w').write(doc.toprettyxml())