summaryrefslogtreecommitdiffstats
path: root/ipalib
diff options
context:
space:
mode:
Diffstat (limited to 'ipalib')
-rw-r--r--ipalib/backend.py68
-rw-r--r--ipalib/request.py33
2 files changed, 100 insertions, 1 deletions
diff --git a/ipalib/backend.py b/ipalib/backend.py
index b1e15f337..827067f47 100644
--- a/ipalib/backend.py
+++ b/ipalib/backend.py
@@ -21,7 +21,10 @@
Base classes for all backed-end plugins.
"""
+import threading
import plugable
+from errors2 import PublicError, InternalError, CommandError
+from request import context, Connection, destroy_context
class Backend(plugable.Plugin):
@@ -29,7 +32,70 @@ class Backend(plugable.Plugin):
Base class for all backend plugins.
"""
- __proxy__ = False # Backend plugins are not wrapped in a PluginProxy
+ __proxy__ = False # Backend plugins are not wrapped in a PluginProxy
+
+
+class Connectible(Backend):
+ # Override in subclass:
+ connection_klass = None
+
+ def connect(self, *args, **kw):
+ """
+ Create thread-local connection.
+ """
+ if hasattr(context, self.name):
+ raise StandardError(
+ "connection 'context.%s' already exists in thread %r" % (
+ self.name, threading.currentThread().getName()
+ )
+ )
+ if not issubclass(self.connection_klass, Connection):
+ raise ValueError(
+ '%s.connection_klass must be a request.Connection subclass' % self.name
+ )
+ conn = self.connection_klass(*args, **kw)
+ setattr(context, self.name, conn)
+ assert self.conn is conn.conn
+
+ def isconnected(self):
+ """
+ Return ``True`` if thread-local connection on `request.context` exists.
+ """
+ return hasattr(context, self.name)
+
+ def __get_conn(self):
+ """
+ Return thread-local connection.
+ """
+ if not hasattr(context, self.name):
+ raise AttributeError('no context.%s in thread %r' % (
+ self.name, threading.currentThread().getName())
+ )
+ return getattr(context, self.name).conn
+ conn = property(__get_conn)
+
+
+class Executioner(Backend):
+
+ def execute(self, name, *args, **options):
+ error = None
+ try:
+ if name not in self.Command:
+ raise CommandError(name=name)
+ result = self.Command[name](*args, **options)
+ except PublicError, e:
+ error = e
+ except StandardError, e:
+ self.exception(
+ 'non-public: %s: %s', e.__class__.__name__, str(e)
+ )
+ error = InternalError()
+ destroy_context()
+ if error is None:
+ return result
+ assert isinstance(error, PublicError)
+ raise error
+
class Context(plugable.Plugin):
diff --git a/ipalib/request.py b/ipalib/request.py
index 6ad7ad35f..812e526d6 100644
--- a/ipalib/request.py
+++ b/ipalib/request.py
@@ -25,6 +25,7 @@ Per-request thread-local data.
import threading
import locale
import gettext
+from base import ReadOnly, lock
from constants import OVERRIDE_ERROR
@@ -32,6 +33,38 @@ from constants import OVERRIDE_ERROR
context = threading.local()
+class Connection(ReadOnly):
+ """
+ Base class for connection objects stored on `request.context`.
+ """
+
+ def __init__(self, *args, **kw):
+ self.conn = self.create(*args, **kw)
+ lock(self)
+
+ def create(self, *args, **kw):
+ """
+ Create and return the connection (implement in subclass).
+ """
+ raise NotImplementedError('%s.create()' % self.__class__.__name__)
+
+ def close(self):
+ """
+ Close the connection (implement in subclass).
+ """
+ raise NotImplementedError('%s.close()' % self.__class__.__name__)
+
+
+def destroy_context():
+ """
+ Delete all attributes on thread-local `request.context`.
+ """
+ for (name, value) in context.__dict__.items():
+ if isinstance(value, Connection):
+ value.close()
+ delattr(context, name)
+
+
def ugettext(message):
if hasattr(context, 'ugettext'):
return context.ugettext(message)