1ed4d6ddc79993492b22aeeb36574b3cb7c8bad44mbligh"""A singleton class for accessing global config values
2ed4d6ddc79993492b22aeeb36574b3cb7c8bad44mbligh
3ed4d6ddc79993492b22aeeb36574b3cb7c8bad44mblighprovides access to global configuration file
4ed4d6ddc79993492b22aeeb36574b3cb7c8bad44mbligh"""
5ed4d6ddc79993492b22aeeb36574b3cb7c8bad44mbligh
660cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi# The config values can be stored in 3 config files:
760cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi#     global_config.ini
860cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi#     moblab_config.ini
960cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi#     shadow_config.ini
1060cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi# When the code is running in Moblab, config values in moblab config override
1160cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi# values in global config, and config values in shadow config override values
1260cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi# in both moblab and global config.
1360cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi# When the code is running in a non-Moblab host, moblab_config.ini is ignored.
1460cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi# Config values in shadow config will override values in global config.
1560cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi
16ed4d6ddc79993492b22aeeb36574b3cb7c8bad44mbligh__author__ = 'raphtee@google.com (Travis Miller)'
17ed4d6ddc79993492b22aeeb36574b3cb7c8bad44mbligh
18a5cfb97ee793d849acf0efa4369b43d6f91a9c4dAllen Liimport collections
1942ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shiimport ConfigParser
2042ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shiimport os
2142ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shiimport re
2242ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shiimport sys
2342ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi
2411788296e58691a3149355a4fc4a3fa1084c689dmblighfrom autotest_lib.client.common_lib import error
2560cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shifrom autotest_lib.client.common_lib import lsbrelease_utils
26104e9ce7a74041fd673ceac5c8b0bc67c74edcd5mbligh
27ed4d6ddc79993492b22aeeb36574b3cb7c8bad44mblighclass ConfigError(error.AutotestError):
2860cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi    """Configuration error."""
290afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski    pass
30ed4d6ddc79993492b22aeeb36574b3cb7c8bad44mbligh
31ed4d6ddc79993492b22aeeb36574b3cb7c8bad44mbligh
32104e9ce7a74041fd673ceac5c8b0bc67c74edcd5mblighclass ConfigValueError(ConfigError):
3360cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi    """Configuration value error, raised when value failed to be converted to
3460cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi    expected type."""
350afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski    pass
36104e9ce7a74041fd673ceac5c8b0bc67c74edcd5mbligh
37104e9ce7a74041fd673ceac5c8b0bc67c74edcd5mbligh
386d08b3c3fc2858ccd60a323008703179f93ae38blmr
396d08b3c3fc2858ccd60a323008703179f93ae38blmrcommon_lib_dir = os.path.dirname(sys.modules[__name__].__file__)
406d08b3c3fc2858ccd60a323008703179f93ae38blmrclient_dir = os.path.dirname(common_lib_dir)
416d08b3c3fc2858ccd60a323008703179f93ae38blmrroot_dir = os.path.dirname(client_dir)
426d08b3c3fc2858ccd60a323008703179f93ae38blmr
436d08b3c3fc2858ccd60a323008703179f93ae38blmr# Check if the config files are at autotest's root dir
446d08b3c3fc2858ccd60a323008703179f93ae38blmr# This will happen if client is executing inside a full autotest tree, or if
456d08b3c3fc2858ccd60a323008703179f93ae38blmr# other entry points are being executed
466d08b3c3fc2858ccd60a323008703179f93ae38blmrglobal_config_path_root = os.path.join(root_dir, 'global_config.ini')
4760cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shimoblab_config_path_root = os.path.join(root_dir, 'moblab_config.ini')
486d08b3c3fc2858ccd60a323008703179f93ae38blmrshadow_config_path_root = os.path.join(root_dir, 'shadow_config.ini')
4988434b12bf2c38f1099c0307b0313fe3f379364bChris Masoneconfig_in_root = os.path.exists(global_config_path_root)
506d08b3c3fc2858ccd60a323008703179f93ae38blmr
516d08b3c3fc2858ccd60a323008703179f93ae38blmr# Check if the config files are at autotest's client dir
526d08b3c3fc2858ccd60a323008703179f93ae38blmr# This will happen if a client stand alone execution is happening
536d08b3c3fc2858ccd60a323008703179f93ae38blmrglobal_config_path_client = os.path.join(client_dir, 'global_config.ini')
546d08b3c3fc2858ccd60a323008703179f93ae38blmrconfig_in_client = os.path.exists(global_config_path_client)
556d08b3c3fc2858ccd60a323008703179f93ae38blmr
566d08b3c3fc2858ccd60a323008703179f93ae38blmrif config_in_root:
576d08b3c3fc2858ccd60a323008703179f93ae38blmr    DEFAULT_CONFIG_FILE = global_config_path_root
5860cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi    if os.path.exists(moblab_config_path_root):
5960cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        DEFAULT_MOBLAB_FILE = moblab_config_path_root
6060cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi    else:
6160cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        DEFAULT_MOBLAB_FILE = None
6288434b12bf2c38f1099c0307b0313fe3f379364bChris Masone    if os.path.exists(shadow_config_path_root):
6388434b12bf2c38f1099c0307b0313fe3f379364bChris Masone        DEFAULT_SHADOW_FILE = shadow_config_path_root
6488434b12bf2c38f1099c0307b0313fe3f379364bChris Masone    else:
6588434b12bf2c38f1099c0307b0313fe3f379364bChris Masone        DEFAULT_SHADOW_FILE = None
666d08b3c3fc2858ccd60a323008703179f93ae38blmr    RUNNING_STAND_ALONE_CLIENT = False
676d08b3c3fc2858ccd60a323008703179f93ae38blmrelif config_in_client:
686d08b3c3fc2858ccd60a323008703179f93ae38blmr    DEFAULT_CONFIG_FILE = global_config_path_client
6960cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi    DEFAULT_MOBLAB_FILE = None
706d08b3c3fc2858ccd60a323008703179f93ae38blmr    DEFAULT_SHADOW_FILE = None
716d08b3c3fc2858ccd60a323008703179f93ae38blmr    RUNNING_STAND_ALONE_CLIENT = True
726d08b3c3fc2858ccd60a323008703179f93ae38blmrelse:
73da8fb97be0353f54eb7e62e132482d728684f200lmr    DEFAULT_CONFIG_FILE = None
7460cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi    DEFAULT_MOBLAB_FILE = None
75da8fb97be0353f54eb7e62e132482d728684f200lmr    DEFAULT_SHADOW_FILE = None
76da8fb97be0353f54eb7e62e132482d728684f200lmr    RUNNING_STAND_ALONE_CLIENT = True
776d08b3c3fc2858ccd60a323008703179f93ae38blmr
784ca1b04d9ad78f3095ff872383ae486b989d2586Allen Li
79773a86ebaa2e4e4cce213b8553aeb4b30c1f3a9dSimran Basiclass global_config_class(object):
8060cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi    """Object to access config values."""
81893db3d528e4c7d10ef5b7073dbf63670dffbccashoward    _NO_DEFAULT_SPECIFIED = object()
82893db3d528e4c7d10ef5b7073dbf63670dffbccashoward
834ca1b04d9ad78f3095ff872383ae486b989d2586Allen Li    _config = None
840afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski    config_file = DEFAULT_CONFIG_FILE
8560cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi    moblab_file=DEFAULT_MOBLAB_FILE
860afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski    shadow_file = DEFAULT_SHADOW_FILE
876d08b3c3fc2858ccd60a323008703179f93ae38blmr    running_stand_alone_client = RUNNING_STAND_ALONE_CLIENT
886d08b3c3fc2858ccd60a323008703179f93ae38blmr
896d08b3c3fc2858ccd60a323008703179f93ae38blmr
904ca1b04d9ad78f3095ff872383ae486b989d2586Allen Li    @property
914ca1b04d9ad78f3095ff872383ae486b989d2586Allen Li    def config(self):
924ca1b04d9ad78f3095ff872383ae486b989d2586Allen Li        """ConfigParser instance.
934ca1b04d9ad78f3095ff872383ae486b989d2586Allen Li
944ca1b04d9ad78f3095ff872383ae486b989d2586Allen Li        If the instance dict doesn't have a config key, this descriptor
954ca1b04d9ad78f3095ff872383ae486b989d2586Allen Li        will be called to ensure the config file is parsed (setting the
964ca1b04d9ad78f3095ff872383ae486b989d2586Allen Li        config key in the instance dict as a side effect).  Once the
974ca1b04d9ad78f3095ff872383ae486b989d2586Allen Li        instance dict has a config key, that value will be used in
984ca1b04d9ad78f3095ff872383ae486b989d2586Allen Li        preference.
994ca1b04d9ad78f3095ff872383ae486b989d2586Allen Li        """
1004ca1b04d9ad78f3095ff872383ae486b989d2586Allen Li        if self._config is None:
1014ca1b04d9ad78f3095ff872383ae486b989d2586Allen Li            self.parse_config_file()
1024ca1b04d9ad78f3095ff872383ae486b989d2586Allen Li        return self._config
1034ca1b04d9ad78f3095ff872383ae486b989d2586Allen Li
1044ca1b04d9ad78f3095ff872383ae486b989d2586Allen Li
1054ca1b04d9ad78f3095ff872383ae486b989d2586Allen Li    @config.setter
1064ca1b04d9ad78f3095ff872383ae486b989d2586Allen Li    def config(self, value):
1074ca1b04d9ad78f3095ff872383ae486b989d2586Allen Li        """Set config attribute.
1084ca1b04d9ad78f3095ff872383ae486b989d2586Allen Li
1094ca1b04d9ad78f3095ff872383ae486b989d2586Allen Li        @param value: value to set
1104ca1b04d9ad78f3095ff872383ae486b989d2586Allen Li        """
1114ca1b04d9ad78f3095ff872383ae486b989d2586Allen Li        self._config = value
1124ca1b04d9ad78f3095ff872383ae486b989d2586Allen Li
1134ca1b04d9ad78f3095ff872383ae486b989d2586Allen Li
1146d08b3c3fc2858ccd60a323008703179f93ae38blmr    def check_stand_alone_client_run(self):
11560cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        """Check if this is a stand alone client that does not need config."""
1166d08b3c3fc2858ccd60a323008703179f93ae38blmr        return self.running_stand_alone_client
1170afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
1180afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
1190afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski    def set_config_files(self, config_file=DEFAULT_CONFIG_FILE,
12060cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi                         shadow_file=DEFAULT_SHADOW_FILE,
12160cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi                         moblab_file=DEFAULT_MOBLAB_FILE):
1220afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        self.config_file = config_file
12360cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        self.moblab_file = moblab_file
1240afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        self.shadow_file = shadow_file
1254ca1b04d9ad78f3095ff872383ae486b989d2586Allen Li        self._config = None
1260afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
1270afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
128d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward    def _handle_no_value(self, section, key, default):
129893db3d528e4c7d10ef5b7073dbf63670dffbccashoward        if default is self._NO_DEFAULT_SPECIFIED:
130d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward            msg = ("Value '%s' not found in section '%s'" %
131d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward                   (key, section))
132d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward            raise ConfigError(msg)
133d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward        else:
134d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward            return default
135d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward
136d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward
137a5cfb97ee793d849acf0efa4369b43d6f91a9c4dAllen Li    def get_section_as_dict(self, section):
138a5cfb97ee793d849acf0efa4369b43d6f91a9c4dAllen Li        """Return a dict mapping section options to values.
139a5cfb97ee793d849acf0efa4369b43d6f91a9c4dAllen Li
140a5cfb97ee793d849acf0efa4369b43d6f91a9c4dAllen Li        This is useful if a config section is being used like a
141a5cfb97ee793d849acf0efa4369b43d6f91a9c4dAllen Li        dictionary.  If the section is missing, return an empty dict.
142a5cfb97ee793d849acf0efa4369b43d6f91a9c4dAllen Li
143a5cfb97ee793d849acf0efa4369b43d6f91a9c4dAllen Li        This returns an OrderedDict, preserving the order of the options
144a5cfb97ee793d849acf0efa4369b43d6f91a9c4dAllen Li        in the section.
145a5cfb97ee793d849acf0efa4369b43d6f91a9c4dAllen Li
146a5cfb97ee793d849acf0efa4369b43d6f91a9c4dAllen Li        @param section: Section to get.
147a5cfb97ee793d849acf0efa4369b43d6f91a9c4dAllen Li        @return: OrderedDict
148a5cfb97ee793d849acf0efa4369b43d6f91a9c4dAllen Li        """
149a5cfb97ee793d849acf0efa4369b43d6f91a9c4dAllen Li        if self.config.has_section(section):
150a5cfb97ee793d849acf0efa4369b43d6f91a9c4dAllen Li            return collections.OrderedDict(self.config.items(section))
151a5cfb97ee793d849acf0efa4369b43d6f91a9c4dAllen Li        else:
152a5cfb97ee793d849acf0efa4369b43d6f91a9c4dAllen Li            return collections.OrderedDict()
153a5cfb97ee793d849acf0efa4369b43d6f91a9c4dAllen Li
154a5cfb97ee793d849acf0efa4369b43d6f91a9c4dAllen Li
1556d08b3c3fc2858ccd60a323008703179f93ae38blmr    def get_section_values(self, section):
1566d08b3c3fc2858ccd60a323008703179f93ae38blmr        """
1576d08b3c3fc2858ccd60a323008703179f93ae38blmr        Return a config parser object containing a single section of the
1586d08b3c3fc2858ccd60a323008703179f93ae38blmr        global configuration, that can be later written to a file object.
1596d08b3c3fc2858ccd60a323008703179f93ae38blmr
1606d08b3c3fc2858ccd60a323008703179f93ae38blmr        @param section: Section we want to turn into a config parser object.
1616d08b3c3fc2858ccd60a323008703179f93ae38blmr        @return: ConfigParser() object containing all the contents of section.
1626d08b3c3fc2858ccd60a323008703179f93ae38blmr        """
1636d08b3c3fc2858ccd60a323008703179f93ae38blmr        cfgparser = ConfigParser.ConfigParser()
1646d08b3c3fc2858ccd60a323008703179f93ae38blmr        cfgparser.add_section(section)
1656d08b3c3fc2858ccd60a323008703179f93ae38blmr        for option, value in self.config.items(section):
1666d08b3c3fc2858ccd60a323008703179f93ae38blmr            cfgparser.set(section, option, value)
1676d08b3c3fc2858ccd60a323008703179f93ae38blmr        return cfgparser
1686d08b3c3fc2858ccd60a323008703179f93ae38blmr
1696d08b3c3fc2858ccd60a323008703179f93ae38blmr
170893db3d528e4c7d10ef5b7073dbf63670dffbccashoward    def get_config_value(self, section, key, type=str,
171893db3d528e4c7d10ef5b7073dbf63670dffbccashoward                         default=_NO_DEFAULT_SPECIFIED, allow_blank=False):
172f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        """Get a configuration value
173f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich
174f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        @param section: Section the key is in.
175f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        @param key: The key to look up.
176f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        @param type: The expected type of the returned value.
177f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        @param default: A value to return in case the key couldn't be found.
178f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        @param allow_blank: If False, an empty string as a value is treated like
179f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich                            there was no value at all. If True, empty strings
180f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich                            will be returned like they were normal values.
181f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich
182f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        @raises ConfigError: If the key could not be found and no default was
183f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich                             specified.
184f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich
185f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        @return: The obtained value or default.
186f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        """
1870afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        try:
1880afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            val = self.config.get(section, key)
189d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward        except ConfigParser.Error:
190d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward            return self._handle_no_value(section, key, default)
191d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward
192d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward        if not val.strip() and not allow_blank:
193d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward            return self._handle_no_value(section, key, default)
1940afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
195893db3d528e4c7d10ef5b7073dbf63670dffbccashoward        return self._convert_value(key, section, val, type)
1960afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
1970afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
19842ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi    def get_config_value_regex(self, section, key_regex, type=str):
19942ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi        """Get a dict of configs in given section with key matched to key-regex.
20042ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi
20142ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi        @param section: Section the key is in.
20242ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi        @param key_regex: The regex that key should match.
20342ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi        @param type: data type the value should have.
20442ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi
20542ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi        @return: A dictionary of key:value with key matching `key_regex`. Return
20642ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi                 an empty dictionary if no matching key is found.
20742ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi        """
20842ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi        configs = {}
20942ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi        for option, value in self.config.items(section):
21042ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi            if re.match(key_regex, option):
21142ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi                configs[option] = self._convert_value(option, section, value,
21242ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi                                                      type)
21342ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi        return configs
21442ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi
21542ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi
216f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich    # This order of parameters ensures this can be called similar to the normal
217f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich    # get_config_value which is mostly called with (section, key, type).
218f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich    def get_config_value_with_fallback(self, section, key, fallback_key,
219f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich                                       type=str, fallback_section=None,
220f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich                                       default=_NO_DEFAULT_SPECIFIED, **kwargs):
221f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        """Get a configuration value if it exists, otherwise use fallback.
222f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich
223f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        Tries to obtain a configuration value for a given key. If this value
224f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        does not exist, the value looked up under a different key will be
225f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        returned.
226f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich
227f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        @param section: Section the key is in.
228f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        @param key: The key to look up.
229f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        @param fallback_key: The key to use in case the original key wasn't
230f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich                             found.
231f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        @param type: data type the value should have.
232f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        @param fallback_section: The section the fallback key resides in. In
233f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich                                 case none is specified, the the same section as
234f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich                                 for the primary key is used.
235f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        @param default: Value to return if values could neither be obtained for
236f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich                        the key nor the fallback key.
237f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        @param **kwargs: Additional arguments that should be passed to
238f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich                         get_config_value.
239f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich
240f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        @raises ConfigError: If the fallback key doesn't exist and no default
241f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich                             was provided.
242f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich
243f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        @return: The value that was looked up for the key. If that didn't
244f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich                 exist, the value looked up for the fallback key will be
245f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich                 returned. If that also didn't exist, default will be returned.
246f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        """
247f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        if fallback_section is None:
248f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich            fallback_section = section
249f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich
250f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        try:
251f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich            return self.get_config_value(section, key, type, **kwargs)
252f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        except ConfigError:
253f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich            return self.get_config_value(fallback_section, fallback_key,
254f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich                                         type, default=default, **kwargs)
255f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich
256f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich
25750c0e71efd37c04c5f441c7e4dd095632b54c35fshoward    def override_config_value(self, section, key, new_value):
25860cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        """Override a value from the config file with a new value.
25960cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi
26060cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        @param section: Name of the section.
26160cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        @param key: Name of the key.
26260cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        @param new_value: new value.
26350c0e71efd37c04c5f441c7e4dd095632b54c35fshoward        """
26450c0e71efd37c04c5f441c7e4dd095632b54c35fshoward        self.config.set(section, key, new_value)
26550c0e71efd37c04c5f441c7e4dd095632b54c35fshoward
26650c0e71efd37c04c5f441c7e4dd095632b54c35fshoward
26750c0e71efd37c04c5f441c7e4dd095632b54c35fshoward    def reset_config_values(self):
26850c0e71efd37c04c5f441c7e4dd095632b54c35fshoward        """
26950c0e71efd37c04c5f441c7e4dd095632b54c35fshoward        Reset all values to those found in the config files (undoes all
27050c0e71efd37c04c5f441c7e4dd095632b54c35fshoward        overrides).
27150c0e71efd37c04c5f441c7e4dd095632b54c35fshoward        """
27250c0e71efd37c04c5f441c7e4dd095632b54c35fshoward        self.parse_config_file()
27350c0e71efd37c04c5f441c7e4dd095632b54c35fshoward
27450c0e71efd37c04c5f441c7e4dd095632b54c35fshoward
27560cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi    def merge_configs(self, override_config):
27660cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        """Merge existing config values with the ones in given override_config.
27760cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi
27860cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        @param override_config: Configs to override existing config values.
27960cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        """
28060cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        # overwrite whats in config with whats in override_config
28160cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        sections = override_config.sections()
2820afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        for section in sections:
2830afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            # add the section if need be
2840afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            if not self.config.has_section(section):
2850afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski                self.config.add_section(section)
2860afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            # now run through all options and set them
28760cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi            options = override_config.options(section)
2880afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            for option in options:
28960cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi                val = override_config.get(section, option)
2900afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski                self.config.set(section, option, val)
2910afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
2920afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
2930afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski    def parse_config_file(self):
29460cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        """Parse config files."""
2950afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        self.config = ConfigParser.ConfigParser()
296da8fb97be0353f54eb7e62e132482d728684f200lmr        if self.config_file and os.path.exists(self.config_file):
297da8fb97be0353f54eb7e62e132482d728684f200lmr            self.config.read(self.config_file)
298da8fb97be0353f54eb7e62e132482d728684f200lmr        else:
299da8fb97be0353f54eb7e62e132482d728684f200lmr            raise ConfigError('%s not found' % (self.config_file))
3000afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
30160cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        # If it's running in Moblab, read moblab config file if exists,
30260cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        # overwrite the value in global config.
30360cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        if (lsbrelease_utils.is_moblab() and self.moblab_file and
30460cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi            os.path.exists(self.moblab_file)):
30560cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi            moblab_config = ConfigParser.ConfigParser()
30660cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi            moblab_config.read(self.moblab_file)
30760cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi            # now we merge moblab into global
30860cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi            self.merge_configs(moblab_config)
30960cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi
3100afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        # now also read the shadow file if there is one
3110afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        # this will overwrite anything that is found in the
3120afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        # other config
3136d08b3c3fc2858ccd60a323008703179f93ae38blmr        if self.shadow_file and os.path.exists(self.shadow_file):
3140afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            shadow_config = ConfigParser.ConfigParser()
3150afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            shadow_config.read(self.shadow_file)
3160afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            # now we merge shadow into global
3170afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            self.merge_configs(shadow_config)
3180afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
3190afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
3200afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski    # the values that are pulled from ini
3210afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski    # are strings.  But we should attempt to
3220afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski    # convert them to other types if needed.
323893db3d528e4c7d10ef5b7073dbf63670dffbccashoward    def _convert_value(self, key, section, value, value_type):
3240afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        # strip off leading and trailing white space
3250afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        sval = value.strip()
3260afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
3270afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        # if length of string is zero then return None
3280afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        if len(sval) == 0:
329a5f30c207b211e2bf57c6d4297734da54055af90showard            if value_type == str:
3300afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski                return ""
331a5f30c207b211e2bf57c6d4297734da54055af90showard            elif value_type == bool:
3320afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski                return False
333a5f30c207b211e2bf57c6d4297734da54055af90showard            elif value_type == int:
3340afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski                return 0
335a5f30c207b211e2bf57c6d4297734da54055af90showard            elif value_type == float:
3360afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski                return 0.0
337a5f30c207b211e2bf57c6d4297734da54055af90showard            elif value_type == list:
338c5ddfd1f71caef9ec0c84c53ef7db42fcdc33e1cmbligh                return []
3390afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            else:
3400afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski                return None
3410afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
342a5f30c207b211e2bf57c6d4297734da54055af90showard        if value_type == bool:
3430afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            if sval.lower() == "false":
3440afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski                return False
3450afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            else:
3460afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski                return True
3470afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
348a5f30c207b211e2bf57c6d4297734da54055af90showard        if value_type == list:
349c5ddfd1f71caef9ec0c84c53ef7db42fcdc33e1cmbligh            # Split the string using ',' and return a list
350c5ddfd1f71caef9ec0c84c53ef7db42fcdc33e1cmbligh            return [val.strip() for val in sval.split(',')]
351c5ddfd1f71caef9ec0c84c53ef7db42fcdc33e1cmbligh
3520afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        try:
353a5f30c207b211e2bf57c6d4297734da54055af90showard            conv_val = value_type(sval)
3540afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            return conv_val
3550afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        except:
356a5f30c207b211e2bf57c6d4297734da54055af90showard            msg = ("Could not convert %s value %r in section %s to type %s" %
357a5f30c207b211e2bf57c6d4297734da54055af90showard                    (key, sval, section, value_type))
3580afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            raise ConfigValueError(msg)
3590afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
3600afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
36171206ef92012e5a5c36bbd1ae47e9176be04036dSimran Basi    def get_sections(self):
36271206ef92012e5a5c36bbd1ae47e9176be04036dSimran Basi        """Return a list of sections available."""
36371206ef92012e5a5c36bbd1ae47e9176be04036dSimran Basi        return self.config.sections()
36471206ef92012e5a5c36bbd1ae47e9176be04036dSimran Basi
36571206ef92012e5a5c36bbd1ae47e9176be04036dSimran Basi
3660afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski# insure the class is a singleton.  Now the symbol global_config
367ed4d6ddc79993492b22aeeb36574b3cb7c8bad44mbligh# will point to the one and only one instace of the class
368773a86ebaa2e4e4cce213b8553aeb4b30c1f3a9dSimran Basiglobal_config = global_config_class()
369c1f41e806039e541b152516058eea535687e07a4Prathmesh Prabhu
370c1f41e806039e541b152516058eea535687e07a4Prathmesh Prabhu
371c1f41e806039e541b152516058eea535687e07a4Prathmesh Prabhuclass FakeGlobalConfig(object):
372c1f41e806039e541b152516058eea535687e07a4Prathmesh Prabhu    """Fake replacement for global_config singleton object.
373c1f41e806039e541b152516058eea535687e07a4Prathmesh Prabhu
374c1f41e806039e541b152516058eea535687e07a4Prathmesh Prabhu    Unittest will want to fake the global_config so that developers'
375c1f41e806039e541b152516058eea535687e07a4Prathmesh Prabhu    shadow_config doesn't leak into unittests. Provide a fake object for that
376c1f41e806039e541b152516058eea535687e07a4Prathmesh Prabhu    purpose.
377c1f41e806039e541b152516058eea535687e07a4Prathmesh Prabhu
378c1f41e806039e541b152516058eea535687e07a4Prathmesh Prabhu    """
379c1f41e806039e541b152516058eea535687e07a4Prathmesh Prabhu    # pylint: disable=missing-docstring
380c1f41e806039e541b152516058eea535687e07a4Prathmesh Prabhu
381c1f41e806039e541b152516058eea535687e07a4Prathmesh Prabhu    def __init__(self):
382c1f41e806039e541b152516058eea535687e07a4Prathmesh Prabhu        self._config_info = {}
383c1f41e806039e541b152516058eea535687e07a4Prathmesh Prabhu
384c1f41e806039e541b152516058eea535687e07a4Prathmesh Prabhu
385c1f41e806039e541b152516058eea535687e07a4Prathmesh Prabhu    def set_config_value(self, section, key, value):
386c1f41e806039e541b152516058eea535687e07a4Prathmesh Prabhu        self._config_info[(section, key)] = value
387c1f41e806039e541b152516058eea535687e07a4Prathmesh Prabhu
388c1f41e806039e541b152516058eea535687e07a4Prathmesh Prabhu
389c1f41e806039e541b152516058eea535687e07a4Prathmesh Prabhu    def get_config_value(self, section, key, type=str,
390c1f41e806039e541b152516058eea535687e07a4Prathmesh Prabhu                         default=None, allow_blank=False):
391c1f41e806039e541b152516058eea535687e07a4Prathmesh Prabhu        identifier = (section, key)
392c1f41e806039e541b152516058eea535687e07a4Prathmesh Prabhu        if identifier not in self._config_info:
393c1f41e806039e541b152516058eea535687e07a4Prathmesh Prabhu            return default
394c1f41e806039e541b152516058eea535687e07a4Prathmesh Prabhu        return self._config_info[identifier]
395c1f41e806039e541b152516058eea535687e07a4Prathmesh Prabhu
396c1f41e806039e541b152516058eea535687e07a4Prathmesh Prabhu
397c1f41e806039e541b152516058eea535687e07a4Prathmesh Prabhu    def parse_config_file(self):
398c1f41e806039e541b152516058eea535687e07a4Prathmesh Prabhu        pass
399