Use python logging in install / log cherrypy at right severity
[cascardo/ipsilon.git] / ipsilon / admin / common.py
old mode 100755 (executable)
new mode 100644 (file)
index 2b83314..87bfcd5
@@ -1,5 +1,3 @@
-#!/usr/bin/python
-#
 # Copyright (C) 2014  Simo Sorce <simo@redhat.com>
 #
 # see file 'COPYING' for use and warranty information
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import cherrypy
+import logging
 from ipsilon.util.page import Page
 from ipsilon.util.page import admin_protect
+from ipsilon.util.endpoint import allow_iframe
 from ipsilon.util import config as pconfig
 
 
@@ -41,11 +41,6 @@ class AdminPage(Page):
 
     def __init__(self, *args, **kwargs):
         super(AdminPage, self).__init__(*args, **kwargs)
-        self.default_headers.update({
-            'Cache-Control': 'no-cache, must-revalidate',
-            'Pragma': 'no-cache',
-            'Expires': 'Thu, 01 Dec 1994 16:00:00 GMT',
-        })
         self.auth_protect = True
 
 
@@ -61,7 +56,7 @@ class AdminPluginConfig(AdminPage):
         self.back = parent.url
 
     def root_with_msg(self, message=None, message_type=None):
-        return self._template('admin/plugin_config.html', title=self.title,
+        return self._template('admin/option_config.html', title=self.title,
                               menu=self.menu, action=self.url, back=self.back,
                               message=message, message_type=message_type,
                               name='admin_%s_%s_form' % (self.facility,
@@ -102,12 +97,25 @@ class AdminPluginConfig(AdminPage):
                         aname = '%s_%s' % (name, a)
                         if aname in kwargs:
                             value.append(a)
+                elif type(option) is pconfig.ComplexList:
+                    value = get_complex_list_value(name,
+                                                   option.get_value(),
+                                                   **kwargs)
+                    if value is None:
+                        continue
+                elif type(option) is pconfig.MappingList:
+                    value = get_mapping_list_value(name,
+                                                   option.get_value(),
+                                                   **kwargs)
+                    if value is None:
+                        continue
                 else:
                     continue
 
             if value != option.get_value():
                 cherrypy.log.error("Storing [%s]: %s = %s" %
-                                   (self._po.name, name, value))
+                                   (self._po.name, name, value),
+                                   severity=logging.DEBUG)
             option.set_value(value)
             new_db_values[name] = option.export_value()
 
@@ -117,7 +125,8 @@ class AdminPluginConfig(AdminPage):
                 self._po.save_plugin_config(new_db_values)
                 message = "New configuration saved."
                 message_type = ADMIN_STATUS_OK
-            except Exception:  # pylint: disable=broad-except
+            except Exception as e:  # pylint: disable=broad-except
+                self.error('Failed to save data: %s' % e)
                 message = "Failed to save data!"
                 message_type = ADMIN_STATUS_ERROR
 
@@ -189,7 +198,8 @@ class AdminPluginsOrder(AdminPage):
                     message = str(e)
                     message_type = ADMIN_STATUS_ERROR
 
-                except Exception, e:  # pylint: disable=broad-except
+                except Exception as e:  # pylint: disable=broad-except
+                    self.error('Failed to save data: %s' % e)
                     message = "Failed to save data!"
                     message_type = ADMIN_STATUS_ERROR
 
@@ -210,8 +220,12 @@ class AdminPlugins(AdminPage):
         self.order = None
         parent.add_subtree(name, self)
 
+        if self._site[facility] is None:
+            return
+
         for plugin in self._site[facility].available:
-            cherrypy.log.error('Admin info plugin: %s' % plugin)
+            cherrypy.log.error('Admin info plugin: %s' % plugin,
+                               severity=logging.DEBUG)
             obj = self._site[facility].available[plugin]
             page = AdminPluginConfig(obj, self._site, self)
             if hasattr(obj, 'admin'):
@@ -271,7 +285,10 @@ class AdminPlugins(AdminPage):
         except AdminError, e:
             return self.root_with_msg(str(e), ADMIN_STATUS_WARN)
         if not obj.is_enabled:
-            obj.enable()
+            try:
+                obj.enable()
+            except Exception as e:  # pylint: disable=broad-except
+                return self.root_with_msg(str(e), ADMIN_STATUS_WARN)
             obj.save_enabled_state()
             msg = "Plugin %s enabled" % obj.name
         return self.root_with_msg(msg, ADMIN_STATUS_OK,
@@ -286,7 +303,10 @@ class AdminPlugins(AdminPage):
         except AdminError, e:
             return self.root_with_msg(str(e), ADMIN_STATUS_WARN)
         if obj.is_enabled:
-            obj.disable()
+            try:
+                obj.disable()
+            except Exception as e:  # pylint: disable=broad-except
+                return self.root_with_msg(str(e), ADMIN_STATUS_WARN)
             obj.save_enabled_state()
             msg = "Plugin %s disabled" % obj.name
         return self.root_with_msg(msg, ADMIN_STATUS_OK,
@@ -327,9 +347,152 @@ class Admin(AdminPage):
         return urls
 
     @admin_protect
+    @allow_iframe
     def scheme(self):
         cherrypy.response.headers.update({'Content-Type': 'image/svg+xml'})
         urls = self.get_menu_urls()
         # pylint: disable=star-args
-        return self._template('admin/ipsilon-scheme.svg', **urls)
+        return str(self._template('admin/ipsilon-scheme.svg', **urls))
     scheme.public_function = True
+
+
+def get_complex_list_value(name, old_value, **kwargs):
+    delete = list()
+    change = dict()
+    for key, val in kwargs.iteritems():
+        if not key.startswith(name):
+            continue
+        n = key[len(name):]
+        if len(n) == 0 or n[0] != ' ':
+            continue
+        try:
+            index, field = n[1:].split('-')
+        except ValueError:
+            continue
+        if field == 'delete':
+            delete.append(int(index))
+        elif field == 'name':
+            change[int(index)] = val
+
+    if len(delete) == 0 and len(change) == 0:
+        return None
+
+    value = old_value
+
+    # remove unwanted changes
+    for i in delete:
+        if i in change:
+            del change[i]
+
+    # perform requested changes
+    for index, val in change.iteritems():
+        val_list = val.split('/')
+        stripped = list()
+        for v in val_list:
+            stripped.append(v.strip())
+        if len(stripped) == 1:
+            stripped = stripped[0]
+        if len(value) <= index:
+            value.extend([None]*(index + 1 - len(value)))
+        value[index] = stripped
+
+        if len(value[index]) == 0:
+            value[index] = None
+
+    # the previous loop may add 'None' entries
+    # if any still exists mark them to be deleted
+    for i in xrange(0, len(value)):
+        if value[i] is None:
+            delete.append(i)
+
+    # remove duplicates and set in reverse order
+    delete = list(set(delete))
+    delete.sort(reverse=True)
+
+    for i in delete:
+        if len(value) > i:
+            del value[i]
+
+    if len(value) == 0:
+        value = None
+    return value
+
+
+def get_mapping_list_value(name, old_value, **kwargs):
+    delete = list()
+    change = dict()
+    for key, val in kwargs.iteritems():
+        if not key.startswith(name):
+            continue
+        n = key[len(name):]
+        if len(n) == 0 or n[0] != ' ':
+            continue
+        try:
+            index, field = n[1:].split('-')
+        except ValueError:
+            continue
+        if field == 'delete':
+            delete.append(int(index))
+        else:
+            i = int(index)
+            if i not in change:
+                change[i] = dict()
+            change[i][field] = val
+
+    if len(delete) == 0 and len(change) == 0:
+        return None
+
+    value = old_value
+
+    # remove unwanted changes
+    for i in delete:
+        if i in change:
+            del change[i]
+
+    # perform requested changes
+    for index, fields in change.iteritems():
+        for k in 'from', 'to':
+            if k in fields:
+                val = fields[k]
+                val_list = val.split('/')
+                stripped = list()
+                for v in val_list:
+                    stripped.append(v.strip())
+                if len(stripped) == 1:
+                    stripped = stripped[0]
+                if len(value) <= index:
+                    value.extend([None]*(index + 1 - len(value)))
+                if value[index] is None:
+                    value[index] = [None, None]
+                if k == 'from':
+                    i = 0
+                else:
+                    i = 1
+                value[index][i] = stripped
+
+        # eliminate incomplete/incorrect entries
+        if value[index] is not None:
+            if ((len(value[index]) != 2 or
+                 value[index][0] is None or
+                 len(value[index][0]) == 0 or
+                 value[index][1] is None or
+                 len(value[index][1]) == 0)):
+                value[index] = None
+
+    # the previous loop may add 'None' entries
+    # if any still exists mark them to be deleted
+    for i in xrange(0, len(value)):
+        if value[i] is None:
+            delete.append(i)
+
+    # remove duplicates and set in reverse order
+    delete = list(set(delete))
+    delete.sort(reverse=True)
+
+    for i in delete:
+        if len(value) > i:
+            del value[i]
+
+    if len(value) == 0:
+        value = None
+    return value