b9dfbf44ab4288153d708fbb8331736453e64c20
[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
25
26 class AdminPage(Page):
27
28     def __init__(self, *args, **kwargs):
29         super(AdminPage, self).__init__(*args, **kwargs)
30         self.default_headers.update({
31             'Cache-Control': 'no-cache, must-revalidate',
32             'Pragma': 'no-cache',
33             'Expires': 'Thu, 01 Dec 1994 16:00:00 GMT',
34         })
35         self.auth_protect = True
36
37
38 class AdminPluginConfig(AdminPage):
39
40     def __init__(self, po, site, parent):
41         super(AdminPluginConfig, self).__init__(site, form=True)
42         self._po = po
43         self.title = '%s plugin' % po.name
44         self.url = '%s/%s' % (parent.url, po.name)
45         self.facility = parent.facility
46         self.menu = [parent]
47         self.back = parent.url
48
49         # Get the defaults
50         options = po.get_config_desc()
51         if options is None:
52             options = dict()
53
54         self.options_order = []
55         if hasattr(po, 'conf_opt_order'):
56             self.options_order = po.conf_opt_order
57
58         # append any undefined options
59         add = []
60         for k in options.keys():
61             if k not in self.options_order:
62                 add.append(k)
63         if len(add):
64             add.sort()
65             for k in add:
66                 self.options_order.append(k)
67
68     def root_with_msg(self, message=None, message_type=None):
69         return self._template('admin/plugin_config.html', title=self.title,
70                               menu=self.menu, action=self.url, back=self.back,
71                               message=message, message_type=message_type,
72                               name='admin_%s_%s_form' % (self.facility,
73                                                          self._po.name),
74                               options_order=self.options_order,
75                               plugin=self._po)
76
77     @admin_protect
78     def GET(self, *args, **kwargs):
79         return self.root_with_msg()
80
81     @admin_protect
82     def POST(self, *args, **kwargs):
83
84         message = "Nothing was modified."
85         message_type = "info"
86         new_values = dict()
87
88         # Get the defaults
89         options = self._po.get_config_desc()
90         if options is None:
91             options = dict()
92
93         for key, value in kwargs.iteritems():
94             if key in options:
95                 if value != self._po.get_config_value(key):
96                     cherrypy.log.error("Storing [%s]: %s = %s" %
97                                        (self._po.name, key, value))
98                     new_values[key] = value
99
100         if len(new_values) != 0:
101             # First we try to save in the database
102             try:
103                 self._po.save_plugin_config(self.facility, new_values)
104                 message = "New configuration saved."
105                 message_type = "success"
106             except Exception:  # pylint: disable=broad-except
107                 message = "Failed to save data!"
108                 message_type = "error"
109
110             # And only if it succeeds we change the live object
111             self._po.refresh_plugin_config(self.facility)
112
113         return self.root_with_msg(message=message,
114                                   message_type=message_type)
115
116
117 class AdminPluginsOrder(AdminPage):
118
119     def __init__(self, site, parent, facility):
120         super(AdminPluginsOrder, self).__init__(site, form=True)
121         self.parent = parent
122         self.facility = facility
123         self.url = '%s/order' % parent.url
124         self.menu = [parent]
125
126     @admin_protect
127     def GET(self, *args, **kwargs):
128         return self.parent.root_with_msg()
129
130     def _get_enabled_by_name(self):
131         by_name = dict()
132         for p in self._site[self.facility]['available'].values():
133             if p.is_enabled:
134                 by_name[p.name] = p
135         return by_name
136
137     @admin_protect
138     def POST(self, *args, **kwargs):
139         message = "Nothing was modified."
140         message_type = "info"
141         by_name = self._get_enabled_by_name()
142
143         if 'order' in kwargs:
144             order = kwargs['order'].split(',')
145             if len(order) != 0:
146                 new_names = []
147                 new_plugins = []
148                 try:
149                     for v in order:
150                         val = v.strip()
151                         if val not in by_name:
152                             error = "Invalid plugin name: %s" % val
153                             raise ValueError(error)
154                         new_names.append(val)
155                         new_plugins.append(by_name[val])
156                     if len(new_names) < len(by_name):
157                         for val in by_name:
158                             if val not in new_names:
159                                 new_names.append(val)
160                                 new_plugins.append(by_name[val])
161
162                     self.parent.save_enabled_plugins(new_names)
163                     self.parent.reorder_plugins(new_names)
164
165                     # When all is saved update also live config. The
166                     # live config is a list of the actual plugin
167                     # objects.
168                     self._site[self.facility]['enabled'] = new_plugins
169
170                     message = "New configuration saved."
171                     message_type = "success"
172
173                 except ValueError, e:
174                     message = str(e)
175                     message_type = "error"
176
177                 except Exception, e:  # pylint: disable=broad-except
178                     message = "Failed to save data!"
179                     message_type = "error"
180
181         return self.parent.root_with_msg(message=message,
182                                          message_type=message_type)
183
184
185 class AdminPlugins(AdminPage):
186     def __init__(self, name, site, parent, facility, ordered=True):
187         super(AdminPlugins, self).__init__(site)
188         self._master = parent
189         self.name = name
190         self.title = '%s plugins' % name
191         self.url = '%s/%s' % (parent.url, name)
192         self.facility = facility
193         self.template = 'admin/plugins.html'
194         self.order = None
195         parent.add_subtree(name, self)
196
197         for plugin in self._site[facility]['available']:
198             cherrypy.log.error('Admin info plugin: %s' % plugin)
199             obj = self._site[facility]['available'][plugin]
200             page = AdminPluginConfig(obj, self._site, self)
201             if hasattr(obj, 'admin'):
202                 obj.admin.mount(page)
203             self.add_subtree(plugin, page)
204
205         if ordered:
206             self.order = AdminPluginsOrder(self._site, self, facility)
207
208     def save_enabled_plugins(self, names):
209         po = PluginObject()
210         po.name = "global"
211         globalconf = dict()
212         globalconf['order'] = ','.join(names)
213         po.set_config(globalconf)
214         po.save_plugin_config(self.facility)
215
216     def reorder_plugins(self, names):
217         return
218
219     def root_with_msg(self, message=None, message_type=None):
220         plugins = self._site[self.facility]
221         enabled = []
222         if self.order:
223             for plugin in plugins['enabled']:
224                 if plugin.is_enabled:
225                     enabled.append(plugin.name)
226         else:
227             for _, plugin in plugins['available'].iteritems():
228                 if plugin.is_enabled:
229                     enabled.append(plugin.name)
230
231         targs = {'title': self.title,
232                  'menu': self._master.menu,
233                  'message': message,
234                  'message_type': message_type,
235                  'available': plugins['available'],
236                  'enabled': enabled,
237                  'baseurl': self.url}
238         if self.order:
239             targs['order_name'] = '%s_order_form' % self.name
240             targs['order_action'] = self.order.url
241
242         # pylint: disable=star-args
243         return self._template(self.template, **targs)
244
245     def root(self, *args, **kwargs):
246         return self.root_with_msg()
247
248     @admin_protect
249     def enable(self, plugin):
250         msg = None
251         plugins = self._site[self.facility]
252         if plugin not in plugins['available']:
253             msg = "Unknown plugin %s" % plugin
254             return self.root_with_msg(msg, "error")
255         obj = plugins['available'][plugin]
256         if not obj.is_enabled:
257             obj.enable(self._site)
258             if self.order:
259                 enabled = list(x.name for x in plugins['enabled'])
260                 self.save_enabled_plugins(enabled)
261             msg = "Plugin %s enabled" % obj.name
262         return self.root_with_msg(msg, "success")
263     enable.public_function = True
264
265     @admin_protect
266     def disable(self, plugin):
267         msg = None
268         plugins = self._site[self.facility]
269         if plugin not in plugins['available']:
270             msg = "Unknown plugin %s" % plugin
271             return self.root_with_msg(msg, "error")
272         obj = plugins['available'][plugin]
273         if obj.is_enabled:
274             obj.disable(self._site)
275             if self.order:
276                 enabled = list(x.name for x in plugins['enabled'])
277                 self.save_enabled_plugins(enabled)
278             msg = "Plugin %s disabled" % obj.name
279         return self.root_with_msg(msg, "success")
280     disable.public_function = True
281
282
283 class Admin(AdminPage):
284
285     def __init__(self, site, mount):
286         super(Admin, self).__init__(site)
287         self.title = 'Home'
288         self.mount = mount
289         self.url = '%s/%s' % (self.basepath, mount)
290         self.menu = [self]
291
292     def root(self, *args, **kwargs):
293         return self._template('admin/index.html',
294                               title='Configuration',
295                               baseurl=self.url,
296                               menu=self.menu)
297
298     def add_subtree(self, name, page):
299         self.__dict__[name] = page
300         self.menu.append(page)
301
302     def del_subtree(self, name):
303         self.menu.remove(self.__dict__[name])
304         del self.__dict__[name]
305
306     def get_menu_urls(self):
307         urls = dict()
308         for item in self.menu:
309             name = getattr(item, 'name', None)
310             if name:
311                 urls['%s_url' % name] = cherrypy.url('/%s/%s' % (self.mount,
312                                                                  name))
313         return urls
314
315     @admin_protect
316     def scheme(self):
317         cherrypy.response.headers.update({'Content-Type': 'image/svg+xml'})
318         urls = self.get_menu_urls()
319         # pylint: disable=star-args
320         return self._template('admin/ipsilon-scheme.svg', **urls)
321     scheme.public_function = True