global_config.py revision da8fb97be0353f54eb7e62e132482d728684f200
1ed4d6ddc79993492b22aeeb36574b3cb7c8bad44mbligh"""A singleton class for accessing global config values
2ed4d6ddc79993492b22aeeb36574b3cb7c8bad44mbligh
3ed4d6ddc79993492b22aeeb36574b3cb7c8bad44mblighprovides access to global configuration file
4ed4d6ddc79993492b22aeeb36574b3cb7c8bad44mbligh"""
5ed4d6ddc79993492b22aeeb36574b3cb7c8bad44mbligh
6ed4d6ddc79993492b22aeeb36574b3cb7c8bad44mbligh__author__ = 'raphtee@google.com (Travis Miller)'
7ed4d6ddc79993492b22aeeb36574b3cb7c8bad44mbligh
8da8fb97be0353f54eb7e62e132482d728684f200lmrimport os, sys, ConfigParser, logging
911788296e58691a3149355a4fc4a3fa1084c689dmblighfrom autotest_lib.client.common_lib import error
10ed4d6ddc79993492b22aeeb36574b3cb7c8bad44mbligh
11104e9ce7a74041fd673ceac5c8b0bc67c74edcd5mbligh
12ed4d6ddc79993492b22aeeb36574b3cb7c8bad44mblighclass ConfigError(error.AutotestError):
130afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski    pass
14ed4d6ddc79993492b22aeeb36574b3cb7c8bad44mbligh
15ed4d6ddc79993492b22aeeb36574b3cb7c8bad44mbligh
16104e9ce7a74041fd673ceac5c8b0bc67c74edcd5mblighclass ConfigValueError(ConfigError):
170afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski    pass
18104e9ce7a74041fd673ceac5c8b0bc67c74edcd5mbligh
19104e9ce7a74041fd673ceac5c8b0bc67c74edcd5mbligh
206d08b3c3fc2858ccd60a323008703179f93ae38blmr
216d08b3c3fc2858ccd60a323008703179f93ae38blmrcommon_lib_dir = os.path.dirname(sys.modules[__name__].__file__)
226d08b3c3fc2858ccd60a323008703179f93ae38blmrclient_dir = os.path.dirname(common_lib_dir)
236d08b3c3fc2858ccd60a323008703179f93ae38blmrroot_dir = os.path.dirname(client_dir)
246d08b3c3fc2858ccd60a323008703179f93ae38blmr
256d08b3c3fc2858ccd60a323008703179f93ae38blmr# Check if the config files are at autotest's root dir
266d08b3c3fc2858ccd60a323008703179f93ae38blmr# This will happen if client is executing inside a full autotest tree, or if
276d08b3c3fc2858ccd60a323008703179f93ae38blmr# other entry points are being executed
286d08b3c3fc2858ccd60a323008703179f93ae38blmrglobal_config_path_root = os.path.join(root_dir, 'global_config.ini')
296d08b3c3fc2858ccd60a323008703179f93ae38blmrshadow_config_path_root = os.path.join(root_dir, 'shadow_config.ini')
306d08b3c3fc2858ccd60a323008703179f93ae38blmrconfig_in_root = (os.path.exists(global_config_path_root) and
316d08b3c3fc2858ccd60a323008703179f93ae38blmr                  os.path.exists(shadow_config_path_root))
326d08b3c3fc2858ccd60a323008703179f93ae38blmr
336d08b3c3fc2858ccd60a323008703179f93ae38blmr# Check if the config files are at autotest's client dir
346d08b3c3fc2858ccd60a323008703179f93ae38blmr# This will happen if a client stand alone execution is happening
356d08b3c3fc2858ccd60a323008703179f93ae38blmrglobal_config_path_client = os.path.join(client_dir, 'global_config.ini')
366d08b3c3fc2858ccd60a323008703179f93ae38blmrconfig_in_client = os.path.exists(global_config_path_client)
376d08b3c3fc2858ccd60a323008703179f93ae38blmr
386d08b3c3fc2858ccd60a323008703179f93ae38blmrif config_in_root:
396d08b3c3fc2858ccd60a323008703179f93ae38blmr    DEFAULT_CONFIG_FILE = global_config_path_root
406d08b3c3fc2858ccd60a323008703179f93ae38blmr    DEFAULT_SHADOW_FILE = shadow_config_path_root
416d08b3c3fc2858ccd60a323008703179f93ae38blmr    RUNNING_STAND_ALONE_CLIENT = False
426d08b3c3fc2858ccd60a323008703179f93ae38blmrelif config_in_client:
436d08b3c3fc2858ccd60a323008703179f93ae38blmr    DEFAULT_CONFIG_FILE = global_config_path_client
446d08b3c3fc2858ccd60a323008703179f93ae38blmr    DEFAULT_SHADOW_FILE = None
456d08b3c3fc2858ccd60a323008703179f93ae38blmr    RUNNING_STAND_ALONE_CLIENT = True
466d08b3c3fc2858ccd60a323008703179f93ae38blmrelse:
47da8fb97be0353f54eb7e62e132482d728684f200lmr    DEFAULT_CONFIG_FILE = None
48da8fb97be0353f54eb7e62e132482d728684f200lmr    DEFAULT_SHADOW_FILE = None
49da8fb97be0353f54eb7e62e132482d728684f200lmr    RUNNING_STAND_ALONE_CLIENT = True
506d08b3c3fc2858ccd60a323008703179f93ae38blmr
51ed4d6ddc79993492b22aeeb36574b3cb7c8bad44mblighclass global_config(object):
52893db3d528e4c7d10ef5b7073dbf63670dffbccashoward    _NO_DEFAULT_SPECIFIED = object()
53893db3d528e4c7d10ef5b7073dbf63670dffbccashoward
540afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski    config = None
550afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski    config_file = DEFAULT_CONFIG_FILE
560afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski    shadow_file = DEFAULT_SHADOW_FILE
576d08b3c3fc2858ccd60a323008703179f93ae38blmr    running_stand_alone_client = RUNNING_STAND_ALONE_CLIENT
586d08b3c3fc2858ccd60a323008703179f93ae38blmr
596d08b3c3fc2858ccd60a323008703179f93ae38blmr
606d08b3c3fc2858ccd60a323008703179f93ae38blmr    def check_stand_alone_client_run(self):
616d08b3c3fc2858ccd60a323008703179f93ae38blmr        return self.running_stand_alone_client
620afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
630afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
640afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski    def set_config_files(self, config_file=DEFAULT_CONFIG_FILE,
650afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski                            shadow_file=DEFAULT_SHADOW_FILE):
660afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        self.config_file = config_file
670afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        self.shadow_file = shadow_file
680afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        self.config = None
690afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
700afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
71d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward    def _handle_no_value(self, section, key, default):
72893db3d528e4c7d10ef5b7073dbf63670dffbccashoward        if default is self._NO_DEFAULT_SPECIFIED:
73d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward            msg = ("Value '%s' not found in section '%s'" %
74d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward                   (key, section))
75d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward            raise ConfigError(msg)
76d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward        else:
77d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward            return default
78d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward
79d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward
806d08b3c3fc2858ccd60a323008703179f93ae38blmr    def get_section_values(self, section):
816d08b3c3fc2858ccd60a323008703179f93ae38blmr        """
826d08b3c3fc2858ccd60a323008703179f93ae38blmr        Return a config parser object containing a single section of the
836d08b3c3fc2858ccd60a323008703179f93ae38blmr        global configuration, that can be later written to a file object.
846d08b3c3fc2858ccd60a323008703179f93ae38blmr
856d08b3c3fc2858ccd60a323008703179f93ae38blmr        @param section: Section we want to turn into a config parser object.
866d08b3c3fc2858ccd60a323008703179f93ae38blmr        @return: ConfigParser() object containing all the contents of section.
876d08b3c3fc2858ccd60a323008703179f93ae38blmr        """
886d08b3c3fc2858ccd60a323008703179f93ae38blmr        cfgparser = ConfigParser.ConfigParser()
896d08b3c3fc2858ccd60a323008703179f93ae38blmr        cfgparser.add_section(section)
906d08b3c3fc2858ccd60a323008703179f93ae38blmr        for option, value in self.config.items(section):
916d08b3c3fc2858ccd60a323008703179f93ae38blmr            cfgparser.set(section, option, value)
926d08b3c3fc2858ccd60a323008703179f93ae38blmr        return cfgparser
936d08b3c3fc2858ccd60a323008703179f93ae38blmr
946d08b3c3fc2858ccd60a323008703179f93ae38blmr
95893db3d528e4c7d10ef5b7073dbf63670dffbccashoward    def get_config_value(self, section, key, type=str,
96893db3d528e4c7d10ef5b7073dbf63670dffbccashoward                         default=_NO_DEFAULT_SPECIFIED, allow_blank=False):
9750c0e71efd37c04c5f441c7e4dd095632b54c35fshoward        self._ensure_config_parsed()
980afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
990afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        try:
1000afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            val = self.config.get(section, key)
101d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward        except ConfigParser.Error:
102d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward            return self._handle_no_value(section, key, default)
103d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward
104d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward        if not val.strip() and not allow_blank:
105d1ee1dd3f3e5ac44f00d7a96deb815dbe1beedadshoward            return self._handle_no_value(section, key, default)
1060afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
107893db3d528e4c7d10ef5b7073dbf63670dffbccashoward        return self._convert_value(key, section, val, type)
1080afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
1090afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
11050c0e71efd37c04c5f441c7e4dd095632b54c35fshoward    def override_config_value(self, section, key, new_value):
11150c0e71efd37c04c5f441c7e4dd095632b54c35fshoward        """
11250c0e71efd37c04c5f441c7e4dd095632b54c35fshoward        Override a value from the config file with a new value.
11350c0e71efd37c04c5f441c7e4dd095632b54c35fshoward        """
11450c0e71efd37c04c5f441c7e4dd095632b54c35fshoward        self._ensure_config_parsed()
11550c0e71efd37c04c5f441c7e4dd095632b54c35fshoward        self.config.set(section, key, new_value)
11650c0e71efd37c04c5f441c7e4dd095632b54c35fshoward
11750c0e71efd37c04c5f441c7e4dd095632b54c35fshoward
11850c0e71efd37c04c5f441c7e4dd095632b54c35fshoward    def reset_config_values(self):
11950c0e71efd37c04c5f441c7e4dd095632b54c35fshoward        """
12050c0e71efd37c04c5f441c7e4dd095632b54c35fshoward        Reset all values to those found in the config files (undoes all
12150c0e71efd37c04c5f441c7e4dd095632b54c35fshoward        overrides).
12250c0e71efd37c04c5f441c7e4dd095632b54c35fshoward        """
12350c0e71efd37c04c5f441c7e4dd095632b54c35fshoward        self.parse_config_file()
12450c0e71efd37c04c5f441c7e4dd095632b54c35fshoward
12550c0e71efd37c04c5f441c7e4dd095632b54c35fshoward
12650c0e71efd37c04c5f441c7e4dd095632b54c35fshoward    def _ensure_config_parsed(self):
127d876f459fff6cc4994cab329b1f80c99a86edcbdmbligh        if self.config is None:
12850c0e71efd37c04c5f441c7e4dd095632b54c35fshoward            self.parse_config_file()
12950c0e71efd37c04c5f441c7e4dd095632b54c35fshoward
13050c0e71efd37c04c5f441c7e4dd095632b54c35fshoward
1310afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski    def merge_configs(self, shadow_config):
1320afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        # overwrite whats in config with whats in shadow_config
1330afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        sections = shadow_config.sections()
1340afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        for section in sections:
1350afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            # add the section if need be
1360afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            if not self.config.has_section(section):
1370afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski                self.config.add_section(section)
1380afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            # now run through all options and set them
1390afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            options = shadow_config.options(section)
1400afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            for option in options:
1410afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski                val = shadow_config.get(section, option)
1420afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski                self.config.set(section, option, val)
1430afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
1440afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
1450afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski    def parse_config_file(self):
1460afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        self.config = ConfigParser.ConfigParser()
147da8fb97be0353f54eb7e62e132482d728684f200lmr        if self.config_file and os.path.exists(self.config_file):
148da8fb97be0353f54eb7e62e132482d728684f200lmr            self.config.read(self.config_file)
149da8fb97be0353f54eb7e62e132482d728684f200lmr        else:
150da8fb97be0353f54eb7e62e132482d728684f200lmr            raise ConfigError('%s not found' % (self.config_file))
1510afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
1520afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        # now also read the shadow file if there is one
1530afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        # this will overwrite anything that is found in the
1540afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        # other config
1556d08b3c3fc2858ccd60a323008703179f93ae38blmr        if self.shadow_file and os.path.exists(self.shadow_file):
1560afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            shadow_config = ConfigParser.ConfigParser()
1570afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            shadow_config.read(self.shadow_file)
1580afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            # now we merge shadow into global
1590afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            self.merge_configs(shadow_config)
1600afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
1610afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
1620afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski    # the values that are pulled from ini
1630afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski    # are strings.  But we should attempt to
1640afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski    # convert them to other types if needed.
165893db3d528e4c7d10ef5b7073dbf63670dffbccashoward    def _convert_value(self, key, section, value, value_type):
1660afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        # strip off leading and trailing white space
1670afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        sval = value.strip()
1680afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
1690afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        # if length of string is zero then return None
1700afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        if len(sval) == 0:
171a5f30c207b211e2bf57c6d4297734da54055af90showard            if value_type == str:
1720afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski                return ""
173a5f30c207b211e2bf57c6d4297734da54055af90showard            elif value_type == bool:
1740afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski                return False
175a5f30c207b211e2bf57c6d4297734da54055af90showard            elif value_type == int:
1760afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski                return 0
177a5f30c207b211e2bf57c6d4297734da54055af90showard            elif value_type == float:
1780afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski                return 0.0
179a5f30c207b211e2bf57c6d4297734da54055af90showard            elif value_type == list:
180c5ddfd1f71caef9ec0c84c53ef7db42fcdc33e1cmbligh                return []
1810afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            else:
1820afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski                return None
1830afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
184a5f30c207b211e2bf57c6d4297734da54055af90showard        if value_type == bool:
1850afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            if sval.lower() == "false":
1860afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski                return False
1870afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            else:
1880afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski                return True
1890afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
190a5f30c207b211e2bf57c6d4297734da54055af90showard        if value_type == list:
191c5ddfd1f71caef9ec0c84c53ef7db42fcdc33e1cmbligh            # Split the string using ',' and return a list
192c5ddfd1f71caef9ec0c84c53ef7db42fcdc33e1cmbligh            return [val.strip() for val in sval.split(',')]
193c5ddfd1f71caef9ec0c84c53ef7db42fcdc33e1cmbligh
1940afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        try:
195a5f30c207b211e2bf57c6d4297734da54055af90showard            conv_val = value_type(sval)
1960afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            return conv_val
1970afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski        except:
198a5f30c207b211e2bf57c6d4297734da54055af90showard            msg = ("Could not convert %s value %r in section %s to type %s" %
199a5f30c207b211e2bf57c6d4297734da54055af90showard                    (key, sval, section, value_type))
2000afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski            raise ConfigValueError(msg)
2010afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
2020afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski
2030afbb6369aa5aa9a75ea67dd9e95ec4b21c0c181jadmanski# insure the class is a singleton.  Now the symbol global_config
204ed4d6ddc79993492b22aeeb36574b3cb7c8bad44mbligh# will point to the one and only one instace of the class
205ed4d6ddc79993492b22aeeb36574b3cb7c8bad44mblighglobal_config = global_config()
206