Merge branch 'master' of ssh://git.planet-lab.org/git/plstackapi
[plstackapi.git] / planetstack / planetstack / config.py
1 #!/usr/bin/python
2 import sys
3 import os
4 import time
5 import ConfigParser
6 import tempfile
7 import codecs
8 from StringIO import StringIO
9 from util.xml import Xml
10
11 default_config = \
12 """
13 """
14
15 DEFAULT_CONFIG_FN = '/opt/planetstack/plstackapi_config'
16
17 def isbool(v):
18         return v.lower() in ("true", "false")
19
20 def str2bool(v):
21         return v.lower() in ("true", "1")
22
23 class Config:
24
25         def __init__(self, config_file=None):
26                 if (config_file==None):
27                     config_file = self.get_config_fn()
28
29                 self._files = []
30                 self.config_path = os.path.dirname(config_file)
31                 self.config = ConfigParser.ConfigParser()
32                 self.filename = config_file
33                 if not os.path.isfile(self.filename):
34                         self.create(self.filename)
35                 self.load(self.filename)
36
37         def get_config_fn(self):
38              # Look for "-C <something>" to get the
39              # name of the config file. Using a real OptionParser here is
40              # problematic as it will throw 'no such option' errors for options
41              # that it does not understand.
42
43              last = None
44              for arg in sys.argv:
45                  if (last=="-C"):
46                      return arg
47                  last = arg
48
49              return DEFAULT_CONFIG_FN
50
51         def _header(self):
52                 header = """
53 DO NOT EDIT. This file was automatically generated at
54 %s from:
55
56 %s
57 """ % (time.asctime(), os.linesep.join(self._files))
58
59                 # Get rid of the surrounding newlines
60                 return header.strip().split(os.linesep)
61
62         def create(self, filename):
63                 if not os.path.exists(os.path.dirname(filename)):
64                         os.makedirs(os.path.dirname(filename))
65                 configfile = open(filename, 'w')
66                 configfile.write(default_config)
67                 configfile.close()
68
69
70         def load(self, filename):
71                 if filename:
72                         try:
73                                 self.config.read(filename)
74                         except ConfigParser.MissingSectionHeaderError:
75                                 if filename.endswith('.xml'):
76                                         self.load_xml(filename)
77                                 else:
78                                         self.load_shell(filename)
79                         self._files.append(filename)
80                         self.set_attributes()
81
82         def load_xml(self, filename):
83                 xml = XML(filename)
84                 categories = xml.xpath('//configuration/variables/category')
85                 for category in categories:
86                         section_name = category.get('id')
87                         if not self.config.has_section(section_name):
88                                 self.config.add_section(section_name)
89                         options = category.xpath('./variablelist/variable')
90                         for option in options:
91                                 option_name = option.get('id')
92                                 value = option.xpath('./value')[0].text
93                                 if not value:
94                                         value = ""
95                                 self.config.set(section_name, option_name, value)
96
97         def load_shell(self, filename):
98                 f = open(filename, 'r')
99                 for line in f:
100                         try:
101                                 if line.startswith('#'):
102                                         continue
103                                 parts = line.strip().split("=")
104                                 if len(parts) < 2:
105                                         continue
106                                 option = parts[0]
107                                 value = parts[1].replace('"', '').replace("'","")
108                                 section, var = self.locate_varname(option, strict=False)
109                                 if section and var:
110                                         self.set(section, var, value)
111                         except:
112                                 pass
113                 f.close()
114
115         def locate_varname(self, varname, strict=True):
116                 varname = varname.lower()
117                 sections = self.config.sections()
118                 section_name = ""
119                 var_name = ""
120                 for section in sections:
121                         if varname.startswith(section.lower()) and len(section) > len(section_name):
122                                 section_name = section.lower()
123                                 var_name = varname.replace(section_name, "")[1:]
124                 if strict and not self.config.has_option(section_name, var_name):
125                         raise ConfigParser.NoOptionError(var_name, section_name)
126                 return (section_name, var_name)
127
128         def set_attributes(self):
129                 sections = self.config.sections()
130                 for section in sections:
131                         for item in self.config.items(section):
132                                 name = "%s_%s" % (section, item[0])
133                                 value = item[1]
134                                 if isbool(value):
135                                         value = str2bool(value)
136                                 elif value.isdigit():
137                                         value = int(value)
138                                 setattr(self, name, value)
139                                 setattr(self, name.upper(), value)
140
141
142         def verify(self, config1, config2, validate_method):
143                 return True
144
145         def validate_type(self, var_type, value):
146                 return True
147
148         @staticmethod
149         def is_xml(config_file):
150                 try:
151                         x = Xml(config_file)
152                         return True
153                 except:
154                         return False
155
156         @staticmethod
157         def is_ini(config_file):
158                 try:
159                         c = ConfigParser.ConfigParser()
160                         c.read(config_file)
161                         return True
162                 except ConfigParser.MissingSectionHeaderError:
163                         return False
164
165
166         def dump(self, sections = []):
167                 sys.stdout.write(output_python())
168
169         def output_python(self, encoding = "utf-8"):
170                 buf = codecs.lookup(encoding)[3](StringIO())
171                 buf.writelines(["# " + line + os.linesep for line in self._header()])
172
173                 for section in self.sections():
174                         buf.write("[%s]%s" % (section, os.linesep))
175                         for (name,value) in self.items(section):
176                                 buf.write("%s=%s%s" % (name,value,os.linesep))
177                         buf.write(os.linesep)
178                 return buf.getvalue()
179
180         def output_shell(self, show_comments = True, encoding = "utf-8"):
181                 """
182                 Return variables as a shell script.
183                 """
184
185                 buf = codecs.lookup(encoding)[3](StringIO())
186                 buf.writelines(["# " + line + os.linesep for line in self._header()])
187
188                 for section in self.sections():
189                         for (name,value) in self.items(section):
190                                 # bash does not have the concept of NULL
191                                 if value:
192                                         option = "%s_%s" % (section.upper(), name.upper())
193                                         if isbool(value):
194                                                 value = str(str2bool(value))
195                                         elif not value.isdigit():
196                                                 value = '"%s"' % value
197                                         buf.write(option + "=" + value + os.linesep)
198                 return buf.getvalue()
199
200         def output_php(self, encoding = "utf-8"):
201                 """
202                 Return variables as a PHP script.
203                 """
204
205                 buf = codecs.lookup(encoding)[3](StringIO())
206                 buf.write("<?php" + os.linesep)
207                 buf.writelines(["// " + line + os.linesep for line in self._header()])
208
209                 for section in self.sections():
210                         for (name,value) in self.items(section):
211                                 option = "%s_%s" % (section, name)
212                                 buf.write(os.linesep)
213                                 buf.write("// " + option + os.linesep)
214                                 if value is None:
215                                         value = 'NULL'
216                                 buf.write("define('%s', %s);" % (option, value) + os.linesep)
217
218                 buf.write("?>" + os.linesep)
219
220                 return buf.getvalue()
221
222         def output_xml(self, encoding = "utf-8"):
223                 pass
224
225         def output_variables(self, encoding="utf-8"):
226                 """
227                 Return list of all variable names.
228                 """
229
230                 buf = codecs.lookup(encoding)[3](StringIO())
231                 for section in self.sections():
232                         for (name,value) in self.items(section):
233                                 option = "%s_%s" % (section,name)
234                                 buf.write(option + os.linesep)
235
236                 return buf.getvalue()
237                 pass
238
239         def write(self, filename=None):
240                 if not filename:
241                         filename = self.filename
242                 configfile = open(filename, 'w')
243                 self.config.write(configfile)
244
245         def save(self, filename=None):
246                 self.write(filename)
247
248         def __getattr__(self, attr):
249                 return getattr(self.config, attr)
250
251 if __name__ == '__main__':
252         filename = None
253         if len(sys.argv) > 1:
254                 filename = sys.argv[1]
255                 config = Config(filename)
256         else:
257                 config = Config()
258         config.dump()