/mysql_watcher/indra/base/config.py
Python | 266 lines | 257 code | 0 blank | 9 comment | 0 complexity | c2113a28d11612bb16115a02e125c337 MD5 | raw file
1"""\ 2@file config.py 3@brief Utility module for parsing and accessing the indra.xml config file. 4 5$LicenseInfo:firstyear=2006&license=mit$ 6 7Copyright (c) 2006-2009, Linden Research, Inc. 8 9Permission is hereby granted, free of charge, to any person obtaining a copy 10of this software and associated documentation files (the "Software"), to deal 11in the Software without restriction, including without limitation the rights 12to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13copies of the Software, and to permit persons to whom the Software is 14furnished to do so, subject to the following conditions: 15 16The above copyright notice and this permission notice shall be included in 17all copies or substantial portions of the Software. 18 19THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25THE SOFTWARE. 26$/LicenseInfo$ 27""" 28 29import copy 30import errno 31import os 32import traceback 33import time 34import types 35 36from os.path import dirname, getmtime, join, realpath 37from indra.base import llsd 38 39_g_config = None 40 41class IndraConfig(object): 42 """ 43 IndraConfig loads a 'indra' xml configuration file 44 and loads into memory. This representation in memory 45 can get updated to overwrite values or add new values. 46 47 The xml configuration file is considered a live file and changes 48 to the file are checked and reloaded periodically. If a value had 49 been overwritten via the update or set method, the loaded values 50 from the file are ignored (the values from the update/set methods 51 override) 52 """ 53 def __init__(self, indra_config_file): 54 self._indra_config_file = indra_config_file 55 self._reload_check_interval = 30 # seconds 56 self._last_check_time = 0 57 self._last_mod_time = 0 58 59 self._config_overrides = {} 60 self._config_file_dict = {} 61 self._combined_dict = {} 62 63 self._load() 64 65 def _load(self): 66 # if you initialize the IndraConfig with None, no attempt 67 # is made to load any files 68 if self._indra_config_file is None: 69 return 70 71 config_file = open(self._indra_config_file) 72 self._config_file_dict = llsd.parse(config_file.read()) 73 self._combine_dictionaries() 74 config_file.close() 75 76 self._last_mod_time = self._get_last_modified_time() 77 self._last_check_time = time.time() # now 78 79 def _get_last_modified_time(self): 80 """ 81 Returns the mtime (last modified time) of the config file, 82 if such exists. 83 """ 84 if self._indra_config_file is not None: 85 return os.path.getmtime(self._indra_config_file) 86 87 return 0 88 89 def _combine_dictionaries(self): 90 self._combined_dict = {} 91 self._combined_dict.update(self._config_file_dict) 92 self._combined_dict.update(self._config_overrides) 93 94 def _reload_if_necessary(self): 95 now = time.time() 96 97 if (now - self._last_check_time) > self._reload_check_interval: 98 self._last_check_time = now 99 try: 100 modtime = self._get_last_modified_time() 101 if modtime > self._last_mod_time: 102 self._load() 103 except OSError, e: 104 if e.errno == errno.ENOENT: # file not found 105 # someone messed with our internal state 106 # or removed the file 107 108 print 'WARNING: Configuration file has been removed ' + (self._indra_config_file) 109 print 'Disabling reloading of configuration file.' 110 111 traceback.print_exc() 112 113 self._indra_config_file = None 114 self._last_check_time = 0 115 self._last_mod_time = 0 116 else: 117 raise # pass the exception along to the caller 118 119 def __getitem__(self, key): 120 self._reload_if_necessary() 121 122 return self._combined_dict[key] 123 124 def get(self, key, default = None): 125 try: 126 return self.__getitem__(key) 127 except KeyError: 128 return default 129 130 def __setitem__(self, key, value): 131 """ 132 Sets the value of the config setting of key to be newval 133 134 Once any key/value pair is changed via the set method, 135 that key/value pair will remain set with that value until 136 change via the update or set method 137 """ 138 self._config_overrides[key] = value 139 self._combine_dictionaries() 140 141 def set(self, key, newval): 142 return self.__setitem__(key, newval) 143 144 def update(self, new_conf): 145 """ 146 Load an XML file and apply its map as overrides or additions 147 to the existing config. Update can be a file or a dict. 148 149 Once any key/value pair is changed via the update method, 150 that key/value pair will remain set with that value until 151 change via the update or set method 152 """ 153 if isinstance(new_conf, dict): 154 overrides = new_conf 155 else: 156 # assuming that it is a filename 157 config_file = open(new_conf) 158 overrides = llsd.parse(config_file.read()) 159 config_file.close() 160 161 self._config_overrides.update(overrides) 162 self._combine_dictionaries() 163 164 def as_dict(self): 165 """ 166 Returns immutable copy of the IndraConfig as a dictionary 167 """ 168 return copy.deepcopy(self._combined_dict) 169 170def load(config_xml_file = None): 171 global _g_config 172 173 load_default_files = config_xml_file is None 174 if load_default_files: 175 ## going from: 176 ## "/opt/linden/indra/lib/python/indra/base/config.py" 177 ## to: 178 ## "/opt/linden/etc/indra.xml" 179 config_xml_file = realpath( 180 dirname(realpath(__file__)) + "../../../../../../etc/indra.xml") 181 182 try: 183 _g_config = IndraConfig(config_xml_file) 184 except IOError: 185 # Failure to load passed in file 186 # or indra.xml default file 187 if load_default_files: 188 try: 189 config_xml_file = realpath( 190 dirname(realpath(__file__)) + "../../../../../../etc/globals.xml") 191 _g_config = IndraConfig(config_xml_file) 192 return 193 except IOError: 194 # Failure to load globals.xml 195 # fall to code below 196 pass 197 198 # Either failed to load passed in file 199 # or failed to load all default files 200 _g_config = IndraConfig(None) 201 202def dump(indra_xml_file, indra_cfg = None, update_in_mem=False): 203 ''' 204 Dump config contents into a file 205 Kindof reverse of load. 206 Optionally takes a new config to dump. 207 Does NOT update global config unless requested. 208 ''' 209 global _g_config 210 211 if not indra_cfg: 212 if _g_config is None: 213 return 214 215 indra_cfg = _g_config.as_dict() 216 217 if not indra_cfg: 218 return 219 220 config_file = open(indra_xml_file, 'w') 221 _config_xml = llsd.format_xml(indra_cfg) 222 config_file.write(_config_xml) 223 config_file.close() 224 225 if update_in_mem: 226 update(indra_cfg) 227 228def update(new_conf): 229 global _g_config 230 231 if _g_config is None: 232 # To keep with how this function behaved 233 # previously, a call to update 234 # before the global is defined 235 # make a new global config which does not 236 # load data from a file. 237 _g_config = IndraConfig(None) 238 239 return _g_config.update(new_conf) 240 241def get(key, default = None): 242 global _g_config 243 244 if _g_config is None: 245 load() 246 247 return _g_config.get(key, default) 248 249def set(key, newval): 250 """ 251 Sets the value of the config setting of key to be newval 252 253 Once any key/value pair is changed via the set method, 254 that key/value pair will remain set with that value until 255 change via the update or set method or program termination 256 """ 257 global _g_config 258 259 if _g_config is None: 260 _g_config = IndraConfig(None) 261 262 _g_config.set(key, newval) 263 264def get_config(): 265 global _g_config 266 return _g_config