Upload the contribution of vstf as bottleneck network framework.
[bottlenecks.git] / vstf / vstf / controller / settings / settings.py
1 #!/usr/bin/python
2 # -*- coding: utf8 -*-
3 # author: wly
4 # date: 2015-09-06
5 # see license for license details
6
7 import json
8 import re
9 import os
10 import copy
11 import logging
12 import sys
13
14 LOG = logging.getLogger(__name__)
15
16
17 def object2dict(obj):
18     # convert object to a dict
19     dic = {'__class__': obj.__class__.__name__, '__module__': obj.__module__}
20     dic.update(obj.__dict__)
21     return dic
22
23
24 def dict2object(dic):
25     # convert dict to object
26     if '__class__' in dic:
27         class_name = dic.pop('__class__')
28         module_name = dic.pop('__module__')
29         module = __import__(module_name)
30         class_ = getattr(module, class_name)
31         args = dict((key.encode('ascii'), value) for key, value in dic.items())  # get args
32         inst = class_(**args)  # create new instance
33     else:
34         inst = dic
35     return inst
36
37
38 def filter_comments(filename, flags="//"):
39     result = []
40     with open(filename, "r") as ifile:
41         lines = ifile.readlines()
42         for data in lines:
43             data = re.sub("%s.*$" % (flags), '', data)
44             data = re.sub("^\s*$", '', data)
45             if data:
46                 result.append(data)
47     LOG.debug(result)
48     return ''.join(result)
49
50
51 class BaseSettings(object):
52     def _load(self, fullname):
53         data = filter_comments(fullname)
54         LOG.debug(fullname)
55         LOG.debug(data)
56         jparams = None
57         if data:
58             jparams = json.loads(data)
59         return jparams
60
61     def _sub(self, ldata, rdata):
62         if isinstance(ldata, list) and isinstance(rdata, list):
63             data = []
64             if ldata:
65                 for litem in ldata:
66                     if rdata:
67                         for ritem in rdata:
68                             if isinstance(litem, dict) or isinstance(litem, list):
69                                 tmp = self._sub(litem, ritem)
70                             else:
71                                 tmp = ritem
72                             if tmp and tmp not in data:
73                                 data.append(tmp)
74                     else:
75                         data.append(litem)
76
77             else:
78                 data = rdata
79
80         elif isinstance(ldata, dict) and isinstance(rdata, dict):
81             data = {}
82             rdata_bak = copy.deepcopy(rdata)
83             for rkey, rvalue in rdata_bak.items():
84                 if rkey not in ldata:
85                     rdata_bak.pop(rkey)
86             for lkey, lvalue in ldata.items():
87                 if lkey in rdata:
88                     if isinstance(lvalue, dict) or isinstance(lvalue, list):
89                         data[lkey] = self._sub(lvalue, rdata[lkey])
90                     else:
91                         data[lkey] = rdata[lkey]
92                 else:
93                     if rdata_bak:
94                         data[lkey] = lvalue
95         else:
96             data = rdata
97
98         return data
99
100     def _save(self, data, filename):
101         if os.path.exists(filename):
102             os.remove(filename)
103         with open(filename, 'w') as ofile:
104             content = json.dumps(data, sort_keys=True, indent=4, separators=(',', ':'))
105             ofile.write(content)
106
107
108 class DefaultSettings(BaseSettings):
109     def __init__(self, path):
110         self._default = os.path.join(path, 'default')
111         self._user = os.path.join(path, 'user')
112     
113     def load(self, filename):
114         dfile = os.path.join(self._default, filename)
115         if os.path.exists(dfile):
116             ddata = self._load(dfile)
117             data = ddata
118         else:
119             err = "default file is missing : %s" % (dfile)
120             LOG.error(err)
121             raise Exception(err)
122         ufile = os.path.join(self._user, filename)
123         if os.path.exists(ufile):
124             udata = self._load(ufile)
125             if udata:
126                 data = self._sub(ddata, udata)
127         else:
128             LOG.info("no user file :%s" % (ufile))
129         return data
130
131     def save(self, data, filename):
132         ufile = os.path.join(self._user, filename)
133         self._save(data, ufile)
134
135
136 class SingleSettings(BaseSettings):
137     def __init__(self, path):
138         self._path = path
139
140     def load(self, filename):
141         pfile = os.path.join(self._path, filename)
142         if os.path.exists(pfile):
143             ddata = self._load(pfile)
144             data = ddata
145         else:
146             err = "settings file is missing : %s" % (pfile)
147             LOG.error(err)
148             raise Exception(err)
149         return data
150
151     def save(self, data, filename):
152         pfile = os.path.join(self._path, filename)
153         self._save(data, pfile)
154
155 SETS_DEFAULT = "Default"
156 SETS_SINGLE = "Single"
157 SETTINGS = [SETS_SINGLE, SETS_DEFAULT]
158
159
160 class Settings(object):
161     def __init__(self, path, filename, mode=SETS_SINGLE):
162         if mode not in SETTINGS:
163             raise Exception("error Settings mode : %s" % (mode))
164         cls_name = mode + "Settings"
165         thismodule = sys.modules[__name__]
166         cls = getattr(thismodule, cls_name)
167         self._settings = cls(path)
168         self._filename = filename
169         self._fset = self._settings.load(filename)
170         self._mset = copy.deepcopy(self._fset)
171         self._register_func()
172
173     def reset(self):
174         self._fset = self._settings.load(self._filename)
175         self._mset = copy.deepcopy(self._fset)
176
177     @property
178     def settings(self):
179         return self._mset
180
181     def _setting_file(self, func_name, mset, fset, key, check=None):
182         def infunc(value):
183             if hasattr(check, '__call__'):
184                 check(value)
185             if isinstance(fset, dict):
186                 mset[key] = copy.deepcopy(value)
187                 fset[key] = copy.deepcopy(value)
188             elif isinstance(fset, list):
189                 del (mset[:])
190                 del (fset[:])
191                 mset.extend(copy.deepcopy(value))
192                 fset.extend(copy.deepcopy(value))
193             self._settings.save(self._fset, self._filename)
194             infunc.__name__ = func_name
195             LOG.debug(self._mset)
196             LOG.debug(self._fset)
197
198         return infunc
199
200     def _setting_memory(self, func_name, mset, key, check=None):
201         def infunc(value):
202             if hasattr(check, '__call__'):
203                 check(value)
204             if isinstance(mset, dict):
205                 mset[key] = copy.deepcopy(value)
206             elif isinstance(mset, list):
207                 for i in range(len(mset)):
208                     mset.pop()
209                 mset.extend(copy.deepcopy(value))
210
211             infunc.__name__ = func_name
212             LOG.debug(self._mset)
213             LOG.debug(self._fset)
214
215         return infunc
216
217     def _adding_file(self, func_name, mset, fset, key, check=None):
218         def infunc(value):
219             if hasattr(check, '__call__'):
220                 check(value)
221             if key:
222                 mset[key].append(copy.deepcopy(value))
223                 fset[key].append(copy.deepcopy(value))
224             else:
225                 mset.append(copy.deepcopy(value))
226                 fset.append(copy.deepcopy(value))
227
228             self._settings.save(self._fset, self._filename)
229             infunc.__name__ = func_name
230             LOG.debug(self._mset)
231             LOG.debug(self._fset)
232
233         return infunc
234
235     def _adding_memory(self, func_name, mset, key, check=None):
236         def infunc(value):
237             if hasattr(check, '__call__'):
238                 check(value)
239             if key:
240                 mset[key].append(copy.deepcopy(value))
241             else:
242                 mset.append(copy.deepcopy(value))
243             infunc.__name__ = func_name
244             LOG.debug(self._mset)
245             LOG.debug(self._fset)
246
247         return infunc
248
249     def _register_func(self):
250         if isinstance(self._fset, dict):
251             items = set(
252                 self._fset.keys()
253             )
254             for item in items:
255                 item = item.encode()
256                 func_name = "set_%s" % item
257                 setattr(self, func_name, self._setting_file(func_name, self._mset, self._fset, item))
258                 func_name = "mset_%s" % item
259                 setattr(self, func_name, self._setting_memory(func_name, self._mset, item))
260         elif isinstance(self._fset, list):
261             func_name = "set"
262             setattr(self, func_name, self._setting_file(func_name, self._mset, self._fset, None))
263             func_name = "mset"
264             setattr(self, func_name, self._setting_memory(func_name, self._mset, None))
265             func_name = "add"
266             setattr(self, func_name, self._adding_file(func_name, self._mset, self._fset, None))
267             func_name = "madd"
268             setattr(self, func_name, self._adding_memory(func_name, self._mset, None))
269
270
271 def unit_test():
272     from vstf.common.log import setup_logging
273     setup_logging(level=logging.DEBUG, log_file="/var/log/vstf-settings.log", clevel=logging.INFO)
274
275     path = '/etc/vstf'
276     setting = DefaultSettings(path)
277     filename = 'reporters.mail.mail-settings'
278     data = setting.load(filename)
279
280     setting.save(data, filename)
281     LOG.info(type(data))
282     LOG.info(data)
283
284
285 if __name__ == '__main__':
286     unit_test()