Clone database connection on manager.execute

This commit is contained in:
Marc Aymerich 2015-05-09 11:41:52 +00:00
parent c5140ccbae
commit 6fa29a15b8
7 changed files with 46 additions and 12 deletions

View file

@ -355,3 +355,5 @@ make django admin taskstate uncollapse fucking traceback, ( if exists ?)
# backend.context and backned.instance provided when an action is called? like forms.cleaned_data: do it on manager.generation(backend.context = backend.get_context()) or in backend.__getattr__ ? also backend.head,tail,content switching on manager.generate()? # backend.context and backned.instance provided when an action is called? like forms.cleaned_data: do it on manager.generation(backend.context = backend.get_context()) or in backend.__getattr__ ? also backend.head,tail,content switching on manager.generate()?
# replace return_code by exit_code everywhere # replace return_code by exit_code everywhere
# plan.rate registry

View file

@ -167,7 +167,11 @@ class ServiceBackend(plugins.Plugin, metaclass=ServiceMount):
run = bool(self.scripts) or (self.force_empty_action_execution or bool(self.content)) run = bool(self.scripts) or (self.force_empty_action_execution or bool(self.content))
if not run: if not run:
state = BackendLog.NOTHING state = BackendLog.NOTHING
log = BackendLog.objects.create(backend=self.get_name(), state=state, server=server) using = kwargs.pop('using', None)
manager = BackendLog.objects
if using:
manager = manager.using(using)
log = manager.create(backend=self.get_name(), state=state, server=server)
return log return log
def execute(self, server, async=False, log=None): def execute(self, server, async=False, log=None):

View file

@ -6,7 +6,7 @@ from functools import partial
from django.core.mail import mail_admins from django.core.mail import mail_admins
from orchestra.utils.db import close_connection from orchestra.utils import db
from orchestra.utils.python import import_class, OrderedSet from orchestra.utils.python import import_class, OrderedSet
from . import settings, Operation from . import settings, Operation
@ -31,7 +31,7 @@ def keep_log(execute, log, operations):
send_report(execute, args, log) send_report(execute, args, log)
except Exception as e: except Exception as e:
trace = traceback.format_exc() trace = traceback.format_exc()
log.state = BackendLog.EXCEPTION log.state = log.EXCEPTION
log.stderr = trace log.stderr = trace
log.save() log.save()
subject = 'EXCEPTION executing backend(s) %s %s' % (str(args), str(kwargs)) subject = 'EXCEPTION executing backend(s) %s %s' % (str(args), str(kwargs))
@ -122,12 +122,9 @@ def execute(scripts, serialize=False, async=None):
kwargs = { kwargs = {
'async': async, 'async': async,
} }
log = backend.create_log(*args, **kwargs) with db.clone(model=BackendLog) as handle:
# TODO Perform this shit outside of the current transaction in a non-hacky way log = backend.create_log(*args, using=handle.target)
#t = threading.Thread(target=backend.create_log, args=args, kwargs=kwargs) log._state.db = handle.origin
#t.start()
#log = t.join()
# End of hack
kwargs['log'] = log kwargs['log'] = log
task = keep_log(backend.execute, log, operations) task = keep_log(backend.execute, log, operations)
logger.debug('%s is going to be executed on %s.' % (backend, route.host)) logger.debug('%s is going to be executed on %s.' % (backend, route.host))
@ -135,7 +132,7 @@ def execute(scripts, serialize=False, async=None):
# Execute one backend at a time, no need for threads # Execute one backend at a time, no need for threads
task(*args, **kwargs) task(*args, **kwargs)
else: else:
task = close_connection(task) task = db.close_connection(task)
thread = threading.Thread(target=task, args=args, kwargs=kwargs) thread = threading.Thread(target=task, args=args, kwargs=kwargs)
thread.start() thread.start()
if not async: if not async:

View file

@ -28,6 +28,7 @@ def grant_permission(modeladmin, request, queryset):
for user in queryset: for user in queryset:
user.grant_to = to user.grant_to = to
user.grant_ro = ro user.grant_ro = ro
# DOn't collect, execute right away for path validation
OperationsMiddleware.collect('grant_permission', instance=user) OperationsMiddleware.collect('grant_permission', instance=user)
context = { context = {
'type': _("read-only") if ro else _("read-write"), 'type': _("read-only") if ro else _("read-write"),
@ -35,6 +36,7 @@ def grant_permission(modeladmin, request, queryset):
} }
msg = _("Granted %(type)s permissions on %(to)s") % context msg = _("Granted %(type)s permissions on %(to)s") % context
modeladmin.log_change(request, user, msg) modeladmin.log_change(request, user, msg)
# TODO feedback message
return return
opts = modeladmin.model._meta opts = modeladmin.model._meta
app_label = opts.app_label app_label = opts.app_label

View file

@ -72,7 +72,7 @@ class UNIXUserBackend(ServiceController):
'to': user.grant_to, 'to': user.grant_to,
'ro': user.grant_ro, 'ro': user.grant_ro,
}) })
if user.ro: if user.grant_ro:
self.append('echo "acl add read permissions for %(user)s to %(to)s"' % context) self.append('echo "acl add read permissions for %(user)s to %(to)s"' % context)
else: else:
self.append('echo "acl add read-write permissions for %(user)s to %(to)s"' % context) self.append('echo "acl add read-write permissions for %(user)s to %(to)s"' % context)

View file

@ -37,7 +37,7 @@
<div class="field-box field-home"> <div class="field-box field-home">
{{ form.path_extension.errors }} {{ form.path_extension.errors }}
<label for="{{ form.base_path.id_for_label }}">{{ form.base_path.label }}:</label> <label for="{{ form.base_path.id_for_label }}">{{ form.base_path.label }}:</label>
{{ form.base_path }} {{ form.base_path }}{% for x in ""|ljust:"50" %}&nbsp;{% endfor %}
<p class="help">{{ form.base_path.help_text|safe }}</p> <p class="help">{{ form.base_path.help_text|safe }}</p>
</div> </div>
<div class="field-box field-home"> <div class="field-box field-home">

View file

@ -1,6 +1,7 @@
import sys import sys
from django import db from django import db
from django.conf import settings as djsettings
def running_syncdb(): def running_syncdb():
@ -31,3 +32,31 @@ def close_connection(execute):
finally: finally:
db.connection.close() db.connection.close()
return wrapper return wrapper
class clone(object):
"""
clone database in order to have fresh connections and make queries outside the current transaction
with db.clone(model=BackendLog) as handle:
log = BackendLog.objects.using(handle.target).create()
log._state.db = handle.origin
"""
def __init__(self, model=None, origin='', target=''):
if model is not None:
origin = db.router.db_for_write(model)
self.origin = origin or db.DEFAULT_DB_ALIAS
self.target = target or 'other_' + origin
def __enter__(self):
djsettings.DATABASES[self.target] = djsettings.DATABASES[self.origin]
# Because db.connections.datases is a cached property
self.old_connections = db.connections
db.connections = db.utils.ConnectionHandler()
return self
def __exit__(self, type, value, traceback):
db.connections[self.target].close()
djsettings.DATABASES.pop(self.target)
db.connections = self.old_connections