lc_ldap/ldap_locks.py
Pierre-Elliott Bécue 924f5c0684 [locks] Création d'une nouvelle gestion de locks.
J'ai pas mal regardé le travail d'adg, certains trucs ne me convenaient
pas dans la gestion, du coup j'ai réécrit un truc, qui me semble
mieux.

J'ai laissé la licence telle quelle, mais j'ai mis mon nom en author
2013-03-09 19:55:21 +01:00

158 lines
5.7 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# LDAP_LOCKS.PY-- Locks for lc_ldap
#
## Copyright (C) 2013 Cr@ns <roots@crans.org>
# Authors:
# * Antoine Durand-Gasselin <adg@crans.org>
# * Pierre-Elliott Bécue <becue@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
import os
import exceptions
import socket
import crans_utils
class LdapLockedByYou(exceptions.StandardError):
"""
Classe d'erreur pour les locks par le process courant
"""
pass
class LdapLockedByOther(exceptions.StandardError):
"""
Erreur car le lock est occupé par un autre process.
"""
pass
class LockFormatError(exceptions.StandardError):
"""
L'objet lock qu'on a récupéré n'est pas tel qu'on le voudrait
"""
pass
LOCKS_DN = 'ou=lock,dc=crans,dc=org'
class LdapLockHolder:
"""
Système de gestion des locks pour une instance de lc_ldap.
"""
def __init__(self, conn):
"""
On crée la connexion, et on crée un dico vide.
"""
self.locks = {}
self.host = socket.gethostbyname()
self.pid = os.getpid()
self.conn = conn
def __del__(self):
"""
On essaye de détruire tous les verrous hébergés par
l'objet mourant.
"""
for item, value in self.locks:
try:
self.removelock(item, value)
except:
pass
del self.conn
def addlock(self, item, value):
"""
Applique un verrou sur "$item=$value,$LOCKS_DN",
si le précédent verrou était géré par la session
courante de lc_ldap, on prévient l'utilisateur
de la session, pour qu'il puisse éventuellement
libérer le lock.
Sinon, on ne peut pas override le lock, et on laisse
tomber.
"""
try:
host, pid = self.getlock(item, value)
if host == self.host and pid == self.pid():
raise LdapLockedByYou("La donnée %r=%r est lockée par vous-même." % (item, value))
elif host == self.host:
status = crans_utils.process_status(pid)
if status:
raise LdapLockedByOther("La donnée %r=%r est lockée par quelqu'un d'autre ou un processus." % (item, value))
else:
self.removelock(item, value)
else:
raise LdapLockedByOther("La donnée %r=%r est lockée par quelqu'un d'autre ou un processus." % (item, value))
except ldap.NO_SUCH_OBJECT:
pass
except LockFormatError:
self.removelock(item, value)
dn = "%s=%s,%s" % (item, value, LOCKS_DN)
lockid = "%s-%s" % (self.host, self.pid)
modlist = ldap.modlist.addModlist({'objectClass' : 'lock',
'lockid' : lockid,
item : value})
try:
self.conn.add_s(dn, modlist)
self.locks[item] = value
except ldap.ALREADY_EXISTS:
status = crans_utils.process_status(pid)
if status:
raise LdapLockedByOther("La donnée %r=%r est lockée par quelqu'un d'autre ou un processus." % (item, value))
else:
self.removelock(item, value)
try:
self.conn.add_s(dn, modlist)
self.locks[item] = value
except:
raise StandardError("Quelque chose a planté durant la pose du lock %s=%s" % (item, value))
def removelock(self, item, value):
"""
Libère le lock "$item=$value,$LOCKS_DN".
"""
print "Deleting %s=%s,%s\n" % (item, value, LOCKS_DN)
if self.locks.get(item, "") == value:
self.locks.pop(item)
self.conn.delete_s("%s=%s,%s" % (item, value, LOCKS_DN))
else:
print "Lock %s=%s,%s does not exist on this session" % (item, value, LOCKS_DN)
def getlock(self, item, value):
"""
Trouve le lock item=value, et renvoie le contenu de lockinfo
via un couple host, pid
"""
result = self.conn.search_s('%s=%s,%s' % (item, value, LOCKS_DN), 0)
try:
return result[0][1]['lockid'][0].split('-')
except:
raise LockFormatError