411 lines
17 KiB
Python
411 lines
17 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
|
|
import ldap
|
|
import socket
|
|
import lc_ldap
|
|
import attributs
|
|
import objets
|
|
import variables
|
|
import config
|
|
from gen_confs.dhcpd_new import dydhcp
|
|
import sys
|
|
if not '/usr/scripts' in sys.path:
|
|
sys.path.append('/usr/scripts')
|
|
import gestion.config as config
|
|
|
|
# liste des attributs dont dépend un service
|
|
services_to_attrs = {}
|
|
services_to_attrs['macip'] = [ attributs.ipHostNumber, attributs.ip6HostNumber, attributs.macAddress, attributs.paiement, attributs.carteEtudiant ]
|
|
services_to_attrs['dns'] = [ attributs.ipHostNumber, attributs.ip6HostNumber, attributs.sshFingerprint, attributs.host, attributs.hostAlias, attributs.dnsIpv6 , attributs.hostCert, attributs.portTCPin, attributs.portUDPin ]
|
|
services_to_attrs['blacklist'] = [ attributs.blacklist, attributs.chbre, attributs.mailInvalide ] + services_to_attrs['macip']
|
|
services_to_attrs['ports'] = [ attributs.portUDPout, attributs.portUDPin, attributs.portTCPout, attributs.portTCPin ]
|
|
services_to_attrs['droits'] = [ attributs.droits ]
|
|
services_to_attrs['mail_ajout_droits'] = [ attributs.droits ]
|
|
services_to_attrs['dhcp'] = services_to_attrs['macip']
|
|
services_to_attrs['mail_bienvenue'] = [ attributs.mail, attributs.canonicalAlias ]
|
|
services_to_attrs['mail_modif'] = [ attributs.droits, attributs.exempt ] + services_to_attrs['ports'] + services_to_attrs['macip']
|
|
|
|
services_to_objects={}
|
|
services_to_objects['delete']={}
|
|
services_to_objects['create']={}
|
|
services_to_objects['create']['home'] = [objets.adherent, objets.club]
|
|
services_to_objects['delete']['del_user'] = [objets.adherent, objets.club]
|
|
|
|
|
|
def services_to_args_mail_modif(x):
|
|
if isinstance(x, attributs.droits):
|
|
return [ "uid=%s" % x.parent['uid'][0] ]
|
|
elif isinstance(x, attributs.Attr) and x.__class__ in [ attributs.exempt ] + services_to_attrs['ports'] and isinstance(x.parent, objets.machine):
|
|
return [ "mid=%s" % x.parent['mid'][0] ]
|
|
elif isinstance(x.parent, objets.machine) and isinstance(x.parent.proprio(mode='ro'), objets.AssociationCrans):
|
|
return [ "mid=%s" % x.parent['mid'][0] ]
|
|
else:
|
|
return []
|
|
|
|
def services_to_args_macip(x):
|
|
if isinstance(x.parent, objets.machine):
|
|
if isinstance(x, attributs.ipHostNumber):
|
|
return [str(x)]
|
|
else:
|
|
return [ str(ip) for ip in x.parent.get('ipHostNumber',[]) ]
|
|
elif isinstance(x.parent, objets.proprio):
|
|
return [ str(ip) for m in x.parent.machines() for ip in m.get('ipHostNumber',[])]
|
|
else:
|
|
return []
|
|
|
|
def services_to_args_port(x):
|
|
if isinstance(x, attributs.ipHostNumber) or isinstance(x, attributs.ip6HostNumber):
|
|
return [str(x)]
|
|
elif isinstance(x.parent, objets.machine):
|
|
return [ str(ip) for ip in x.parent.get('ipHostNumber',[]) ]
|
|
else:
|
|
return []
|
|
|
|
def services_to_args_dns(x):
|
|
if isinstance(x.parent, objets.machine):
|
|
return [ str(x.parent['mid'][0]) ]
|
|
elif isinstance(x.parent, objets.baseCert):
|
|
return [ str(x.parent.machine()['mid'][0]) ]
|
|
else:
|
|
return []
|
|
|
|
# Trouver comment faire le cas où on ajoute une redirection mail (il faut alors retourner un quadruplet "homedir,uidNumber,uid,mail")
|
|
def services_to_args_home(x):
|
|
if isinstance(x, attributs.Attr):
|
|
proprio=x.parent
|
|
elif isinstance(x, objets.proprio):
|
|
proprio=x
|
|
else:
|
|
return []
|
|
if u'cransAccount' in [ str(o) for o in proprio['objectClass']]:
|
|
return [ "%s,%s,%s" % (proprio['homeDirectory'][0],proprio['uidNumber'][0],proprio['uid'][0]) ]
|
|
else:
|
|
return []
|
|
|
|
def services_to_args_del_user(x):
|
|
if isinstance(x, attributs.Attr):
|
|
proprio=x.parent
|
|
elif isinstance(x, objets.proprio):
|
|
proprio=x
|
|
else:
|
|
return []
|
|
if u'cransAccount' in [ str(o) for o in proprio['objectClass']]:
|
|
return [ "%s,%s" % (proprio['uid'][0], proprio['homeDirectory'][0]) ]
|
|
else:
|
|
return []
|
|
|
|
|
|
def services_to_args_blacklist(x):
|
|
if isinstance(x.parent, objets.machine):
|
|
return [ str(ip) for m in x.parent.proprio(mode='ro').machines() for ip in m['ipHostNumber'] ]
|
|
elif isinstance(x.parent, objets.proprio):
|
|
return [ str(ip) for m in x.parent.machines() for ip in m['ipHostNumber'] ]
|
|
else:
|
|
return []
|
|
|
|
def services_to_args_mail_bienvenue(x):
|
|
if isinstance(x, attributs.Attr) and isinstance(x.parent, objets.adherent):
|
|
adh=x.parent
|
|
elif isinstance(x, objets.adherent):
|
|
adh=x
|
|
else:
|
|
return []
|
|
if u'cransAccount' in [ str(o) for o in adh['objectClass']]:
|
|
return [ "%s" % adh['canonicalAlias'][0] ]
|
|
else:
|
|
return [ "%s" % adh['mail'][0] ]
|
|
|
|
def services_to_time_blacklist(x):
|
|
if isinstance(x, attributs.blacklist):
|
|
# Un set à au plus deux valeur : la date de début et la date de fin de la blacklist
|
|
return set([(0 if x['debut'] == '-' else x['debut']), (0 if x['fin'] == '-' else x['fin'])])
|
|
else:
|
|
return [0]
|
|
|
|
def services_to_time_mail_bienvenue(x):
|
|
if isinstance(x, attributs.Attr) and isinstance(x.parent, objets.adherent):
|
|
adh=x.parent
|
|
elif isinstance(x, objets.adherent):
|
|
adh=x
|
|
else:
|
|
return []
|
|
if u'cransAccount' in [ str(o) for o in adh['objectClass']]:
|
|
return [ time.time() + 660 ]
|
|
else:
|
|
return [ 0 ]
|
|
|
|
# génération des arguments du service à redémarrer (par defaut [])
|
|
services_to_args={}
|
|
services_to_args['macip']=services_to_args_macip
|
|
## Inutile pour blackliste pour le moment
|
|
#services_to_args['blacklist']=services_to_args_blacklist
|
|
services_to_args['port']=services_to_args_port
|
|
services_to_args['dns']=services_to_args_dns
|
|
services_to_args['home']=services_to_args_home
|
|
services_to_args['mail_ajout_droits'] = lambda x: "%s:%s" % (x.parent['uid'][0], x)
|
|
services_to_args['del_user']=services_to_args_del_user
|
|
services_to_args['mail_bienvenue']=services_to_args_mail_bienvenue
|
|
services_to_args['mail_modif']=services_to_args_mail_modif
|
|
|
|
# Quand redémarrer le service (par defaut [0]), doit returner un iterable avec potentiellement plusieurs dates
|
|
services_to_time={}
|
|
services_to_time['blacklist']=services_to_time_blacklist
|
|
services_to_time['mail_bienvenue']=services_to_time_mail_bienvenue
|
|
|
|
attrs_to_services = {}
|
|
for (key, values) in services_to_attrs.items():
|
|
for value in values:
|
|
attrs_to_services[value] = attrs_to_services.get(value, []) + [key]
|
|
|
|
objects_to_services={}
|
|
for status in ['delete', 'create']:
|
|
objects_to_services[status]={}
|
|
for (key, values) in services_to_objects[status].items():
|
|
for value in values:
|
|
objects_to_services[status][value]=objects_to_services[status].get(value, []) + [key]
|
|
|
|
# Identique à celle dans ldap_crans.py
|
|
def preattr(val):
|
|
"""
|
|
val est :
|
|
* un entier
|
|
* une chaîne
|
|
* une liste avec un seul entier ou une seule chaîne
|
|
|
|
Retourne [ len(str(val).strip), str(val).strip en utf-8 ]
|
|
"""
|
|
|
|
if isinstance(val, list) and len(val) == 1:
|
|
return preattr(val[0])
|
|
|
|
elif isinstance(val, str) or isinstance(val, int):
|
|
val = str(val).strip()
|
|
# On passe tout en utf-8 pour ne pas avoir de problèmes
|
|
# d'accents dans la base
|
|
return [len(val), val]
|
|
elif isinstance(val, unicode):
|
|
val = val.strip()
|
|
return [len(val), val.encode('utf-8')]
|
|
else:
|
|
raise TypeError(type(val))
|
|
|
|
# Presque identique à celle dans ldap_crans.py
|
|
def service_to_restart(conn, new=None, args=[], start=0):
|
|
"""
|
|
start indique la date (en secondes depuis epoch) à partir du
|
|
moment où cette action sera effectuée.
|
|
|
|
Si new = None retourne la liste des services à redémarrer.
|
|
|
|
Si new est fourni, mais ne commence pas par '-', on ajoute
|
|
le service à la liste avec les arguments args (args doit être
|
|
une liste).
|
|
|
|
Si new commence par '-', on supprime le service si son start
|
|
est dans le futur.
|
|
|
|
Si new commence par '--', on supprime le service sans condition.
|
|
"""
|
|
if new: new = preattr(new)[1]
|
|
|
|
# Quels services sont déjà à redémarrer ?
|
|
serv = {} # { service: [ arguments ] }
|
|
serv_dates = {} # { service: [ dates de restart ] }
|
|
services = []
|
|
for s in conn.search_s(variables.services_dn, 1, 'objectClass=service'):
|
|
s = s[1]
|
|
serv[s['cn'][0]] = s.get('args', [])
|
|
serv_dates[s['cn'][0]] = s.get('start', [])
|
|
services.append(Service(s['cn'][0], s.get('args', []), s.get('start', [])))
|
|
|
|
# Retourne la liste des services à redémarrer
|
|
if not new: return services
|
|
|
|
# Effacement d'un service
|
|
if new[0] == '-':
|
|
if new[1] == '-':
|
|
# Double -- on enlève quelque soit la date
|
|
remove_dn = 'cn=%s,%s' % (new[2:], variables.services_dn)
|
|
else:
|
|
# On enlève uniquement si la date est passée
|
|
remove_dn = 'cn=%s,%s' % (new[1:], variables.services_dn)
|
|
if not serv.has_key(new[1:]):
|
|
# Existe pas => rien à faire
|
|
return
|
|
keep_date = []
|
|
for date in serv_dates[new[1:]]:
|
|
if time.time() < int(date):
|
|
keep_date.append(date)
|
|
if keep_date:
|
|
mods = [{'start': serv_dates[new[1:]]}, { 'start': keep_date }]
|
|
conn.modify_s(remove_dn, ldap.modlist.modifyModlist(*mods))
|
|
remove_dn = None
|
|
|
|
if remove_dn:
|
|
# Suppression
|
|
try: conn.delete_s(remove_dn)
|
|
except: pass
|
|
# Si n'existe pas => Erreur mais le résultat est là.
|
|
return
|
|
|
|
serv_dn = 'cn=%s,%s' % (new, variables.services_dn)
|
|
|
|
# Conversion avant stockage dans la base
|
|
if isinstance(args, basestring):
|
|
args = [args]
|
|
args = map(lambda x:preattr(x)[1], args)
|
|
try:
|
|
start = [int(start)]
|
|
except TypeError:
|
|
pass
|
|
start = map(lambda x:preattr(x)[1], start)
|
|
|
|
if new in serv.keys():
|
|
modlist = []
|
|
|
|
new_args = []
|
|
# Nouveaux arguments ?
|
|
for arg in args:
|
|
if arg not in serv[new]:
|
|
new_args.append(arg)
|
|
if new_args:
|
|
modlist += ldap.modlist.modifyModlist({'args': serv[new]},
|
|
{'args': serv[new] + new_args})
|
|
|
|
new_date = []
|
|
# Nouvelle date ?
|
|
for date in start:
|
|
if date not in serv_dates[new]:
|
|
new_date.append(date)
|
|
if new_date:
|
|
modlist += ldap.modlist.modifyModlist({'start': serv_dates[new]},
|
|
{'start': serv_dates[new] + new_date})
|
|
|
|
if modlist:
|
|
try:
|
|
conn.modify_s(serv_dn, modlist)
|
|
except ldap.TYPE_OR_VALUE_EXISTS:
|
|
# Pas grave
|
|
pass
|
|
# else rien à faire
|
|
else:
|
|
# Entrée non présente -> ajout
|
|
modlist = ldap.modlist.addModlist({ 'objectClass': 'service',
|
|
'cn': new,
|
|
'args': args,
|
|
'start': start })
|
|
try:
|
|
conn.add_s(serv_dn, modlist)
|
|
except ldap.ALREADY_EXISTS:
|
|
# Existe déja => rien à faire
|
|
pass
|
|
|
|
def services_to_restart(conn, old_attrs={}, new_attrs={}, created_object=[], deleted_object=[]):
|
|
"""Détermine quels sont les services à reconfigurer"""
|
|
|
|
# On tranforme les dico *_attrs du type string -> attr en *_attrs_c du type class -> attr
|
|
old_attrs_c = dict((attributs.AttributeFactory.get(key), value) for key,value in old_attrs.items() if attributs.AttributeFactory.get(key, fallback=None) != None and value)
|
|
new_attrs_c = dict((attributs.AttributeFactory.get(key), value) for key,value in new_attrs.items() if attributs.AttributeFactory.get(key, fallback=None) != None and value)
|
|
|
|
# Les attributs crée
|
|
created_attr = [ attr for name in new_attrs_c.keys() if not name in old_attrs_c.keys() for attr in new_attrs_c[name]]
|
|
# Les attributs supprimé
|
|
deleted_attr = [ attr for name in old_attrs_c.keys() if not name in new_attrs_c.keys() for attr in old_attrs_c[name]]
|
|
# Les attributs mis à jour : liste de (ancien, nouveau, ancien\nouveau, nouveau\ancien) pour les types d'attibut a la fois dans old_attrs et new_attrs
|
|
updated_attr = [ (old_attrs_c[name], new_attrs_c[name],
|
|
[ i for i in old_attrs_c[name] if i.value not in [ j.value for j in new_attrs_c[name]]],
|
|
[ i for i in new_attrs_c[name] if i.value not in [ j.value for j in old_attrs_c[name]]],
|
|
) for name in set(new_attrs_c.keys()).intersection(old_attrs_c.keys()) if old_attrs_c[name][-1].value != new_attrs_c[name][-1].value ]
|
|
updated_attr_new = [ i for j in updated_attr for i in j[3]]
|
|
updated_attr_old = [ i for j in updated_attr for i in j[2]]
|
|
|
|
|
|
# Génération du dico "nom de service à redémarrer" -> "attribut dont dépende le service qui ont été modifier"
|
|
services_to_restart = {}
|
|
for attr in [ attr for l in [created_attr, deleted_attr, updated_attr_new ] for attr in l]:
|
|
for service in attrs_to_services.get(attr.__class__, []):
|
|
services_to_restart[service] = services_to_restart.get(service, []) + [attr]
|
|
|
|
for obj in created_object:
|
|
for service in objects_to_services['create'].get(obj.__class__, []):
|
|
services_to_restart[service] = services_to_restart.get(service, []) + [obj]
|
|
|
|
for obj in deleted_object:
|
|
for service in objects_to_services['delete'].get(obj.__class__, []):
|
|
services_to_restart[service] = services_to_restart.get(service, []) + [obj]
|
|
|
|
for service in services_to_restart.keys():
|
|
# ok, la variable s'appel attr, mais ça paut aussi être un objet
|
|
for attr in services_to_restart[service]:
|
|
arg = set()
|
|
# Il y a une fonction pour générer des arguements, ils sont donc nécessaire
|
|
if service in services_to_args.keys():
|
|
arg = arg.union(services_to_args[service](attr))
|
|
if attr in updated_attr_new:
|
|
for old_attr in updated_attr_old:
|
|
if attr.__class__ == old_attr.__class__:
|
|
arg = arg.union(services_to_args[service](old_attr))
|
|
if not arg: # services_to_args retour une liste vide d'arguments, on passe à l'attribut suivant
|
|
continue
|
|
|
|
# Cas du dhcp
|
|
if attr.__class__ in services_to_attrs['dhcp']:
|
|
for server in config.dhcp_servers:
|
|
try:
|
|
dhcp=dydhcp(server)
|
|
if old_attrs.get('ipHostNumber', []) and old_attrs.get('macAddress', []):
|
|
if new_attrs.get('ipHostNumber', []) and new_attrs.get('macAddress', []):
|
|
if str(old_attrs['ipHostNumber'][0]) != str(new_attrs['ipHostNumber'][0]) or str(old_attrs['macAddress'][0]) != str(new_attrs['macAddress'][0]):
|
|
dhcp.del_host(str(old_attrs['ipHostNumber'][0]), str(old_attrs['macAddress'][0]))
|
|
dhcp.add_host(str(new_attrs['ipHostNumber'][0]), str(new_attrs['macAddress'][0]), str(new_attrs['host'][0]))
|
|
else:
|
|
dhcp.del_host(str(old_attrs['ipHostNumber'][0]), str(old_attrs['macAddress'][0]))
|
|
elif new_attrs.get('ipHostNumber', []) and new_attrs.get('macAddress', []):
|
|
dhcp.add_host(str(new_attrs['ipHostNumber'][0]), str(new_attrs['macAddress'][0]), str(new_attrs['host'][0]))
|
|
except socket.error:
|
|
pass
|
|
|
|
# Il y a une fonction pour dire quand il faut redémarrer le service
|
|
if service in services_to_time.keys():
|
|
for start in services_to_time[service](attr):
|
|
service_to_restart(conn, service, list(arg), start)
|
|
# Sinon, on le redémarre tout de suite
|
|
else:
|
|
service_to_restart(conn, service, list(arg), 0)
|
|
else:
|
|
service_to_restart(conn, service, [], 0)
|
|
|
|
|
|
|
|
|
|
|
|
# Identique à la classe dans ldap_crans.py
|
|
class Service:
|
|
""" Définit un service à redémarrer """
|
|
def __init__(self, nom, args=[], start=[]):
|
|
"""
|
|
Nom du service
|
|
Liste des arguments
|
|
Liste d'horaires de démarrages
|
|
"""
|
|
|
|
self.nom = nom
|
|
self.args = args
|
|
self.start = map(int, start)
|
|
|
|
def __unicode__(self):
|
|
starting = self.start
|
|
starting.sort()
|
|
dates = u' et '.join(map(lambda t: t < time.time() and \
|
|
u"maintenant" or time.strftime(date_format,
|
|
time.localtime(t)),
|
|
self.start))
|
|
dates = u" à partir d%s %s" % (dates.startswith(u"maintenant") and u"e" or u"u",
|
|
dates)
|
|
return (u"%s(%s)%s" % (self.nom,
|
|
u','.join([i.decode("UTF-8") for i in self.args]),
|
|
dates)).replace(u" et maintenant", u"")
|
|
|
|
def __str__(self):
|
|
return self.__unicode__().encode("utf-8", "ignore")
|