Add simple SqlSession implementation
authorSimo Sorce <simo@redhat.com>
Mon, 10 Nov 2014 19:57:53 +0000 (14:57 -0500)
committerPatrick Uiterwijk <puiterwijk@redhat.com>
Wed, 12 Nov 2014 22:46:52 +0000 (23:46 +0100)
This allows us to store session data in the DB. This way session data can
be shared by multiple servers behind a balancer.

Signed-off-by: Simo Sorce <simo@redhat.com>
Reviewed-by: Patrick Uiterwijk <puiterwijk@redhat.com>
ipsilon/install/ipsilon-server-install
ipsilon/ipsilon
ipsilon/util/sessions.py [new file with mode: 0755]
quickrun.py
templates/install/ipsilon.conf
tests/helpers/common.py
tests/pgdb.py

index c6d656d..df2a965 100755 (executable)
@@ -112,6 +112,18 @@ def install(plugins, args):
                     'datadir': args['data_dir'], 'dbname': 'transactions'},
                 'secure': "False" if args['secure'] == "no" else "True",
                 'debugging': "True" if args['server_debugging'] else "False"}
+    # Testing database sessions
+    if 'session_type' in args:
+        confopts['sesstype'] = args['session_type']
+    else:
+        confopts['sesstype'] = 'file'
+    if 'session_dburi' in args:
+        confopts['sessopt'] = 'dburi'
+        confopts['sessval'] = args['session_dburi']
+    else:
+        confopts['sessopt'] = 'path'
+        confopts['sessval'] = os.path.join(args['data_dir'], 'sessions')
+    # Whetehr to disable security (for testing)
     if args['secure'] == 'no':
         confopts['secure'] = "False"
         confopts['sslrequiressl'] = ""
index 681600d..094a09d 100755 (executable)
@@ -27,6 +27,7 @@ from ipsilon.util.data import AdminStore
 from ipsilon.util import page
 from ipsilon.root import Root
 from jinja2 import Environment, FileSystemLoader
+import ipsilon.util.sessions
 
 
 def nuke_session_locks():
@@ -51,6 +52,7 @@ elif os.path.isfile('/etc/ipsilon/ipsilon.conf'):
 else:
     raise IOError("Configuration file not found")
 
+cherrypy.lib.sessions.SqlSession = ipsilon.util.sessions.SqlSession
 cherrypy.config.update(cfgfile)
 
 nuke_session_locks()
diff --git a/ipsilon/util/sessions.py b/ipsilon/util/sessions.py
new file mode 100755 (executable)
index 0000000..b724471
--- /dev/null
@@ -0,0 +1,74 @@
+#!/usr/bin/python
+#
+# Copyright (C) 2014  Ipsilon project Contributors, for licensee see COPYING
+
+import base64
+from cherrypy.lib.sessions import Session
+from ipsilon.util.data import SqlStore, SqlQuery
+import threading
+try:
+    import cPickle as pickle
+except ImportError:
+    import pickle
+
+
+SESSION_COLUMNS = ['id', 'data', 'expiration_time']
+
+
+class SqlSession(Session):
+
+    dburi = None
+    _db = None
+    _proto = 2
+    locks = {}
+
+    @classmethod
+    def setup(cls, **kwargs):
+        """Initialization from cherrypy"""
+
+        for k, v in kwargs.items():
+            if k == 'storage_dburi':
+                cls.dburi = v
+
+        cls._db = SqlStore(cls.dburi)
+
+    def _exists(self):
+        q = SqlQuery(self._db, 'sessions', SESSION_COLUMNS)
+        result = q.select({'id': self.id})
+        return True if result.fetchone() else False
+
+    def _load(self):
+        q = SqlQuery(self._db, 'sessions', SESSION_COLUMNS)
+        result = q.select({'id': self.id})
+        r = result.fetchone()
+        if r:
+            data = str(base64.b64decode(r[1]))
+            return pickle.loads(data)
+
+    def _save(self, expiration_time):
+        q = None
+        try:
+            q = SqlQuery(self._db, 'sessions', SESSION_COLUMNS, trans=True)
+            q.delete({'id': self.id})
+            data = pickle.dumps((self._data, expiration_time), self._proto)
+            q.insert((self.id, base64.b64encode(data), expiration_time))
+            q.commit()
+        except Exception:  # pylint: disable=broad-except
+            if q:
+                q.rollback()
+            raise
+
+    def _delete(self):
+        q = SqlQuery(self._db, 'sessions', SESSION_COLUMNS)
+        q.delete({'id': self.id})
+
+    # copy what RamSession does for now
+    def acquire_lock(self):
+        """Acquire an exclusive lock on the currently-loaded session data."""
+        self.locked = True
+        self.locks.setdefault(self.id, threading.RLock()).acquire()
+
+    def release_lock(self):
+        """Release the lock on the currently-loaded session data."""
+        self.locks[self.id].release()
+        self.locked = False
index 852f8b0..0546fcc 100755 (executable)
@@ -77,6 +77,9 @@ def config(workdir):
                          'admindb': admin_db,
                          'usersdb': users_db,
                          'transdb': trans_db,
+                         'sesstype': 'file',
+                         'sessopt': 'path',
+                         'sessval': os.path.join(workdir, 'sessions'),
                          'secure': 'False'})
     conf = os.path.join(workdir, 'ipsilon.conf')
     with open(conf, 'w+') as f:
index 2e402a3..4b170ca 100644 (file)
@@ -11,8 +11,8 @@ transactions.db = "${transdb}"
 
 tools.sessions.on = True
 tools.sessions.name = "${instance}_ipsilon_session_id"
-tools.sessions.storage_type = "file"
-tools.sessions.storage_path = "${datadir}/sessions"
+tools.sessions.storage_type = "${sesstype}"
+tools.sessions.storage_${sessopt} = "${sessval}"
 tools.sessions.path = "/${instance}"
 tools.sessions.timeout = 60
 tools.sessions.httponly = ${secure}
index a0adfae..e316718 100755 (executable)
@@ -138,7 +138,7 @@ class IpsilonTestBase(object):
                              env=env, preexec_fn=os.setsid)
         self.processes.append(p)
         p.wait()
-        for d in ['adminconfig', 'userprefs', 'transactions']:
+        for d in ['adminconfig', 'userprefs', 'transactions', 'sessions']:
             cmd = ['/usr/bin/createdb', '-h', addr, '-p', port, d]
             subprocess.check_call(cmd, env=env)
 
index 12f1cf2..14ffd36 100755 (executable)
@@ -37,6 +37,8 @@ idp_g = {'TEMPLATES': '${TESTDIR}/templates/install',
 
 idp_a = {'hostname': '${ADDRESS}:${PORT}',
          'database_url': 'postgresql://@127.0.0.10:45432/%(dbname)s',
+         'session_type': 'sql',
+         'session_dburi': 'postgresql://@127.0.0.10:45432/sessions',
          'admin_user': '${TEST_USER}',
          'system_user': '${TEST_USER}',
          'instance': '${NAME}',