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