From 00358434594bf5621f62235c2ef7ac3d1d21d80e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20K=C3=A1cha?= <ph@cesnet.cz>
Date: Mon, 12 Sep 2016 13:38:59 +0200
Subject: [PATCH] Refactored configuration for outside world usage (also
 simplified and got rid of ugly globals())

---
 warden3/warden_server/warden_server.py | 211 ++++++++++++++-----------
 1 file changed, 115 insertions(+), 96 deletions(-)

diff --git a/warden3/warden_server/warden_server.py b/warden3/warden_server/warden_server.py
index 736a437..3d30fce 100755
--- a/warden3/warden_server/warden_server.py
+++ b/warden3/warden_server/warden_server.py
@@ -1193,7 +1193,95 @@ def fallback_wsgi(environ, start_response, exc_info=None):
     return [output]
 
 
-def build_server(conf):
+# Order in which the base objects must get initialized
+section_order = ("log", "db", "auth", "validator", "handler", "server")
+
+# List of sections and objects, configured by them
+# First object in each object list is the default one, otherwise
+# "type" keyword in section may be used to choose other
+section_def = {
+    "log": [FileLogger, SysLogger],
+    "db": [MySQL],
+    "auth": [X509Authenticator, PlainAuthenticator, X509NameAuthenticator, X509MixMatchAuthenticator],
+    "validator": [JSONSchemaValidator, NoValidator],
+    "handler": [WardenHandler],
+    "server": [Server]
+}
+
+# Object parameter conversions and defaults
+param_def = {
+    FileLogger: {
+        "req": {"type": "obj", "default": "req"},
+        "filename": {"type": "filepath", "default": path.join(path.dirname(__file__), path.splitext(path.split(__file__)[1])[0] + ".log")},
+        "level": {"type": "loglevel", "default": "info"},
+    },
+    SysLogger: {
+        "req": {"type": "obj", "default": "req"},
+        "socket": {"type": "filepath", "default": "/dev/log"},
+        "facility": {"type": "facility", "default": "daemon"},
+        "level": {"type": "loglevel", "default": "info"}
+    },
+    PlainAuthenticator: {
+        "req": {"type": "obj", "default": "req"},
+        "log": {"type": "obj", "default": "log"},
+        "db": {"type": "obj", "default": "db"}
+    },
+    X509Authenticator: {
+        "req": {"type": "obj", "default": "req"},
+        "log": {"type": "obj", "default": "log"},
+        "db": {"type": "obj", "default": "db"}
+    },
+    X509NameAuthenticator: {
+        "req": {"type": "obj", "default": "req"},
+        "log": {"type": "obj", "default": "log"},
+        "db": {"type": "obj", "default": "db"}
+    },
+    NoValidator: {
+        "req": {"type": "obj", "default": "req"},
+        "log": {"type": "obj", "default": "log"},
+    },
+    JSONSchemaValidator: {
+        "req": {"type": "obj", "default": "req"},
+        "log": {"type": "obj", "default": "log"},
+        "filename": {"type": "filepath", "default": path.join(path.dirname(__file__), "idea.schema")}
+    },
+    MySQL: {
+        "req": {"type": "obj", "default": "req"},
+        "log": {"type": "obj", "default": "log"},
+        "host": {"type": "str", "default": "localhost"},
+        "user": {"type": "str", "default": "warden"},
+        "password": {"type": "str", "default": ""},
+        "dbname": {"type": "str", "default": "warden3"},
+        "port": {"type": "natural", "default": 3306},
+        "retry_pause": {"type": "natural", "default": 5},
+        "retry_count": {"type": "natural", "default": 3},
+        "event_size_limit": {"type": "natural", "default": 5*1024*1024},
+        "catmap_filename": {"type": "filepath", "default": path.join(path.dirname(__file__), "catmap_mysql.json")},
+        "tagmap_filename": {"type": "filepath", "default": path.join(path.dirname(__file__), "tagmap_mysql.json")}
+    },
+    WardenHandler: {
+        "req": {"type": "obj", "default": "req"},
+        "log": {"type": "obj", "default": "log"},
+        "validator": {"type": "obj", "default": "validator"},
+        "db": {"type": "obj", "default": "DB"},
+        "auth": {"type": "obj", "default": "auth"},
+        "send_events_limit": {"type": "natural", "default": 500},
+        "get_events_limit": {"type": "natural", "default": 1000},
+        "description": {"type": "str", "default": ""}
+    },
+    Server: {
+        "req": {"type": "obj", "default": "req"},
+        "log": {"type": "obj", "default": "log"},
+        "auth": {"type": "obj", "default": "auth"},
+        "handler": {"type": "obj", "default": "handler"}
+    }
+}
+
+
+def build_server(conf, section_order=section_order, section_def=section_def, param_def=param_def):
+
+    objects = {}    # Already initialized objects
+
 
     # Functions for validation and conversion of config values
     def facility(name):
@@ -1212,93 +1300,20 @@ def build_server(conf):
         # Make paths relative to dir of this script
         return path.join(path.dirname(__file__), name)
 
-    def objdef(name):
+    def obj(name):
         return objects[name.lower()]
 
-    obj = objdef    # Draw into local namespace for init_obj
 
-    objects = {}    # Already initialized objects
-
-    # List of sections and objects, configured by them
-    # First object in each object list is the default one, otherwise
-    # "type" keyword in section may be used to choose other
-    section_def = {
-        "log": ["FileLogger", "SysLogger"],
-        "db": ["MySQL"],
-        "auth": ["X509Authenticator", "PlainAuthenticator", "X509NameAuthenticator", "X509MixMatchAuthenticator"],
-        "validator": ["JSONSchemaValidator", "NoValidator"],
-        "handler": ["WardenHandler"],
-        "server": ["Server"]
+    # Typedef dictionary
+    conv_dict = {
+        "facility": facility,
+        "loglevel": loglevel,
+        "natural": natural,
+        "filepath": filepath,
+        "obj": obj,
+        "str": str
     }
 
-    # Object parameter conversions and defaults
-    param_def = {
-        "FileLogger": {
-            "req": {"type": obj, "default": "req"},
-            "filename": {"type": filepath, "default": path.join(path.dirname(__file__), path.splitext(path.split(__file__)[1])[0] + ".log")},
-            "level": {"type": loglevel, "default": "info"},
-        },
-        "SysLogger": {
-            "req": {"type": obj, "default": "req"},
-            "socket": {"type": filepath, "default": "/dev/log"},
-            "facility": {"type": facility, "default": "daemon"},
-            "level": {"type": loglevel, "default": "info"}
-        },
-        "PlainAuthenticator": {
-            "req": {"type": obj, "default": "req"},
-            "log": {"type": obj, "default": "log"},
-            "db": {"type": obj, "default": "db"}
-        },
-        "X509Authenticator": {
-            "req": {"type": obj, "default": "req"},
-            "log": {"type": obj, "default": "log"},
-            "db": {"type": obj, "default": "db"}
-        },
-        "X509NameAuthenticator": {
-            "req": {"type": obj, "default": "req"},
-            "log": {"type": obj, "default": "log"},
-            "db": {"type": obj, "default": "db"}
-        },
-        "NoValidator": {
-            "req": {"type": obj, "default": "req"},
-            "log": {"type": obj, "default": "log"},
-        },
-        "JSONSchemaValidator": {
-            "req": {"type": obj, "default": "req"},
-            "log": {"type": obj, "default": "log"},
-            "filename": {"type": filepath, "default": path.join(path.dirname(__file__), "idea.schema")}
-        },
-        "MySQL": {
-            "req": {"type": obj, "default": "req"},
-            "log": {"type": obj, "default": "log"},
-            "host": {"type": str, "default": "localhost"},
-            "user": {"type": str, "default": "warden"},
-            "password": {"type": str, "default": ""},
-            "dbname": {"type": str, "default": "warden3"},
-            "port": {"type": natural, "default": 3306},
-            "retry_pause": {"type": natural, "default": 5},
-            "retry_count": {"type": natural, "default": 3},
-            "event_size_limit": {"type": natural, "default": 5*1024*1024},
-            "catmap_filename": {"type": filepath, "default": path.join(path.dirname(__file__), "catmap_mysql.json")},
-            "tagmap_filename": {"type": filepath, "default": path.join(path.dirname(__file__), "tagmap_mysql.json")}
-        },
-        "WardenHandler": {
-            "req": {"type": obj, "default": "req"},
-            "log": {"type": obj, "default": "log"},
-            "validator": {"type": obj, "default": "validator"},
-            "db": {"type": obj, "default": "DB"},
-            "auth": {"type": obj, "default": "auth"},
-            "send_events_limit": {"type": natural, "default": 500},
-            "get_events_limit": {"type": natural, "default": 1000},
-            "description": {"type": str, "default": ""}
-        },
-        "Server": {
-            "req": {"type": obj, "default": "req"},
-            "log": {"type": obj, "default": "log"},
-            "auth": {"type": obj, "default": "auth"},
-            "handler": {"type": obj, "default": "handler"}
-        }
-    }
 
     def init_obj(sect_name):
         config = conf.get(sect_name, {})
@@ -1309,17 +1324,21 @@ def build_server(conf):
             objtype = config["type"]
             del config["type"]
         except KeyError:    # No, fetch default object type for this section
-            objtype = sect_def[0]
-        else:
-            if not objtype in sect_def:
+            cls = sect_def[0]
+        else:	# Yes, get corresponding class/callable
+            names = [o.__name__ for o in sect_def]
+            try:
+                idx = names.index(objtype)
+            except ValueError:
                 raise KeyError("Unknown type %s in section %s" % (objtype, sect_name))
+            cls = sect_def[idx]
 
-        params = param_def[objtype]
+        params = param_def[cls]
 
         # No surplus parameters? Disallow also 'obj' attributes, these are only
         # to provide default referenced section
         for name in config:
-            if name not in params or (name in params and params[name]["type"] is objdef):
+            if name not in params or (name in params and params[name]["type"] == "obj"):
                 raise KeyError("Unknown key %s in section %s" % (name, sect_name))
 
         # Process parameters
@@ -1327,24 +1346,24 @@ def build_server(conf):
         for name, definition in params.iteritems():
             raw_val = config.get(name, definition["default"])
             try:
-                val = definition["type"](raw_val)
+                type_callable = conv_dict[definition["type"]]
+                val = type_callable(raw_val)
             except Exception:
                 raise KeyError("Bad value \"%s\" for %s in section %s" % (raw_val, name, sect_name))
             kwargs[name] = val
 
-        cls = globals()[objtype]   # get class/function type
         try:
-            obj = cls(**kwargs)         # run it
+            obj_inst = cls(**kwargs)         # run it
         except Exception as e:
             raise KeyError("Cannot initialize %s from section %s: %s" % (
                 objtype, sect_name, str(e)))
 
-        if isinstance(obj, Object):
+        if isinstance(obj_inst, Object):
             # Log only objects here, functions must take care of themselves
-            logging.getLogger(__name__).info("Initialized %s" % str(obj))
+            logging.getLogger(__name__).info("Initialized %s" % str(obj_inst))
 
-        objects[sect_name] = obj
-        return obj
+        objects[sect_name] = obj_inst
+        return obj_inst
 
     # Init logging with at least simple stderr StreamLogger
     # Dunno if it's ok within wsgi, but we have no other choice, let's
@@ -1356,7 +1375,7 @@ def build_server(conf):
 
     try:
         # Now try to init required objects
-        for o in ("log", "db", "auth", "validator", "handler", "server"):
+        for o in section_order:
             init_obj(o)
     except Exception as e:
         logging.getLogger(__name__).critical(str(e))
-- 
GitLab