Refactor plugin configuration
[cascardo/ipsilon.git] / ipsilon / admin / common.py
1 #!/usr/bin/python
2 #
3 # Copyright (C) 2014  Simo Sorce <simo@redhat.com>
4 #
5 # see file 'COPYING' for use and warranty information
6 #
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20 import cherrypy
21 from ipsilon.util.page import Page
22 from ipsilon.util.page import admin_protect
23 from ipsilon.util.plugin import PluginObject
24 from ipsilon.util import config as pconfig
25
26
27 class AdminPage(Page):
28
29     def __init__(self, *args, **kwargs):
30         super(AdminPage, self).__init__(*args, **kwargs)
31         self.default_headers.update({
32             'Cache-Control': 'no-cache, must-revalidate',
33             'Pragma': 'no-cache',
34             'Expires': 'Thu, 01 Dec 1994 16:00:00 GMT',
35         })
36         self.auth_protect = True
37
38
39 class AdminPluginConfig(AdminPage):
40
41     def __init__(self, po, site, parent):
42         super(AdminPluginConfig, self).__init__(site, form=True)
43         self._po = po
44         self.title = '%s plugin' % po.name
45         self.url = '%s/%s' % (parent.url, po.name)
46         self.facility = parent.facility
47         self.menu = [parent]
48         self.back = parent.url
49
50     def root_with_msg(self, message=None, message_type=None):
51         return self._template('admin/plugin_config.html', title=self.title,
52                               menu=self.menu, action=self.url, back=self.back,
53                               message=message, message_type=message_type,
54                               name='admin_%s_%s_form' % (self.facility,
55                                                          self._po.name),
56                               config=self._po.get_config_obj())
57
58     @admin_protect
59     def GET(self, *args, **kwargs):
60         return self.root_with_msg()
61
62     @admin_protect
63     def POST(self, *args, **kwargs):
64
65         message = "Nothing was modified."
66         message_type = "info"
67         new_db_values = dict()
68
69         conf = self._po.get_config_obj()
70
71         for name, option in conf.iteritems():
72             if name in kwargs:
73                 value = kwargs[name]
74                 if isinstance(option, pconfig.List):
75                     value = [x.strip() for x in value.split('\n')]
76                 elif isinstance(option, pconfig.Condition):
77                     value = True
78             else:
79                 if isinstance(option, pconfig.Condition):
80                     value = False
81                 elif isinstance(option, pconfig.Choice):
82                     value = list()
83                     for a in option.get_allowed():
84                         aname = '%s_%s' % (name, a)
85                         if aname in kwargs:
86                             value.append(a)
87                 else:
88                     continue
89
90             if value != option.get_value():
91                 cherrypy.log.error("Storing [%s]: %s = %s" %
92                                    (self._po.name, name, value))
93             option.set_value(value)
94             new_db_values[name] = option.export_value()
95
96         if len(new_db_values) != 0:
97             # First we try to save in the database
98             try:
99                 self._po.save_plugin_config(self.facility, new_db_values)
100                 message = "New configuration saved."
101                 message_type = "success"
102             except Exception:  # pylint: disable=broad-except
103                 message = "Failed to save data!"
104                 message_type = "error"
105
106             # Then refresh the actual objects
107             self._po.refresh_plugin_config(self.facility)
108
109         return self.root_with_msg(message=message,
110                                   message_type=message_type)
111
112
113 class AdminPluginsOrder(AdminPage):
114
115     def __init__(self, site, parent, facility):
116         super(AdminPluginsOrder, self).__init__(site, form=True)
117         self.parent = parent
118         self.facility = facility
119         self.url = '%s/order' % parent.url
120         self.menu = [parent]
121
122     @admin_protect
123     def GET(self, *args, **kwargs):
124         return self.parent.root_with_msg()
125
126     def _get_enabled_by_name(self):
127         by_name = dict()
128         for p in self._site[self.facility]['available'].values():
129             if p.is_enabled:
130                 by_name[p.name] = p
131         return by_name
132
133     @admin_protect
134     def POST(self, *args, **kwargs):
135         message = "Nothing was modified."
136         message_type = "info"
137         by_name = self._get_enabled_by_name()
138
139         if 'order' in kwargs:
140             order = kwargs['order'].split(',')
141             if len(order) != 0:
142                 new_names = []
143                 new_plugins = []
144                 try:
145                     for v in order:
146                         val = v.strip()
147                         if val not in by_name:
148                             error = "Invalid plugin name: %s" % val
149                             raise ValueError(error)
150                         new_names.append(val)
151                         new_plugins.append(by_name[val])
152                     if len(new_names) < len(by_name):
153                         for val in by_name:
154                             if val not in new_names:
155                                 new_names.append(val)
156                                 new_plugins.append(by_name[val])
157
158                     self.parent.save_enabled_plugins(new_names)
159                     self.parent.reorder_plugins(new_names)
160
161                     # When all is saved update also live config. The
162                     # live config is a list of the actual plugin
163                     # objects.
164                     self._site[self.facility]['enabled'] = new_plugins
165
166                     message = "New configuration saved."
167                     message_type = "success"
168
169                 except ValueError, e:
170                     message = str(e)
171                     message_type = "error"
172
173                 except Exception, e:  # pylint: disable=broad-except
174                     message = "Failed to save data!"
175                     message_type = "error"
176
177         return self.parent.root_with_msg(message=message,
178                                          message_type=message_type)
179
180
181 class AdminPlugins(AdminPage):
182     def __init__(self, name, site, parent, facility, ordered=True):
183         super(AdminPlugins, self).__init__(site)
184         self._master = parent
185         self.name = name
186         self.title = '%s plugins' % name
187         self.url = '%s/%s' % (parent.url, name)
188         self.facility = facility
189         self.template = 'admin/plugins.html'
190         self.order = None
191         parent.add_subtree(name, self)
192
193         for plugin in self._site[facility]['available']:
194             cherrypy.log.error('Admin info plugin: %s' % plugin)
195             obj = self._site[facility]['available'][plugin]
196             page = AdminPluginConfig(obj, self._site, self)
197             if hasattr(obj, 'admin'):
198                 obj.admin.mount(page)
199             self.add_subtree(plugin, page)
200
201         if ordered:
202             self.order = AdminPluginsOrder(self._site, self, facility)
203
204     def save_enabled_plugins(self, names):
205         po = PluginObject()
206         po.name = "global"
207         globalconf = dict()
208         globalconf['order'] = ','.join(names)
209         po.import_config(globalconf)
210         po.save_plugin_config(self.facility)
211
212     def reorder_plugins(self, names):
213         return
214
215     def root_with_msg(self, message=None, message_type=None):
216         plugins = self._site[self.facility]
217         enabled = []
218         if self.order:
219             for plugin in plugins['enabled']:
220                 if plugin.is_enabled:
221                     enabled.append(plugin.name)
222         else:
223             for _, plugin in plugins['available'].iteritems():
224                 if plugin.is_enabled:
225                     enabled.append(plugin.name)
226
227         targs = {'title': self.title,
228                  'menu': self._master.menu,
229                  'message': message,
230                  'message_type': message_type,
231                  'available': plugins['available'],
232                  'enabled': enabled,
233                  'baseurl': self.url}
234         if self.order:
235             targs['order_name'] = '%s_order_form' % self.name
236             targs['order_action'] = self.order.url
237
238         # pylint: disable=star-args
239         return self._template(self.template, **targs)
240
241     def root(self, *args, **kwargs):
242         return self.root_with_msg()
243
244     @admin_protect
245     def enable(self, plugin):
246         msg = None
247         plugins = self._site[self.facility]
248         if plugin not in plugins['available']:
249             msg = "Unknown plugin %s" % plugin
250             return self.root_with_msg(msg, "error")
251         obj = plugins['available'][plugin]
252         if not obj.is_enabled:
253             obj.enable(self._site)
254             if self.order:
255                 enabled = list(x.name for x in plugins['enabled'])
256                 self.save_enabled_plugins(enabled)
257             msg = "Plugin %s enabled" % obj.name
258         return self.root_with_msg(msg, "success")
259     enable.public_function = True
260
261     @admin_protect
262     def disable(self, plugin):
263         msg = None
264         plugins = self._site[self.facility]
265         if plugin not in plugins['available']:
266             msg = "Unknown plugin %s" % plugin
267             return self.root_with_msg(msg, "error")
268         obj = plugins['available'][plugin]
269         if obj.is_enabled:
270             obj.disable(self._site)
271             if self.order:
272                 enabled = list(x.name for x in plugins['enabled'])
273                 self.save_enabled_plugins(enabled)
274             msg = "Plugin %s disabled" % obj.name
275         return self.root_with_msg(msg, "success")
276     disable.public_function = True
277
278
279 class Admin(AdminPage):
280
281     def __init__(self, site, mount):
282         super(Admin, self).__init__(site)
283         self.title = 'Home'
284         self.mount = mount
285         self.url = '%s/%s' % (self.basepath, mount)
286         self.menu = [self]
287
288     def root(self, *args, **kwargs):
289         return self._template('admin/index.html',
290                               title='Configuration',
291                               baseurl=self.url,
292                               menu=self.menu)
293
294     def add_subtree(self, name, page):
295         self.__dict__[name] = page
296         self.menu.append(page)
297
298     def del_subtree(self, name):
299         self.menu.remove(self.__dict__[name])
300         del self.__dict__[name]
301
302     def get_menu_urls(self):
303         urls = dict()
304         for item in self.menu:
305             name = getattr(item, 'name', None)
306             if name:
307                 urls['%s_url' % name] = cherrypy.url('/%s/%s' % (self.mount,
308                                                                  name))
309         return urls
310
311     @admin_protect
312     def scheme(self):
313         cherrypy.response.headers.update({'Content-Type': 'image/svg+xml'})
314         urls = self.get_menu_urls()
315         # pylint: disable=star-args
316         return self._template('admin/ipsilon-scheme.svg', **urls)
317     scheme.public_function = True