xref: /aosp_15_r20/external/autotest/client/bin/test_config.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1*9c5db199SXin Li# Lint as: python2, python3
2*9c5db199SXin Li"""
3*9c5db199SXin LiWrapper around ConfigParser to manage testcases configuration.
4*9c5db199SXin Li
5*9c5db199SXin Li@author [email protected] (Ricardo Salveti de Araujo)
6*9c5db199SXin Li"""
7*9c5db199SXin Li
8*9c5db199SXin Lifrom __future__ import absolute_import
9*9c5db199SXin Lifrom __future__ import division
10*9c5db199SXin Lifrom __future__ import print_function
11*9c5db199SXin Lifrom six.moves.configparser import ConfigParser
12*9c5db199SXin Lifrom six.moves import range
13*9c5db199SXin Lifrom six import StringIO
14*9c5db199SXin Lifrom os import path
15*9c5db199SXin Liimport re, six, string
16*9c5db199SXin Lifrom autotest_lib.client.common_lib import utils
17*9c5db199SXin Li
18*9c5db199SXin Li__all__ = ['config_loader']
19*9c5db199SXin Li
20*9c5db199SXin Liclass config_loader:
21*9c5db199SXin Li    """
22*9c5db199SXin Li    Base class of the configuration parser
23*9c5db199SXin Li    """
24*9c5db199SXin Li    def __init__(self, cfg, tmpdir='/tmp', raise_errors=False):
25*9c5db199SXin Li        """
26*9c5db199SXin Li        Instantiate ConfigParser and provide the file like object that we'll
27*9c5db199SXin Li        use to read configuration data from.
28*9c5db199SXin Li        @param cfg: Where we'll get configuration data. It can be either:
29*9c5db199SXin Li                * A URL containing the file
30*9c5db199SXin Li                * A valid file path inside the filesystem
31*9c5db199SXin Li                * A string containing configuration data
32*9c5db199SXin Li        @param tmpdir: Where we'll dump the temporary conf files.
33*9c5db199SXin Li        @param raise_errors: Whether config value absences will raise
34*9c5db199SXin Li                ValueError exceptions.
35*9c5db199SXin Li        """
36*9c5db199SXin Li        # Base Parser
37*9c5db199SXin Li        self.parser = ConfigParser()
38*9c5db199SXin Li        # Raise errors when lacking values
39*9c5db199SXin Li        self.raise_errors = raise_errors
40*9c5db199SXin Li        # File is already a file like object
41*9c5db199SXin Li        if hasattr(cfg, 'read'):
42*9c5db199SXin Li            self.cfg = cfg
43*9c5db199SXin Li            self.parser.readfp(self.cfg)
44*9c5db199SXin Li        elif isinstance(cfg, six.string_types):
45*9c5db199SXin Li            # Config file is a URL. Download it to a temp dir
46*9c5db199SXin Li            if cfg.startswith('http') or cfg.startswith('ftp'):
47*9c5db199SXin Li                self.cfg = path.join(tmpdir, path.basename(cfg))
48*9c5db199SXin Li                utils.urlretrieve(cfg, self.cfg)
49*9c5db199SXin Li                self.parser.read(self.cfg)
50*9c5db199SXin Li            # Config is a valid filesystem path to a file.
51*9c5db199SXin Li            elif path.exists(path.abspath(cfg)):
52*9c5db199SXin Li                if path.isfile(cfg):
53*9c5db199SXin Li                    self.cfg = path.abspath(cfg)
54*9c5db199SXin Li                    self.parser.read(self.cfg)
55*9c5db199SXin Li                else:
56*9c5db199SXin Li                    e_msg = 'Invalid config file path: %s' % cfg
57*9c5db199SXin Li                    raise IOError(e_msg)
58*9c5db199SXin Li            # Config file is just a string, convert it to a python file like
59*9c5db199SXin Li            # object using StringIO
60*9c5db199SXin Li            else:
61*9c5db199SXin Li                self.cfg = StringIO(cfg)
62*9c5db199SXin Li                self.parser.readfp(self.cfg)
63*9c5db199SXin Li
64*9c5db199SXin Li
65*9c5db199SXin Li    def get(self, section, option, default=None):
66*9c5db199SXin Li        """
67*9c5db199SXin Li        Get the value of a option.
68*9c5db199SXin Li
69*9c5db199SXin Li        Section of the config file and the option name.
70*9c5db199SXin Li        You can pass a default value if the option doesn't exist.
71*9c5db199SXin Li
72*9c5db199SXin Li        @param section: Configuration file section.
73*9c5db199SXin Li        @param option: Option we're looking after.
74*9c5db199SXin Li        @default: In case the option is not available and raise_errors is set
75*9c5db199SXin Li                to False, return the default.
76*9c5db199SXin Li        """
77*9c5db199SXin Li        if not self.parser.has_option(section, option):
78*9c5db199SXin Li            if self.raise_errors:
79*9c5db199SXin Li                raise ValueError('No value for option %s. Please check your '
80*9c5db199SXin Li                                 'config file "%s".' % (option, self.cfg))
81*9c5db199SXin Li            else:
82*9c5db199SXin Li                return default
83*9c5db199SXin Li
84*9c5db199SXin Li        return self.parser.get(section, option)
85*9c5db199SXin Li
86*9c5db199SXin Li
87*9c5db199SXin Li    def set(self, section, option, value):
88*9c5db199SXin Li        """
89*9c5db199SXin Li        Set an option.
90*9c5db199SXin Li
91*9c5db199SXin Li        This change is not persistent unless saved with 'save()'.
92*9c5db199SXin Li        """
93*9c5db199SXin Li        if not self.parser.has_section(section):
94*9c5db199SXin Li            self.parser.add_section(section)
95*9c5db199SXin Li        return self.parser.set(section, option, value)
96*9c5db199SXin Li
97*9c5db199SXin Li
98*9c5db199SXin Li    def remove(self, section, option):
99*9c5db199SXin Li        """
100*9c5db199SXin Li        Remove an option.
101*9c5db199SXin Li        """
102*9c5db199SXin Li        if self.parser.has_section(section):
103*9c5db199SXin Li            self.parser.remove_option(section, option)
104*9c5db199SXin Li
105*9c5db199SXin Li
106*9c5db199SXin Li    def save(self):
107*9c5db199SXin Li        """
108*9c5db199SXin Li        Save the configuration file with all modifications
109*9c5db199SXin Li        """
110*9c5db199SXin Li        if not self.cfg:
111*9c5db199SXin Li            return
112*9c5db199SXin Li        fileobj = open(self.cfg, 'w')
113*9c5db199SXin Li        try:
114*9c5db199SXin Li            self.parser.write(fileobj)
115*9c5db199SXin Li        finally:
116*9c5db199SXin Li            fileobj.close()
117*9c5db199SXin Li
118*9c5db199SXin Li
119*9c5db199SXin Li    def check(self, section):
120*9c5db199SXin Li        """
121*9c5db199SXin Li        Check if the config file has valid values
122*9c5db199SXin Li        """
123*9c5db199SXin Li        if not self.parser.has_section(section):
124*9c5db199SXin Li            return False, "Section not found: %s"%(section)
125*9c5db199SXin Li
126*9c5db199SXin Li        options = self.parser.items(section)
127*9c5db199SXin Li        for i in range(options.__len__()):
128*9c5db199SXin Li            param = options[i][0]
129*9c5db199SXin Li            aux = string.split(param, '.')
130*9c5db199SXin Li
131*9c5db199SXin Li            if aux.__len__ < 2:
132*9c5db199SXin Li                return False, "Invalid parameter syntax at %s"%(param)
133*9c5db199SXin Li
134*9c5db199SXin Li            if not self.check_parameter(aux[0], options[i][1]):
135*9c5db199SXin Li                return False, "Invalid value at %s"%(param)
136*9c5db199SXin Li
137*9c5db199SXin Li        return True, None
138*9c5db199SXin Li
139*9c5db199SXin Li
140*9c5db199SXin Li    def check_parameter(self, param_type, parameter):
141*9c5db199SXin Li        """
142*9c5db199SXin Li        Check if a option has a valid value
143*9c5db199SXin Li        """
144*9c5db199SXin Li        if parameter == '' or parameter == None:
145*9c5db199SXin Li            return False
146*9c5db199SXin Li        elif param_type == "ip" and self.__isipaddress(parameter):
147*9c5db199SXin Li            return True
148*9c5db199SXin Li        elif param_type == "int" and self.__isint(parameter):
149*9c5db199SXin Li            return True
150*9c5db199SXin Li        elif param_type == "float" and self.__isfloat(parameter):
151*9c5db199SXin Li            return True
152*9c5db199SXin Li        elif param_type == "str" and self.__isstr(parameter):
153*9c5db199SXin Li            return True
154*9c5db199SXin Li
155*9c5db199SXin Li        return False
156*9c5db199SXin Li
157*9c5db199SXin Li
158*9c5db199SXin Li    def __isipaddress(self, parameter):
159*9c5db199SXin Li        """
160*9c5db199SXin Li        Verify if the ip address is valid
161*9c5db199SXin Li
162*9c5db199SXin Li        @param ip String: IP Address
163*9c5db199SXin Li        @return True if a valid IP Address or False
164*9c5db199SXin Li        """
165*9c5db199SXin Li        octet1 = "([1-9][0-9]{,1}|1[0-9]{2}|2[0-4][0-9]|25[0-5])"
166*9c5db199SXin Li        octet = "([0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])"
167*9c5db199SXin Li        pattern = "^" + octet1 + "\.(" + octet + "\.){2}" + octet + "$"
168*9c5db199SXin Li        if re.match(pattern, parameter) == None:
169*9c5db199SXin Li            return False
170*9c5db199SXin Li        else:
171*9c5db199SXin Li            return True
172*9c5db199SXin Li
173*9c5db199SXin Li
174*9c5db199SXin Li    def __isint(self, parameter):
175*9c5db199SXin Li        try:
176*9c5db199SXin Li            int(parameter)
177*9c5db199SXin Li        except Exception as e_stack:
178*9c5db199SXin Li            return False
179*9c5db199SXin Li        return True
180*9c5db199SXin Li
181*9c5db199SXin Li
182*9c5db199SXin Li    def __isfloat(self, parameter):
183*9c5db199SXin Li        try:
184*9c5db199SXin Li            float(parameter)
185*9c5db199SXin Li        except Exception as e_stack:
186*9c5db199SXin Li            return False
187*9c5db199SXin Li        return True
188*9c5db199SXin Li
189*9c5db199SXin Li
190*9c5db199SXin Li    def __isstr(self, parameter):
191*9c5db199SXin Li        try:
192*9c5db199SXin Li            str(parameter)
193*9c5db199SXin Li        except Exception as e_stack:
194*9c5db199SXin Li            return False
195*9c5db199SXin Li        return True
196