diff --git a/gestion/chambres_vides.py b/gestion/chambres_vides.py index 3743398e..e1f6eee6 100755 --- a/gestion/chambres_vides.py +++ b/gestion/chambres_vides.py @@ -1,134 +1,138 @@ #!/bin/bash /usr/scripts/python.sh # -*- encoding: utf-8 -*- -""" Pour détecter les gens en chambre invalide, les prévenir, et supprimer leurs machines - en l'absence de réponse. Récupérer des IPs, c'est cool.""" +""" Pour détecter les gens en chambre invalide, les prévenir, et supprimer leurs +machines en l'absence de réponse. Récupérer des IPs, c'est cool. -# Codé par b2moo, commenté par 20-100, cr{itiqu|on}é par Nit -# -# -# +Codé par b2moo, commenté par 20-100, cr{itiqu|on}é par Nit + + + +""" +import sys +import os import datetime import time import re + +import affichage import lc_ldap.shortcuts -from lc_ldap.crans_utils import to_generalized_time_format as toGeneralizedTimeFormat -conn = lc_ldap.shortcuts.lc_ldap_admin() +from lc_ldap.crans_utils import to_generalized_time_format as to_gtf import mail as mail_module -import sys -#: envoyer un mail à chaque adhérent concerné -sendmails = False -if "--mail-all" in sys.argv: - sendmails = True -from email.header import Header +from config import demenagement_delai as delai, \ + debut_periode_transitoire, periode_transitoire -#: Envoyer un mail à respbats -sendmail_respbats = True -if "--no-mail" in sys.argv: - sendmail_respbats = False +ERASE_DAY = { 'second': 0, 'minute': 0, 'microsecond': 0, 'hour': 0, } +DAY = datetime.timedelta(days=1) +FORMAT_LDAP = '%Y%m%d%H%M%S%z' -DEBUG = False -if "--debug" in sys.argv: - DEBUG = True +RESP = 'respbats@crans.org' -import os -import config -year = config.ann_scol -delai = config.demenagement_delai +FILTER = u'''(& + (aid=*) + (chbre=????) + (finAdhesion>=%(date)s) + (finConnexion>=%(date)s) +)''' # On récupère ceux qui n'ont pas payé cette année -now = time.time() -if config.periode_transitoire: - bad_boys_e_s = conn.search(u'(&(aid=*)(chbre=????)(|(&(paiement=%d)(!(paiement=%d)))(finAdhesion<=%s)(finConnexion<=%s)))' % (year-1, year, toGeneralizedTimeFormat(now), toGeneralizedTimeFormat(now))) -else: - bad_boys_e_s = conn.search(u'(&(aid=*)(chbre=????)(|(paiement=%d)(finAdhesion>=%s)(finConnexion>=%s)))' % (year, toGeneralizedTimeFormat(now), toGeneralizedTimeFormat(now))) +now = datetime.datetime.now().replace(**ERASE_DAY) -to_print = [] -to_error = [] -for clandestin in bad_boys_e_s: +def get_kickout(adh): + """Renvoie la date et la chambre de la dernière expulsion""" # On cherche la dernière fois qu'il s'est retrouvé en chambre ???? - for l in clandestin['historique'][::-1]: + for l in adh['historique'][::-1]: # On récupère la date du dernier changement de chambre # (l'historique est enregistré par ordre chronologique) x = re.match("(.*),.* : chbre \((.*) -> \?\?\?\?\)", str(l)) if x <> None: - kickout_date = x.group(1) - exchambre = x.group(2) + return (l.get_datetime(), x.group(2)) + raise IndexError('Adh jamais expulse') - machine_liste = clandestin.machines() - # On lui accorde un délai - kickout_date = time.mktime(time.strptime(kickout_date, "%d/%m/%Y %H:%M")) - delta = now - kickout_date - ttl = delai*86400 - delta - if ttl > 0: - if (sendmails and machine_liste != [] or DEBUG) and (ttl >= (delai - 1)*86400 or 0 < ttl <= 86400): - # On lui envoie un mail pour le prévenir - to = clandestin['mail'][0] - mail = mail_module.generate('demenagement', {"from" : "respbats@crans.org", - "chambre" : exchambre, - "jours" : int(ttl/86400) + 1, - "to" : to, - "prenom" : clandestin["prenom"][0], - "nom" : clandestin["nom"][0], - "lang_info":"English version below"} - ).as_string() - if DEBUG: - print mail - mailer = os.popen("/usr/sbin/sendmail -t", "w") - mailer.write(mail + "\n.") - mailer.close() +def warn_or_delete(smtp, clandestin, fail, done): + """Avertit l'adhérent ou supprime ses machines si nécessaire""" + date, exchambre = get_kickout(clandestin) + + # Date de suppression prévue + date_suppr = date + delai*DAY - else: - for m in machine_liste: + if date_suppr < now: # A expiré + reason = u'Adhérent sans chambre valide depuis %d jours' % delai + for m in clandestin.machines(mode='rw'): try: - reason = u'Adhérent sans chambre valide depuis %d jours' % delai - with conn.search(u'mid=%s' % m['mid'][0], mode='w')[0] as m2: - m2.delete(reason) - to_print.append( (clandestin['aid'][0], m['ipHostNumber'][0], m['mid'][0], m['host'][0]) ) + with m: + m.delete(reason) + done.append(m) except Exception as e: - to_error.append((clandestin['aid'][0], m['ipHostNumber'][0], m['mid'][0], m['host'][0], e)) + fail.append((m, e)) + elif date_suppr < now + DAY or now - date < DAY: + # Expire dans un jour ou vient tout juste de se faire virer + mail_addr = clandestin.get_mail() + if not clandestin.machines() or not mail_addr: + return # Si pas de machine, on s'en fout. Si pas de mail, inutile + data = { + "from" : RESP, + "chambre" : exchambre, + "jours" : (date_suppr - now).days+1, + "to" : mail_addr, + "adh": clandestin, + "lang_info": "English version below", + } + mail = mail_module.generate('demenagement', data) + smtp.sendmail(RESP, [mail_addr], mail.as_string()) -message = u"" -if to_print != []: - # Il s'est passé quelque chose, donc on envoie un mail - # On regarde le plus grand hostname - hostnamemaxsize = max([len(str(i[3])) for i in to_print]) - template = u"| %%4s | %%-15s | %%4s | %%-%ss |\n" % (hostnamemaxsize) - message += u"\nListe des machines supprimées pour chambre invalide depuis plus de %s jours :\n" % delai - tiret_line = u"+------+-----------------+------+-%s-+\n" % ("-" * hostnamemaxsize) - message += tiret_line - message += template % ("aid", " ip", "mid", (" " * (max((hostnamemaxsize-8)/2,0)) + "hostname")) - message += tiret_line - for aid, ip, mid, hostname in to_print: - message += template % (aid, ip, mid, hostname) +def format_entry(m): + """Renvoie une ligne de tableau, pour une machine""" + return [ + unicode(m['host'][0]), + unicode(' '.join(m.dn.split(',')[:2])), + ] - message += tiret_line - message += u"\nScore de cette nuit : %s" % (len(to_print)) - -if to_error != []: - hostnamemaxsize = max([len(str(i[3])) for i in to_error]) - errormaxsize = max([len(str(i[4])) for i in to_error]) - template = u"| %%4s | %%-15s | %%4s | %%-%ss | %%-%ss |\n" % (hostnamemaxsize, errormaxsize) - message += u"\n" - tiret_line = u"+------+-----------------+------+-%s-+-%s-+\n" % ("-" * hostnamemaxsize, "-" * errormaxsize) - message += u"\nListe des machines dont la supression à échoué :\n" - message += tiret_line - message += template % ("aid", " ip", "mid", (" " * (max((hostnamemaxsize-8)/2,0)) + "hostname"), (" " * (max((errormaxsize-6)/2,0)) + "erreur")) - for aid, ip, mid, hostname, error in to_error: - message += template % (aid, ip, mid, hostname, error) - message += tiret_line - -if to_print != [] or to_error != []: - headers = u"From: respbats@crans.org\nSubject: %s\n" % Header("Machines supprimées pour chambre invalide", "utf8").encode() - headers += u"Content-Type: text/plain; charset=UTF-8\n" - headers += u"X-Mailer: /usr/scripts/gestion/chambres_vides.py\n" - headers += u"To: respbats@crans.org\n" - mail = headers + "\n" + message - if sendmails: - mailer = os.popen("/usr/sbin/sendmail -t", "w") - mailer.write(mail.encode("utf-8") + "\n.") - mailer.close() +def spaces(x): + """Ajoute des espaces en plus sur un tableau de tableau de strings""" + if type(x) == list: + return map(spaces, x) else: - print mail + return u' ' + unicode(x) + u' ' + +if __name__ == '__main__': + conn = lc_ldap.shortcuts.lc_ldap_admin() + + if periode_transitoire: + date = to_gtf(debut_periode_transitoire) + else: + date = now.strftime(FORMAT_LDAP) + 'z' + + bad_boys_e_s = conn.search(FILTER % {'date': date}) + + fail = [] + done = [] + with mail_module.ServerConnection() as smtp: + for clandestin in bad_boys_e_s: + warn_or_delete(smtp, clandestin, fail, done) + + if fail or done: + done = spaces([format_entry(m) for m in done]) + fail = spaces([format_entry(m) + [e] for (m,e) in fail]) + data = { + 'from': RESP, + 'to': RESP, + 'delai': delai, + 'fail': fail, + 'done': done, + 'done_tab': affichage.tableau( + done, + titre=[u'Hôte', u'Référence'], + alignement=['d', 'd'], + ), + 'fail_tab': affichage.tableau( + fail, + titre=[u'Hôte', u'Référence', u'Erreurs'], + alignement=['d', 'd', 'd'], + ), + } + with mail_module.ServerConnection() as smtp: + mail = mail_module.generate('demenagement_stats', data) + smtp.sendmail(RESP, [RESP], mail.as_string()) diff --git a/gestion/mail/template/demenagement/To/fr b/gestion/mail/template/demenagement/To/fr index e6199f75..fffce61e 100644 --- a/gestion/mail/template/demenagement/To/fr +++ b/gestion/mail/template/demenagement/To/fr @@ -1 +1 @@ -{{to}} +"{{adh|name}}" <{{to}}> diff --git a/gestion/mail/template/demenagement/body/en b/gestion/mail/template/demenagement/body/en index 90e1955c..21c11ad7 100644 --- a/gestion/mail/template/demenagement/body/en +++ b/gestion/mail/template/demenagement/body/en @@ -1,4 +1,4 @@ -Hi {{prenom}} {{nom}}! +Hi {{ adh|name }}! A new member of Crans has declared to live in the room {{chambre}} you were previously registered in. This means we do not have your residency information @@ -10,7 +10,7 @@ campus. If you do not wish to keep your Crans Internet access, a simple message from you is enough to delete all your computers from our database. This deletion -will automatically take place in {{jours}} day(s) if you do not answer. +will automatically take place in {{jours}} day{% if jours > 1 %}s{%endif%} if you do not answer. If you do have a Crans account, you will keep an unlimited acces to it, as well as all the associated services such as your email firstname.lastname@crans.org. diff --git a/gestion/mail/template/demenagement/body/fr b/gestion/mail/template/demenagement/body/fr index a786fae9..1c563272 100644 --- a/gestion/mail/template/demenagement/body/fr +++ b/gestion/mail/template/demenagement/body/fr @@ -1,4 +1,4 @@ -Bonjour {{prenom}} {{nom}}, +Bonjour {{ adh|name }}, Un adhérent du Crans a déclaré résider dans la chambre {{chambre}}, que tu occupais précédemment. Cela signifie que nous ne disposons @@ -10,7 +10,7 @@ si tu as quitté le campus. Si tu ne souhaites pas conserver ton accès Internet, un simple message de ta part et nous supprimerons les machines que tu possèdes de notre base de -données. Cette suppression aura automatiquement lieu dans {{jours}} jour(s) en +données. Cette suppression aura automatiquement lieu dans {{jours}} jour{% if jours > 1%}s{%endif%} en l'absence de réponse. Si tu possèdes un compte Crans, tu conserves un accès à celui-ci sans limite de