00001 """\
00002 @file config.py
00003 @brief Utility module for parsing and accessing the indra.xml config file.
00004
00005 $LicenseInfo:firstyear=2006&license=mit$
00006
00007 Copyright (c) 2006-2008, Linden Research, Inc.
00008
00009 Permission is hereby granted, free of charge, to any person obtaining a copy
00010 of this software and associated documentation files (the "Software"), to deal
00011 in the Software without restriction, including without limitation the rights
00012 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00013 copies of the Software, and to permit persons to whom the Software is
00014 furnished to do so, subject to the following conditions:
00015
00016 The above copyright notice and this permission notice shall be included in
00017 all copies or substantial portions of the Software.
00018
00019 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00020 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00021 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00022 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00023 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00024 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
00025 THE SOFTWARE.
00026 $/LicenseInfo$
00027 """
00028
00029 import copy
00030 import os
00031 import traceback
00032 import time
00033 import types
00034
00035 from os.path import dirname, getmtime, join, realpath
00036 from indra.base import llsd
00037
00038 _g_config = None
00039
00040 class IndraConfig(object):
00041 """
00042 IndraConfig loads a 'indra' xml configuration file
00043 and loads into memory. This representation in memory
00044 can get updated to overwrite values or add new values.
00045
00046 The xml configuration file is considered a live file and changes
00047 to the file are checked and reloaded periodically. If a value had
00048 been overwritten via the update or set method, the loaded values
00049 from the file are ignored (the values from the update/set methods
00050 override)
00051 """
00052 def __init__(self, indra_config_file):
00053 self._indra_config_file = indra_config_file
00054 self._reload_check_interval = 30
00055 self._last_check_time = 0
00056 self._last_mod_time = 0
00057
00058 self._config_overrides = {}
00059 self._config_file_dict = {}
00060 self._combined_dict = {}
00061
00062 self._load()
00063
00064 def _load(self):
00065 if self._indra_config_file is None:
00066 return
00067
00068 config_file = open(self._indra_config_file)
00069 self._config_file_dict = llsd.parse(config_file.read())
00070 self._combine_dictionaries()
00071 config_file.close()
00072
00073 self._last_mod_time = self._get_last_modified_time()
00074 self._last_check_time = time.time()
00075
00076 def _get_last_modified_time(self):
00077 """
00078 Returns the mtime (last modified time) of the config file,
00079 if such exists.
00080 """
00081 if self._indra_config_file is not None:
00082 return os.path.getmtime(self._indra_config_file)
00083
00084 return 0
00085
00086 def _combine_dictionaries(self):
00087 self._combined_dict = {}
00088 self._combined_dict.update(self._config_file_dict)
00089 self._combined_dict.update(self._config_overrides)
00090
00091 def _reload_if_necessary(self):
00092 now = time.time()
00093
00094 if (now - self._last_check_time) > self._reload_check_interval:
00095 self._last_check_time = now
00096 try:
00097 modtime = self._get_last_modified_time()
00098 if modtime > self._last_mod_time:
00099 self._load()
00100 except OSError, e:
00101 if e.errno == errno.ENOENT:
00102
00103
00104
00105 print 'WARNING: Configuration file has been removed ' + (self._indra_config_file)
00106 print 'Disabling reloading of configuration file.'
00107
00108 traceback.print_exc()
00109
00110 self._indra_config_file = None
00111 self._last_check_time = 0
00112 self._last_mod_time = 0
00113 else:
00114 raise
00115
00116 def __getitem__(self, key):
00117 self._reload_if_necessary()
00118
00119 return self._combined_dict[key]
00120
00121 def get(self, key, default = None):
00122 try:
00123 return self.__getitem__(key)
00124 except KeyError:
00125 return default
00126
00127 def __setitem__(self, key, value):
00128 """
00129 Sets the value of the config setting of key to be newval
00130
00131 Once any key/value pair is changed via the set method,
00132 that key/value pair will remain set with that value until
00133 change via the update or set method
00134 """
00135 self._config_overrides[key] = value
00136 self._combine_dictionaries()
00137
00138 def set(self, key, newval):
00139 return self.__setitem__(key, newval)
00140
00141 def update(self, new_conf):
00142 """
00143 Load an XML file and apply its map as overrides or additions
00144 to the existing config. Update can be a file or a dict.
00145
00146 Once any key/value pair is changed via the update method,
00147 that key/value pair will remain set with that value until
00148 change via the update or set method
00149 """
00150 if isinstance(new_conf, dict):
00151 overrides = new_conf
00152 else:
00153
00154 config_file = open(new_conf)
00155 overrides = llsd.parse(config_file.read())
00156 config_file.close()
00157
00158 self._config_overrides.update(overrides)
00159 self._combine_dictionaries()
00160
00161 def as_dict(self):
00162 """
00163 Returns immutable copy of the IndraConfig as a dictionary
00164 """
00165 return copy.deepcopy(self._combined_dict)
00166
00167 def load(indra_xml_file = None):
00168 global _g_config
00169
00170 if indra_xml_file is None:
00171
00172
00173
00174
00175 indra_xml_file = realpath(
00176 dirname(realpath(__file__)) + "../../../../../../etc/indra.xml")
00177
00178 _g_config = IndraConfig(indra_xml_file)
00179
00180 def dump(indra_xml_file, indra_cfg = None, update_in_mem=False):
00181 '''
00182 Dump config contents into a file
00183 Kindof reverse of load.
00184 Optionally takes a new config to dump.
00185 Does NOT update global config unless requested.
00186 '''
00187 global _g_config
00188
00189 if not indra_cfg:
00190 if _g_config is None:
00191 return
00192
00193 indra_cfg = _g_config.as_dict()
00194
00195 if not indra_cfg:
00196 return
00197
00198 config_file = open(indra_xml_file, 'w')
00199 _config_xml = llsd.format_xml(indra_cfg)
00200 config_file.write(_config_xml)
00201 config_file.close()
00202
00203 if update_in_mem:
00204 update(indra_cfg)
00205
00206 def update(new_conf):
00207 global _g_config
00208
00209 if _g_config is None:
00210
00211
00212
00213
00214
00215 _g_config = IndraConfig(None)
00216
00217 return _g_config.update(new_conf)
00218
00219 def get(key, default = None):
00220 global _g_config
00221
00222 if _g_config is None:
00223 load()
00224
00225 return _g_config.get(key, default)
00226
00227 def set(key, newval):
00228 """
00229 Sets the value of the config setting of key to be newval
00230
00231 Once any key/value pair is changed via the set method,
00232 that key/value pair will remain set with that value until
00233 change via the update or set method or program termination
00234 """
00235 global _g_config
00236
00237 if _g_config is None:
00238 _g_config = IndraConfig(None)
00239
00240 _g_config.set(key, newval)
00241
00242 def get_config():
00243 global _g_config
00244 return _g_config