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
1842ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shiimport ConfigParser
1942ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shiimport os
2042ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shiimport re
2142ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shiimport sys
2242ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi
2311788296e58691a3149355a4fc4a3fa1084c689dmblighfrom autotest_lib.client.common_lib import error
2460cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shifrom autotest_lib.client.common_lib import lsbrelease_utils
25104e9ce7a74041fd673ceac5c8b0bc67c74edcd5mbligh
26ed4d6ddc79993492b22aeeb36574b3cb7c8bad44mblighclass ConfigError(error.AutotestError):
2760cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi    """Configuration error."""
280afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski    pass
29ed4d6ddc79993492b22aeeb36574b3cb7c8bad44mbligh
30ed4d6ddc79993492b22aeeb36574b3cb7c8bad44mbligh
31104e9ce7a74041fd673ceac5c8b0bc67c74edcd5mblighclass ConfigValueError(ConfigError):
3260cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi    """Configuration value error, raised when value failed to be converted to
3360cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi    expected type."""
340afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski    pass
35104e9ce7a74041fd673ceac5c8b0bc67c74edcd5mbligh
36104e9ce7a74041fd673ceac5c8b0bc67c74edcd5mbligh
376d08b3c3fc2858ccd60a323008703179f93ae38blmr
386d08b3c3fc2858ccd60a323008703179f93ae38blmrcommon_lib_dir = os.path.dirname(sys.modules[__name__].__file__)
396d08b3c3fc2858ccd60a323008703179f93ae38blmrclient_dir = os.path.dirname(common_lib_dir)
406d08b3c3fc2858ccd60a323008703179f93ae38blmrroot_dir = os.path.dirname(client_dir)
416d08b3c3fc2858ccd60a323008703179f93ae38blmr
426d08b3c3fc2858ccd60a323008703179f93ae38blmr# Check if the config files are at autotest's root dir
436d08b3c3fc2858ccd60a323008703179f93ae38blmr# This will happen if client is executing inside a full autotest tree, or if
446d08b3c3fc2858ccd60a323008703179f93ae38blmr# other entry points are being executed
456d08b3c3fc2858ccd60a323008703179f93ae38blmrglobal_config_path_root = os.path.join(root_dir, 'global_config.ini')
4660cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shimoblab_config_path_root = os.path.join(root_dir, 'moblab_config.ini')
476d08b3c3fc2858ccd60a323008703179f93ae38blmrshadow_config_path_root = os.path.join(root_dir, 'shadow_config.ini')
4888434b12bf2c38f1099c0307b0313fe3f379364bChris Masoneconfig_in_root = os.path.exists(global_config_path_root)
496d08b3c3fc2858ccd60a323008703179f93ae38blmr
506d08b3c3fc2858ccd60a323008703179f93ae38blmr# Check if the config files are at autotest's client dir
516d08b3c3fc2858ccd60a323008703179f93ae38blmr# This will happen if a client stand alone execution is happening
526d08b3c3fc2858ccd60a323008703179f93ae38blmrglobal_config_path_client = os.path.join(client_dir, 'global_config.ini')
536d08b3c3fc2858ccd60a323008703179f93ae38blmrconfig_in_client = os.path.exists(global_config_path_client)
546d08b3c3fc2858ccd60a323008703179f93ae38blmr
556d08b3c3fc2858ccd60a323008703179f93ae38blmrif config_in_root:
566d08b3c3fc2858ccd60a323008703179f93ae38blmr    DEFAULT_CONFIG_FILE = global_config_path_root
5760cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi    if os.path.exists(moblab_config_path_root):
5860cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        DEFAULT_MOBLAB_FILE = moblab_config_path_root
5960cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi    else:
6060cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        DEFAULT_MOBLAB_FILE = None
6188434b12bf2c38f1099c0307b0313fe3f379364bChris Masone    if os.path.exists(shadow_config_path_root):
6288434b12bf2c38f1099c0307b0313fe3f379364bChris Masone        DEFAULT_SHADOW_FILE = shadow_config_path_root
6388434b12bf2c38f1099c0307b0313fe3f379364bChris Masone    else:
6488434b12bf2c38f1099c0307b0313fe3f379364bChris Masone        DEFAULT_SHADOW_FILE = None
656d08b3c3fc2858ccd60a323008703179f93ae38blmr    RUNNING_STAND_ALONE_CLIENT = False
666d08b3c3fc2858ccd60a323008703179f93ae38blmrelif config_in_client:
676d08b3c3fc2858ccd60a323008703179f93ae38blmr    DEFAULT_CONFIG_FILE = global_config_path_client
6860cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi    DEFAULT_MOBLAB_FILE = None
696d08b3c3fc2858ccd60a323008703179f93ae38blmr    DEFAULT_SHADOW_FILE = None
706d08b3c3fc2858ccd60a323008703179f93ae38blmr    RUNNING_STAND_ALONE_CLIENT = True
716d08b3c3fc2858ccd60a323008703179f93ae38blmrelse:
72da8fb97be0353f54eb7e62e132482d728684f200lmr    DEFAULT_CONFIG_FILE = None
7360cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi    DEFAULT_MOBLAB_FILE = None
74da8fb97be0353f54eb7e62e132482d728684f200lmr    DEFAULT_SHADOW_FILE = None
75da8fb97be0353f54eb7e62e132482d728684f200lmr    RUNNING_STAND_ALONE_CLIENT = True
766d08b3c3fc2858ccd60a323008703179f93ae38blmr
77773a86ebaa2e4e4cce213b8553aeb4b30c1f3a9dSimran Basiclass global_config_class(object):
7860cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi    """Object to access config values."""
79893db3d528e4c7d10ef5b7073dbf63670dffbccashoward    _NO_DEFAULT_SPECIFIED = object()
80893db3d528e4c7d10ef5b7073dbf63670dffbccashoward
810afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski    config = None
820afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski    config_file = DEFAULT_CONFIG_FILE
8360cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi    moblab_file=DEFAULT_MOBLAB_FILE
840afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski    shadow_file = DEFAULT_SHADOW_FILE
856d08b3c3fc2858ccd60a323008703179f93ae38blmr    running_stand_alone_client = RUNNING_STAND_ALONE_CLIENT
866d08b3c3fc2858ccd60a323008703179f93ae38blmr
876d08b3c3fc2858ccd60a323008703179f93ae38blmr
886d08b3c3fc2858ccd60a323008703179f93ae38blmr    def check_stand_alone_client_run(self):
8960cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        """Check if this is a stand alone client that does not need config."""
906d08b3c3fc2858ccd60a323008703179f93ae38blmr        return self.running_stand_alone_client
910afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
920afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
930afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski    def set_config_files(self, config_file=DEFAULT_CONFIG_FILE,
9460cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi                         shadow_file=DEFAULT_SHADOW_FILE,
9560cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi                         moblab_file=DEFAULT_MOBLAB_FILE):
960afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        self.config_file = config_file
9760cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        self.moblab_file = moblab_file
980afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        self.shadow_file = shadow_file
990afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        self.config = None
1000afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
1010afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
102d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward    def _handle_no_value(self, section, key, default):
103893db3d528e4c7d10ef5b7073dbf63670dffbccashoward        if default is self._NO_DEFAULT_SPECIFIED:
104d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward            msg = ("Value '%s' not found in section '%s'" %
105d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward                   (key, section))
106d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward            raise ConfigError(msg)
107d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward        else:
108d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward            return default
109d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward
110d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward
1116d08b3c3fc2858ccd60a323008703179f93ae38blmr    def get_section_values(self, section):
1126d08b3c3fc2858ccd60a323008703179f93ae38blmr        """
1136d08b3c3fc2858ccd60a323008703179f93ae38blmr        Return a config parser object containing a single section of the
1146d08b3c3fc2858ccd60a323008703179f93ae38blmr        global configuration, that can be later written to a file object.
1156d08b3c3fc2858ccd60a323008703179f93ae38blmr
1166d08b3c3fc2858ccd60a323008703179f93ae38blmr        @param section: Section we want to turn into a config parser object.
1176d08b3c3fc2858ccd60a323008703179f93ae38blmr        @return: ConfigParser() object containing all the contents of section.
1186d08b3c3fc2858ccd60a323008703179f93ae38blmr        """
1196d08b3c3fc2858ccd60a323008703179f93ae38blmr        cfgparser = ConfigParser.ConfigParser()
1206d08b3c3fc2858ccd60a323008703179f93ae38blmr        cfgparser.add_section(section)
1216d08b3c3fc2858ccd60a323008703179f93ae38blmr        for option, value in self.config.items(section):
1226d08b3c3fc2858ccd60a323008703179f93ae38blmr            cfgparser.set(section, option, value)
1236d08b3c3fc2858ccd60a323008703179f93ae38blmr        return cfgparser
1246d08b3c3fc2858ccd60a323008703179f93ae38blmr
1256d08b3c3fc2858ccd60a323008703179f93ae38blmr
126893db3d528e4c7d10ef5b7073dbf63670dffbccashoward    def get_config_value(self, section, key, type=str,
127893db3d528e4c7d10ef5b7073dbf63670dffbccashoward                         default=_NO_DEFAULT_SPECIFIED, allow_blank=False):
128f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        """Get a configuration value
129f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich
130f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        @param section: Section the key is in.
131f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        @param key: The key to look up.
132f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        @param type: The expected type of the returned value.
133f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        @param default: A value to return in case the key couldn't be found.
134f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        @param allow_blank: If False, an empty string as a value is treated like
135f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich                            there was no value at all. If True, empty strings
136f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich                            will be returned like they were normal values.
137f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich
138f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        @raises ConfigError: If the key could not be found and no default was
139f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich                             specified.
140f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich
141f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        @return: The obtained value or default.
142f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        """
14350c0e71efd37c04c5f441c7e4dd095632b54c35fshoward        self._ensure_config_parsed()
1440afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
1450afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        try:
1460afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            val = self.config.get(section, key)
147d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward        except ConfigParser.Error:
148d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward            return self._handle_no_value(section, key, default)
149d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward
150d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward        if not val.strip() and not allow_blank:
151d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward            return self._handle_no_value(section, key, default)
1520afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
153893db3d528e4c7d10ef5b7073dbf63670dffbccashoward        return self._convert_value(key, section, val, type)
1540afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
1550afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
15642ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi    def get_config_value_regex(self, section, key_regex, type=str):
15742ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi        """Get a dict of configs in given section with key matched to key-regex.
15842ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi
15942ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi        @param section: Section the key is in.
16042ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi        @param key_regex: The regex that key should match.
16142ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi        @param type: data type the value should have.
16242ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi
16342ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi        @return: A dictionary of key:value with key matching `key_regex`. Return
16442ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi                 an empty dictionary if no matching key is found.
16542ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi        """
16642ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi        configs = {}
16742ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi        self._ensure_config_parsed()
16842ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi        for option, value in self.config.items(section):
16942ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi            if re.match(key_regex, option):
17042ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi                configs[option] = self._convert_value(option, section, value,
17142ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi                                                      type)
17242ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi        return configs
17342ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi
17442ffd3d0a235f8ab14af52142176c7bd7f299487Dan Shi
175f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich    # This order of parameters ensures this can be called similar to the normal
176f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich    # get_config_value which is mostly called with (section, key, type).
177f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich    def get_config_value_with_fallback(self, section, key, fallback_key,
178f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich                                       type=str, fallback_section=None,
179f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich                                       default=_NO_DEFAULT_SPECIFIED, **kwargs):
180f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        """Get a configuration value if it exists, otherwise use fallback.
181f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich
182f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        Tries to obtain a configuration value for a given key. If this value
183f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        does not exist, the value looked up under a different key will be
184f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        returned.
185f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich
186f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        @param section: Section the key is in.
187f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        @param key: The key to look up.
188f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        @param fallback_key: The key to use in case the original key wasn't
189f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich                             found.
190f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        @param type: data type the value should have.
191f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        @param fallback_section: The section the fallback key resides in. In
192f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich                                 case none is specified, the the same section as
193f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich                                 for the primary key is used.
194f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        @param default: Value to return if values could neither be obtained for
195f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich                        the key nor the fallback key.
196f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        @param **kwargs: Additional arguments that should be passed to
197f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich                         get_config_value.
198f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich
199f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        @raises ConfigError: If the fallback key doesn't exist and no default
200f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich                             was provided.
201f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich
202f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        @return: The value that was looked up for the key. If that didn't
203f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich                 exist, the value looked up for the fallback key will be
204f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich                 returned. If that also didn't exist, default will be returned.
205f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        """
206f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        if fallback_section is None:
207f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich            fallback_section = section
208f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich
209f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        try:
210f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich            return self.get_config_value(section, key, type, **kwargs)
211f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich        except ConfigError:
212f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich            return self.get_config_value(fallback_section, fallback_key,
213f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich                                         type, default=default, **kwargs)
214f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich
215f9c43d1e95e3c8375f62f1ae914d44d826e28a81Jakob Juelich
21650c0e71efd37c04c5f441c7e4dd095632b54c35fshoward    def override_config_value(self, section, key, new_value):
21760cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        """Override a value from the config file with a new value.
21860cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi
21960cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        @param section: Name of the section.
22060cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        @param key: Name of the key.
22160cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        @param new_value: new value.
22250c0e71efd37c04c5f441c7e4dd095632b54c35fshoward        """
22350c0e71efd37c04c5f441c7e4dd095632b54c35fshoward        self._ensure_config_parsed()
22450c0e71efd37c04c5f441c7e4dd095632b54c35fshoward        self.config.set(section, key, new_value)
22550c0e71efd37c04c5f441c7e4dd095632b54c35fshoward
22650c0e71efd37c04c5f441c7e4dd095632b54c35fshoward
22750c0e71efd37c04c5f441c7e4dd095632b54c35fshoward    def reset_config_values(self):
22850c0e71efd37c04c5f441c7e4dd095632b54c35fshoward        """
22950c0e71efd37c04c5f441c7e4dd095632b54c35fshoward        Reset all values to those found in the config files (undoes all
23050c0e71efd37c04c5f441c7e4dd095632b54c35fshoward        overrides).
23150c0e71efd37c04c5f441c7e4dd095632b54c35fshoward        """
23250c0e71efd37c04c5f441c7e4dd095632b54c35fshoward        self.parse_config_file()
23350c0e71efd37c04c5f441c7e4dd095632b54c35fshoward
23450c0e71efd37c04c5f441c7e4dd095632b54c35fshoward
23550c0e71efd37c04c5f441c7e4dd095632b54c35fshoward    def _ensure_config_parsed(self):
23660cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        """Make sure config files are parsed.
23760cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        """
238d876f459fff6cc4994cab329b1f80c99a86edcbdmbligh        if self.config is None:
23950c0e71efd37c04c5f441c7e4dd095632b54c35fshoward            self.parse_config_file()
24050c0e71efd37c04c5f441c7e4dd095632b54c35fshoward
24150c0e71efd37c04c5f441c7e4dd095632b54c35fshoward
24260cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi    def merge_configs(self, override_config):
24360cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        """Merge existing config values with the ones in given override_config.
24460cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi
24560cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        @param override_config: Configs to override existing config values.
24660cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        """
24760cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        # overwrite whats in config with whats in override_config
24860cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        sections = override_config.sections()
2490afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        for section in sections:
2500afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            # add the section if need be
2510afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            if not self.config.has_section(section):
2520afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski                self.config.add_section(section)
2530afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            # now run through all options and set them
25460cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi            options = override_config.options(section)
2550afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            for option in options:
25660cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi                val = override_config.get(section, option)
2570afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski                self.config.set(section, option, val)
2580afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
2590afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
2600afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski    def parse_config_file(self):
26160cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        """Parse config files."""
2620afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        self.config = ConfigParser.ConfigParser()
263da8fb97be0353f54eb7e62e132482d728684f200lmr        if self.config_file and os.path.exists(self.config_file):
264da8fb97be0353f54eb7e62e132482d728684f200lmr            self.config.read(self.config_file)
265da8fb97be0353f54eb7e62e132482d728684f200lmr        else:
266da8fb97be0353f54eb7e62e132482d728684f200lmr            raise ConfigError('%s not found' % (self.config_file))
2670afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
26860cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        # If it's running in Moblab, read moblab config file if exists,
26960cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        # overwrite the value in global config.
27060cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi        if (lsbrelease_utils.is_moblab() and self.moblab_file and
27160cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi            os.path.exists(self.moblab_file)):
27260cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi            moblab_config = ConfigParser.ConfigParser()
27360cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi            moblab_config.read(self.moblab_file)
27460cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi            # now we merge moblab into global
27560cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi            self.merge_configs(moblab_config)
27660cf6a9294dc53a9fe11dc9b28eaaea1ee69ca92Dan Shi
2770afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        # now also read the shadow file if there is one
2780afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        # this will overwrite anything that is found in the
2790afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        # other config
2806d08b3c3fc2858ccd60a323008703179f93ae38blmr        if self.shadow_file and os.path.exists(self.shadow_file):
2810afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            shadow_config = ConfigParser.ConfigParser()
2820afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            shadow_config.read(self.shadow_file)
2830afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            # now we merge shadow into global
2840afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            self.merge_configs(shadow_config)
2850afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
2860afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
2870afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski    # the values that are pulled from ini
2880afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski    # are strings.  But we should attempt to
2890afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski    # convert them to other types if needed.
290893db3d528e4c7d10ef5b7073dbf63670dffbccashoward    def _convert_value(self, key, section, value, value_type):
2910afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        # strip off leading and trailing white space
2920afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        sval = value.strip()
2930afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
2940afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        # if length of string is zero then return None
2950afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        if len(sval) == 0:
296a5f30c207b211e2bf57c6d4297734da54055af90showard            if value_type == str:
2970afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski                return ""
298a5f30c207b211e2bf57c6d4297734da54055af90showard            elif value_type == bool:
2990afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski                return False
300a5f30c207b211e2bf57c6d4297734da54055af90showard            elif value_type == int:
3010afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski                return 0
302a5f30c207b211e2bf57c6d4297734da54055af90showard            elif value_type == float:
3030afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski                return 0.0
304a5f30c207b211e2bf57c6d4297734da54055af90showard            elif value_type == list:
305c5ddfd1f71caef9ec0c84c53ef7db42fcdc33e1cmbligh                return []
3060afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            else:
3070afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski                return None
3080afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
309a5f30c207b211e2bf57c6d4297734da54055af90showard        if value_type == bool:
3100afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            if sval.lower() == "false":
3110afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski                return False
3120afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            else:
3130afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski                return True
3140afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
315a5f30c207b211e2bf57c6d4297734da54055af90showard        if value_type == list:
316c5ddfd1f71caef9ec0c84c53ef7db42fcdc33e1cmbligh            # Split the string using ',' and return a list
317c5ddfd1f71caef9ec0c84c53ef7db42fcdc33e1cmbligh            return [val.strip() for val in sval.split(',')]
318c5ddfd1f71caef9ec0c84c53ef7db42fcdc33e1cmbligh
3190afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        try:
320a5f30c207b211e2bf57c6d4297734da54055af90showard            conv_val = value_type(sval)
3210afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            return conv_val
3220afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        except:
323a5f30c207b211e2bf57c6d4297734da54055af90showard            msg = ("Could not convert %s value %r in section %s to type %s" %
324a5f30c207b211e2bf57c6d4297734da54055af90showard                    (key, sval, section, value_type))
3250afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            raise ConfigValueError(msg)
3260afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
3270afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
32871206ef92012e5a5c36bbd1ae47e9176be04036dSimran Basi    def get_sections(self):
32971206ef92012e5a5c36bbd1ae47e9176be04036dSimran Basi        """Return a list of sections available."""
33071206ef92012e5a5c36bbd1ae47e9176be04036dSimran Basi        self._ensure_config_parsed()
33171206ef92012e5a5c36bbd1ae47e9176be04036dSimran Basi        return self.config.sections()
33271206ef92012e5a5c36bbd1ae47e9176be04036dSimran Basi
33371206ef92012e5a5c36bbd1ae47e9176be04036dSimran Basi
3340afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski# insure the class is a singleton.  Now the symbol global_config
335ed4d6ddc79993492b22aeeb36574b3cb7c8bad44mbligh# will point to the one and only one instace of the class
336773a86ebaa2e4e4cce213b8553aeb4b30c1f3a9dSimran Basiglobal_config = global_config_class()
337