global_config.py revision 6d08b3c3fc2858ccd60a323008703179f93ae38b
1"""A singleton class for accessing global config values
2
3provides access to global configuration file
4"""
5
6__author__ = 'raphtee@google.com (Travis Miller)'
7
8import os, sys, ConfigParser
9from autotest_lib.client.common_lib import error
10
11
12class ConfigError(error.AutotestError):
13    pass
14
15
16class ConfigValueError(ConfigError):
17    pass
18
19
20
21common_lib_dir = os.path.dirname(sys.modules[__name__].__file__)
22client_dir = os.path.dirname(common_lib_dir)
23root_dir = os.path.dirname(client_dir)
24
25# Check if the config files are at autotest's root dir
26# This will happen if client is executing inside a full autotest tree, or if
27# other entry points are being executed
28global_config_path_root = os.path.join(root_dir, 'global_config.ini')
29shadow_config_path_root = os.path.join(root_dir, 'shadow_config.ini')
30config_in_root = (os.path.exists(global_config_path_root) and
31                  os.path.exists(shadow_config_path_root))
32
33# Check if the config files are at autotest's client dir
34# This will happen if a client stand alone execution is happening
35global_config_path_client = os.path.join(client_dir, 'global_config.ini')
36config_in_client = os.path.exists(global_config_path_client)
37
38if config_in_root:
39    DEFAULT_CONFIG_FILE = global_config_path_root
40    DEFAULT_SHADOW_FILE = shadow_config_path_root
41    RUNNING_STAND_ALONE_CLIENT = False
42elif config_in_client:
43    DEFAULT_CONFIG_FILE = global_config_path_client
44    DEFAULT_SHADOW_FILE = None
45    RUNNING_STAND_ALONE_CLIENT = True
46else:
47    raise ConfigError("Could not find configuration files "
48                      "needed for this program to function. Please refer to "
49                      "http://autotest.kernel.org/wiki/GlobalConfig "
50                      "for more info.")
51
52
53class global_config(object):
54    _NO_DEFAULT_SPECIFIED = object()
55
56    config = None
57    config_file = DEFAULT_CONFIG_FILE
58    shadow_file = DEFAULT_SHADOW_FILE
59    running_stand_alone_client = RUNNING_STAND_ALONE_CLIENT
60
61
62    def check_stand_alone_client_run(self):
63        return self.running_stand_alone_client
64
65
66    def set_config_files(self, config_file=DEFAULT_CONFIG_FILE,
67                            shadow_file=DEFAULT_SHADOW_FILE):
68        self.config_file = config_file
69        self.shadow_file = shadow_file
70        self.config = None
71
72
73    def _handle_no_value(self, section, key, default):
74        if default is self._NO_DEFAULT_SPECIFIED:
75            msg = ("Value '%s' not found in section '%s'" %
76                   (key, section))
77            raise ConfigError(msg)
78        else:
79            return default
80
81
82    def get_section_values(self, section):
83        """
84        Return a config parser object containing a single section of the
85        global configuration, that can be later written to a file object.
86
87        @param section: Section we want to turn into a config parser object.
88        @return: ConfigParser() object containing all the contents of section.
89        """
90        cfgparser = ConfigParser.ConfigParser()
91        cfgparser.add_section(section)
92        for option, value in self.config.items(section):
93            cfgparser.set(section, option, value)
94        return cfgparser
95
96
97    def get_config_value(self, section, key, type=str,
98                         default=_NO_DEFAULT_SPECIFIED, allow_blank=False):
99        self._ensure_config_parsed()
100
101        try:
102            val = self.config.get(section, key)
103        except ConfigParser.Error:
104            return self._handle_no_value(section, key, default)
105
106        if not val.strip() and not allow_blank:
107            return self._handle_no_value(section, key, default)
108
109        return self._convert_value(key, section, val, type)
110
111
112    def override_config_value(self, section, key, new_value):
113        """
114        Override a value from the config file with a new value.
115        """
116        self._ensure_config_parsed()
117        self.config.set(section, key, new_value)
118
119
120    def reset_config_values(self):
121        """
122        Reset all values to those found in the config files (undoes all
123        overrides).
124        """
125        self.parse_config_file()
126
127
128    def _ensure_config_parsed(self):
129        if self.config is None:
130            self.parse_config_file()
131
132
133    def merge_configs(self, shadow_config):
134        # overwrite whats in config with whats in shadow_config
135        sections = shadow_config.sections()
136        for section in sections:
137            # add the section if need be
138            if not self.config.has_section(section):
139                self.config.add_section(section)
140            # now run through all options and set them
141            options = shadow_config.options(section)
142            for option in options:
143                val = shadow_config.get(section, option)
144                self.config.set(section, option, val)
145
146
147    def parse_config_file(self):
148        if not os.path.exists(self.config_file):
149            raise ConfigError('%s not found' % (self.config_file))
150        self.config = ConfigParser.ConfigParser()
151        self.config.read(self.config_file)
152
153        # now also read the shadow file if there is one
154        # this will overwrite anything that is found in the
155        # other config
156        if self.shadow_file and os.path.exists(self.shadow_file):
157            shadow_config = ConfigParser.ConfigParser()
158            shadow_config.read(self.shadow_file)
159            # now we merge shadow into global
160            self.merge_configs(shadow_config)
161
162
163    # the values that are pulled from ini
164    # are strings.  But we should attempt to
165    # convert them to other types if needed.
166    def _convert_value(self, key, section, value, value_type):
167        # strip off leading and trailing white space
168        sval = value.strip()
169
170        # if length of string is zero then return None
171        if len(sval) == 0:
172            if value_type == str:
173                return ""
174            elif value_type == bool:
175                return False
176            elif value_type == int:
177                return 0
178            elif value_type == float:
179                return 0.0
180            elif value_type == list:
181                return []
182            else:
183                return None
184
185        if value_type == bool:
186            if sval.lower() == "false":
187                return False
188            else:
189                return True
190
191        if value_type == list:
192            # Split the string using ',' and return a list
193            return [val.strip() for val in sval.split(',')]
194
195        try:
196            conv_val = value_type(sval)
197            return conv_val
198        except:
199            msg = ("Could not convert %s value %r in section %s to type %s" %
200                    (key, sval, section, value_type))
201            raise ConfigValueError(msg)
202
203
204# insure the class is a singleton.  Now the symbol global_config
205# will point to the one and only one instace of the class
206global_config = global_config()
207