ia64/xen-unstable
changeset 13181:ae3f3dd40df4
Added support for state records in Xend, for keeping storage and network
details.
By Alastair Tse <atse@xensource.com>.
Signed-off-by: Ewan Mellor <ewan@xensource.com>
details.
By Alastair Tse <atse@xensource.com>.
Signed-off-by: Ewan Mellor <ewan@xensource.com>
author | Ewan Mellor <ewan@xensource.com> |
---|---|
date | Mon Dec 25 14:38:25 2006 +0000 (2006-12-25) |
parents | 4e079a8496b7 |
children | 362233086f66 |
files | tools/python/xen/xend/XendRoot.py tools/python/xen/xend/XendStateStore.py |
line diff
1.1 --- a/tools/python/xen/xend/XendRoot.py Mon Dec 25 14:32:41 2006 +0000 1.2 +++ b/tools/python/xen/xend/XendRoot.py Mon Dec 25 14:38:25 2006 +0000 1.3 @@ -104,6 +104,9 @@ class XendRoot: 1.4 """Default session storage path.""" 1.5 xend_domains_path_default = '/var/lib/xend/domains' 1.6 1.7 + """Default xend management state storage.""" 1.8 + xend_state_path_default = '/var/lib/xend/state' 1.9 + 1.10 components = {} 1.11 1.12 def __init__(self): 1.13 @@ -263,6 +266,11 @@ class XendRoot: 1.14 """ 1.15 return self.get_config_value("xend-domains-path", self.xend_domains_path_default) 1.16 1.17 + def get_xend_state_path(self): 1.18 + """ Get the path for persistent domain configuration storage 1.19 + """ 1.20 + return self.get_config_value("xend-state-path", self.xend_state_path_default) 1.21 + 1.22 def get_network_script(self): 1.23 """@return the script used to alter the network configuration when 1.24 Xend starts and stops, or None if no such script is specified."""
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/tools/python/xen/xend/XendStateStore.py Mon Dec 25 14:38:25 2006 +0000 2.3 @@ -0,0 +1,210 @@ 2.4 +#============================================================================ 2.5 +# This library is free software; you can redistribute it and/or 2.6 +# modify it under the terms of version 2.1 of the GNU Lesser General Public 2.7 +# License as published by the Free Software Foundation. 2.8 +# 2.9 +# This library is distributed in the hope that it will be useful, 2.10 +# but WITHOUT ANY WARRANTY; without even the implied warranty of 2.11 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 2.12 +# Lesser General Public License for more details. 2.13 +# 2.14 +# You should have received a copy of the GNU Lesser General Public 2.15 +# License along with this library; if not, write to the Free Software 2.16 +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 2.17 +#============================================================================ 2.18 +# Copyright (C) 2004, 2005 Mike Wray <mike.wray@hp.com> 2.19 +# Copyright (c) 2006 Xensource Inc. 2.20 +#============================================================================ 2.21 + 2.22 +import os 2.23 + 2.24 +from xen.xend import uuid 2.25 +from xen.xend.XendLogging import log 2.26 +from xml.dom import minidom 2.27 +from xml.dom import Node 2.28 + 2.29 +class XendStateStore: 2.30 + """Manages persistent storage of Xend's internal state, mainly 2.31 + relating to API objects. 2.32 + 2.33 + It stores objects atomically in the file system as flat XML files 2.34 + categorised by their 'class'. 2.35 + 2.36 + For example: 2.37 + 2.38 + /var/lib/xend/state/cpu.xml will contain the host cpu state 2.39 + /var/lib/xend/state/sr.xml will contain the storage repository state. 2.40 + 2.41 + For the application, it will load the state via this class: 2.42 + 2.43 + load_state('cpu') will return a marshalled dictionary object 2.44 + containing the cpu state. 2.45 + 2.46 + save_state('cpu', dict) will save the state contained in the dictionary 2.47 + object about the 'cpu'. 2.48 + 2.49 + The state is stored where each top level element has a UUID in its 2.50 + attributes. eg: 2.51 + 2.52 + host['49c01812-3c28-1ad4-a59d-2a3f81b13ec2'] = { 2.53 + 'name': 'norwich', 2.54 + 'desc': 'Test Xen Host', 2.55 + 'cpu': {'6fc2d1ed-7eb0-4c9d-8006-3657d5483ae0': <obj>, 2.56 + '669df3b8-62be-4e61-800b-bbe8ee63a760': <obj>} 2.57 + } 2.58 + 2.59 + will turn into: 2.60 + 2.61 + <hosts> 2.62 + <host uuid='49c01812-3c28-1ad4-a59d-2a3f81b13ec2'> 2.63 + <name type='string'>norwich</name> 2.64 + <description type='string'>Test Xen Host</description> 2.65 + <cpu uuid='6fc2d1ed-7eb0-4c9d-8006-3657d5483ae0' /> 2.66 + <cpu uuid='669df3b8-62be-4e61-800b-bbe8ee63a760' /> 2.67 + </host> 2.68 + </hosts> 2.69 + 2.70 + Note that it only dumps one level, so the references to CPU are 2.71 + stored in a separate file. 2.72 + 2.73 + """ 2.74 + 2.75 + def __init__(self, base = "/var/lib/xend/state"): 2.76 + self.base = base 2.77 + if not os.path.exists(self.base): 2.78 + os.makedirs(self.base) 2.79 + 2.80 + def _xml_file(self, cls): 2.81 + """Return the absolute filename of the XML state storage file. 2.82 + 2.83 + @param cls: name of the class. 2.84 + @type cls: string 2.85 + @rtype: string 2.86 + @return absolute filename of XML file to write/read from. 2.87 + """ 2.88 + return os.path.join(self.base, '%s.xml' % cls) 2.89 + 2.90 + def load_state(self, cls): 2.91 + """Load the saved state of a class from persistent XML storage. 2.92 + 2.93 + References loaded from the XML will just point to an empty 2.94 + dictionary which the caller will need to replace manually. 2.95 + 2.96 + @param cls: name of the class to load. 2.97 + @type cls: string 2.98 + @rtype: dict 2.99 + """ 2.100 + 2.101 + xml_path = self._xml_file(cls) 2.102 + if not os.path.exists(xml_path): 2.103 + return {} 2.104 + 2.105 + dom = minidom.parse(xml_path) 2.106 + root = dom.documentElement 2.107 + state = {} 2.108 + 2.109 + for child in root.childNodes: 2.110 + if child.nodeType != Node.ELEMENT_NODE: 2.111 + continue # skip non element nodes 2.112 + 2.113 + uuid = child.getAttribute('uuid') 2.114 + cls_dict = {} 2.115 + for val_elem in child.childNodes: 2.116 + if val_elem.nodeType != Node.ELEMENT_NODE: 2.117 + continue # skip non element nodes 2.118 + 2.119 + val_name = val_elem.tagName 2.120 + val_type = val_elem.getAttribute('type').encode('utf8') 2.121 + val_uuid = val_elem.getAttribute('uuid').encode('utf8') 2.122 + val_elem.normalize() 2.123 + val_text = '' 2.124 + if val_elem.firstChild: 2.125 + val_text = val_elem.firstChild.nodeValue.strip() 2.126 + 2.127 + if val_type == '' and val_uuid != '': 2.128 + # this is a reference 2.129 + if val_name not in cls_dict: 2.130 + cls_dict[val_name] = {} 2.131 + cls_dict[val_name][val_uuid] = None 2.132 + elif val_type == 'string': 2.133 + cls_dict[val_name] = val_text.encode('utf8') 2.134 + elif val_type == 'float': 2.135 + cls_dict[val_name] = float(val_text) 2.136 + elif val_type == 'int': 2.137 + cls_dict[val_name] = int(val_text) 2.138 + elif val_type == 'bool': 2.139 + cls_dict[val_name] = bool(int(val_text)) 2.140 + state[uuid] = cls_dict 2.141 + 2.142 + return state 2.143 + 2.144 + def save_state(self, cls, state): 2.145 + """Save a Xen API record struct into an XML persistent storage 2.146 + for future loading when Xend restarts. 2.147 + 2.148 + If we encounter a dictionary or a list, we only store the 2.149 + keys because they are going to be UUID references to another 2.150 + object. 2.151 + 2.152 + @param cls: Class name (singular) of the record 2.153 + @type cls: string 2.154 + @param state: a Xen API struct of the state of the class. 2.155 + @type state: dict 2.156 + @rtype: None 2.157 + """ 2.158 + 2.159 + xml_path = self._xml_file(cls) 2.160 + 2.161 + doc = minidom.getDOMImplementation().createDocument(None, 2.162 + cls + 's', 2.163 + None) 2.164 + root = doc.documentElement 2.165 + 2.166 + # Marshall a dictionary into our custom XML file format. 2.167 + for uuid, info in state.items(): 2.168 + node = doc.createElement(cls) 2.169 + root.appendChild(node) 2.170 + node.setAttribute('uuid', uuid) 2.171 + 2.172 + for key, val in info.items(): 2.173 + store_val = val 2.174 + store_type = None 2.175 + 2.176 + # deal with basic types 2.177 + if type(val) in (str, unicode): 2.178 + store_val = val 2.179 + store_type = 'string' 2.180 + elif type(val) == int: 2.181 + store_val = str(val) 2.182 + store_type = 'int' 2.183 + elif type(val) == float: 2.184 + store_val = str(val) 2.185 + store_type = 'float' 2.186 + elif type(val) == bool: 2.187 + store_val = str(int(val)) 2.188 + store_type = 'bool' 2.189 + 2.190 + if store_type != None: 2.191 + val_node = doc.createElement(key) 2.192 + val_node.setAttribute('type', store_type) 2.193 + node.appendChild(val_node) 2.194 + # attach the value 2.195 + val_text = doc.createTextNode(store_val) 2.196 + val_node.appendChild(val_text) 2.197 + continue 2.198 + 2.199 + # deal with dicts and lists 2.200 + if type(val) == dict: 2.201 + for val_uuid in val.keys(): 2.202 + val_node = doc.createElement(key) 2.203 + val_node.setAttribute('uuid', val_uuid) 2.204 + node.appendChild(val_node) 2.205 + elif type(val) in (list, tuple): 2.206 + for val_uuid in val: 2.207 + val_node = doc.createElement(key) 2.208 + val_node.setAttribute('uuid', val_uuid) 2.209 + node.appendChild(val_node) 2.210 + 2.211 + open(xml_path, 'w').write(doc.toprettyxml()) 2.212 + 2.213 +