global_config.py revision 773a86ebaa2e4e4cce213b8553aeb4b30c1f3a9d
1"""A singleton class for accessing global config values 2 3provides access to global configuration file 4""" 5 6# The config values can be stored in 3 config files: 7# global_config.ini 8# moblab_config.ini 9# shadow_config.ini 10# When the code is running in Moblab, config values in moblab config override 11# values in global config, and config values in shadow config override values 12# in both moblab and global config. 13# When the code is running in a non-Moblab host, moblab_config.ini is ignored. 14# Config values in shadow config will override values in global config. 15 16__author__ = 'raphtee@google.com (Travis Miller)' 17 18import os, sys, ConfigParser 19from autotest_lib.client.common_lib import error 20from autotest_lib.client.common_lib import lsbrelease_utils 21 22class ConfigError(error.AutotestError): 23 """Configuration error.""" 24 pass 25 26 27class ConfigValueError(ConfigError): 28 """Configuration value error, raised when value failed to be converted to 29 expected type.""" 30 pass 31 32 33 34common_lib_dir = os.path.dirname(sys.modules[__name__].__file__) 35client_dir = os.path.dirname(common_lib_dir) 36root_dir = os.path.dirname(client_dir) 37 38# Check if the config files are at autotest's root dir 39# This will happen if client is executing inside a full autotest tree, or if 40# other entry points are being executed 41global_config_path_root = os.path.join(root_dir, 'global_config.ini') 42moblab_config_path_root = os.path.join(root_dir, 'moblab_config.ini') 43shadow_config_path_root = os.path.join(root_dir, 'shadow_config.ini') 44config_in_root = os.path.exists(global_config_path_root) 45 46# Check if the config files are at autotest's client dir 47# This will happen if a client stand alone execution is happening 48global_config_path_client = os.path.join(client_dir, 'global_config.ini') 49config_in_client = os.path.exists(global_config_path_client) 50 51if config_in_root: 52 DEFAULT_CONFIG_FILE = global_config_path_root 53 if os.path.exists(moblab_config_path_root): 54 DEFAULT_MOBLAB_FILE = moblab_config_path_root 55 else: 56 DEFAULT_MOBLAB_FILE = None 57 if os.path.exists(shadow_config_path_root): 58 DEFAULT_SHADOW_FILE = shadow_config_path_root 59 else: 60 DEFAULT_SHADOW_FILE = None 61 RUNNING_STAND_ALONE_CLIENT = False 62elif config_in_client: 63 DEFAULT_CONFIG_FILE = global_config_path_client 64 DEFAULT_MOBLAB_FILE = None 65 DEFAULT_SHADOW_FILE = None 66 RUNNING_STAND_ALONE_CLIENT = True 67else: 68 DEFAULT_CONFIG_FILE = None 69 DEFAULT_MOBLAB_FILE = None 70 DEFAULT_SHADOW_FILE = None 71 RUNNING_STAND_ALONE_CLIENT = True 72 73class global_config_class(object): 74 """Object to access config values.""" 75 _NO_DEFAULT_SPECIFIED = object() 76 77 config = None 78 config_file = DEFAULT_CONFIG_FILE 79 moblab_file=DEFAULT_MOBLAB_FILE 80 shadow_file = DEFAULT_SHADOW_FILE 81 running_stand_alone_client = RUNNING_STAND_ALONE_CLIENT 82 83 84 def check_stand_alone_client_run(self): 85 """Check if this is a stand alone client that does not need config.""" 86 return self.running_stand_alone_client 87 88 89 def set_config_files(self, config_file=DEFAULT_CONFIG_FILE, 90 shadow_file=DEFAULT_SHADOW_FILE, 91 moblab_file=DEFAULT_MOBLAB_FILE): 92 self.config_file = config_file 93 self.moblab_file = moblab_file 94 self.shadow_file = shadow_file 95 self.config = None 96 97 98 def _handle_no_value(self, section, key, default): 99 if default is self._NO_DEFAULT_SPECIFIED: 100 msg = ("Value '%s' not found in section '%s'" % 101 (key, section)) 102 raise ConfigError(msg) 103 else: 104 return default 105 106 107 def get_section_values(self, section): 108 """ 109 Return a config parser object containing a single section of the 110 global configuration, that can be later written to a file object. 111 112 @param section: Section we want to turn into a config parser object. 113 @return: ConfigParser() object containing all the contents of section. 114 """ 115 cfgparser = ConfigParser.ConfigParser() 116 cfgparser.add_section(section) 117 for option, value in self.config.items(section): 118 cfgparser.set(section, option, value) 119 return cfgparser 120 121 122 def get_config_value(self, section, key, type=str, 123 default=_NO_DEFAULT_SPECIFIED, allow_blank=False): 124 """Get a configuration value 125 126 @param section: Section the key is in. 127 @param key: The key to look up. 128 @param type: The expected type of the returned value. 129 @param default: A value to return in case the key couldn't be found. 130 @param allow_blank: If False, an empty string as a value is treated like 131 there was no value at all. If True, empty strings 132 will be returned like they were normal values. 133 134 @raises ConfigError: If the key could not be found and no default was 135 specified. 136 137 @return: The obtained value or default. 138 """ 139 self._ensure_config_parsed() 140 141 try: 142 val = self.config.get(section, key) 143 except ConfigParser.Error: 144 return self._handle_no_value(section, key, default) 145 146 if not val.strip() and not allow_blank: 147 return self._handle_no_value(section, key, default) 148 149 return self._convert_value(key, section, val, type) 150 151 152 # This order of parameters ensures this can be called similar to the normal 153 # get_config_value which is mostly called with (section, key, type). 154 def get_config_value_with_fallback(self, section, key, fallback_key, 155 type=str, fallback_section=None, 156 default=_NO_DEFAULT_SPECIFIED, **kwargs): 157 """Get a configuration value if it exists, otherwise use fallback. 158 159 Tries to obtain a configuration value for a given key. If this value 160 does not exist, the value looked up under a different key will be 161 returned. 162 163 @param section: Section the key is in. 164 @param key: The key to look up. 165 @param fallback_key: The key to use in case the original key wasn't 166 found. 167 @param type: data type the value should have. 168 @param fallback_section: The section the fallback key resides in. In 169 case none is specified, the the same section as 170 for the primary key is used. 171 @param default: Value to return if values could neither be obtained for 172 the key nor the fallback key. 173 @param **kwargs: Additional arguments that should be passed to 174 get_config_value. 175 176 @raises ConfigError: If the fallback key doesn't exist and no default 177 was provided. 178 179 @return: The value that was looked up for the key. If that didn't 180 exist, the value looked up for the fallback key will be 181 returned. If that also didn't exist, default will be returned. 182 """ 183 if fallback_section is None: 184 fallback_section = section 185 186 try: 187 return self.get_config_value(section, key, type, **kwargs) 188 except ConfigError: 189 return self.get_config_value(fallback_section, fallback_key, 190 type, default=default, **kwargs) 191 192 193 def override_config_value(self, section, key, new_value): 194 """Override a value from the config file with a new value. 195 196 @param section: Name of the section. 197 @param key: Name of the key. 198 @param new_value: new value. 199 """ 200 self._ensure_config_parsed() 201 self.config.set(section, key, new_value) 202 203 204 def reset_config_values(self): 205 """ 206 Reset all values to those found in the config files (undoes all 207 overrides). 208 """ 209 self.parse_config_file() 210 211 212 def _ensure_config_parsed(self): 213 """Make sure config files are parsed. 214 """ 215 if self.config is None: 216 self.parse_config_file() 217 218 219 def merge_configs(self, override_config): 220 """Merge existing config values with the ones in given override_config. 221 222 @param override_config: Configs to override existing config values. 223 """ 224 # overwrite whats in config with whats in override_config 225 sections = override_config.sections() 226 for section in sections: 227 # add the section if need be 228 if not self.config.has_section(section): 229 self.config.add_section(section) 230 # now run through all options and set them 231 options = override_config.options(section) 232 for option in options: 233 val = override_config.get(section, option) 234 self.config.set(section, option, val) 235 236 237 def parse_config_file(self): 238 """Parse config files.""" 239 self.config = ConfigParser.ConfigParser() 240 if self.config_file and os.path.exists(self.config_file): 241 self.config.read(self.config_file) 242 else: 243 raise ConfigError('%s not found' % (self.config_file)) 244 245 # If it's running in Moblab, read moblab config file if exists, 246 # overwrite the value in global config. 247 if (lsbrelease_utils.is_moblab() and self.moblab_file and 248 os.path.exists(self.moblab_file)): 249 moblab_config = ConfigParser.ConfigParser() 250 moblab_config.read(self.moblab_file) 251 # now we merge moblab into global 252 self.merge_configs(moblab_config) 253 254 # now also read the shadow file if there is one 255 # this will overwrite anything that is found in the 256 # other config 257 if self.shadow_file and os.path.exists(self.shadow_file): 258 shadow_config = ConfigParser.ConfigParser() 259 shadow_config.read(self.shadow_file) 260 # now we merge shadow into global 261 self.merge_configs(shadow_config) 262 263 264 # the values that are pulled from ini 265 # are strings. But we should attempt to 266 # convert them to other types if needed. 267 def _convert_value(self, key, section, value, value_type): 268 # strip off leading and trailing white space 269 sval = value.strip() 270 271 # if length of string is zero then return None 272 if len(sval) == 0: 273 if value_type == str: 274 return "" 275 elif value_type == bool: 276 return False 277 elif value_type == int: 278 return 0 279 elif value_type == float: 280 return 0.0 281 elif value_type == list: 282 return [] 283 else: 284 return None 285 286 if value_type == bool: 287 if sval.lower() == "false": 288 return False 289 else: 290 return True 291 292 if value_type == list: 293 # Split the string using ',' and return a list 294 return [val.strip() for val in sval.split(',')] 295 296 try: 297 conv_val = value_type(sval) 298 return conv_val 299 except: 300 msg = ("Could not convert %s value %r in section %s to type %s" % 301 (key, sval, section, value_type)) 302 raise ConfigValueError(msg) 303 304 305 def get_sections(self): 306 """Return a list of sections available.""" 307 self._ensure_config_parsed() 308 return self.config.sections() 309 310 311# insure the class is a singleton. Now the symbol global_config 312# will point to the one and only one instace of the class 313global_config = global_config_class() 314