lc_ldap/ldap_locks.py
2010-07-03 17:50:10 +02:00

144 lines
5.4 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# LDAP_LOCKS.PY-- Locks for lc_ldap
#
## Copyright (C) 2010 Cr@ns <roots@crans.org>
# Author: Antoine Durand-Gasselin <adg@crans.org>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the Cr@ns nor the names of its contributors may
# be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT
# HOLDER> BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import ldap, os
base_lock = 'ou=lock,dc=crans,dc=org'
class CransLock:
def __init__(self, conn):
self.conn = conn
self.locks = {}
self._active = []
def __enter__(self):
self.lock()
def __exit__(self, *args):
# XXX - connecter correctement les tracebacks.
print "exiting with exception", args
self.release()
return True
def add(self, item, valeur):
'''rajoute un lock, après avoir vérifié qu'il peut être posé'''
try:
locked = self._islocked(item, valeur)
if locked:
raise EnvironmentError(u'Object déjà locké', locked)
except ldap.NO_SUCH_OBJECT:
pass
locked_values = self.locks.get(item, [])
if valeur not in locked_values:
locked_values.append(valeur)
self.locks[item] = locked_values
def remove(self, item, valeur):
'''Enlève un lock'''
self.locks[item].remove(valeur)
def lock(self):
'''Essaie de prendre tous les verrous'''
items = self.locks.items()
items.sort()
try:
for item, valeurs in items:
for valeur in valeurs:
self._lockitem(item, valeur)
except Exception, e:
# XXX - connecter proprement les traceback
self.release()
raise e
def release(self):
'''Relâche tous les verrous'''
exceptions = []
print "releasing", self._active
for item in self._active[:]:
try:
self._releaseitem(item)
except Exception, e:
exceptions.append(e)
if len(exceptions) == 1:
# XXX - connecter proprement les tracebacks
raise exceptions[0]
elif len(exceptions) > 1:
raise Exception(exceptions)
def _islocked(self, item, valeur):
# XXX - return self.conn.search_s(base_dn, 2, '%s=%s' % (item, valeur))
return self.conn.search_s('%s=%s,%s' % (item, valeur, base_lock), 0)
def _lockitem(self, item, valeur):
u"""
Lock un item avec la valeur valeur, les items possibles
peuvent être :
aid $ chbre $ mail $ mailAlias $ canonicalAlias $
mid $ macAddress $ host $ hostAlias $ ipHostNumber
Retourne le dn du lock
"""
valeur = valeur.encode('utf-8')
lock_dn = '%s=%s,%s' % (item, valeur, base_lock)
lockid = '%s-%s' % ('localhost', os.getpid())
modlist = ldap.modlist.addModlist({ 'objectClass': 'lock',
'lockid': lockid,
item: valeur })
print "locking", lock_dn
try:
self.conn.add_s(lock_dn, modlist)
except ldap.ALREADY_EXISTS:
# # Pas de chance, le lock est déja pris
# try:
# res = self.conn.search_s(lock_dn, 2, 'objectClass=lock')[0]
# l = res[1]['lockid'][0]
# except: l = '%s-1' % hostname
# if l != lockid:
# # C'est locké par un autre process que le notre
# # il tourne encore ?
# if l.split('-')[0] == hostname and os.system('ps %s > /dev/null 2>&1' % l.split('-')[1] ):
# # Il ne tourne plus
# self._releaseitem(res[0]) # delock
# return self._lockitem(item, valeur) # relock
raise EnvironmentError(u'Objet (%s=%s) locké, patienter.' % (item, valeur), lockid)
self._active.append(lock_dn)
return lock_dn
def _releaseitem(self, lockdn):
u"""Destruction d'un lock"""
# Mettre des verifs ?
print "releasing", lockdn
self._active.remove(lockdn)
self.conn.delete_s(lockdn)