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