144 lines
5.4 KiB
Python
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)
|