# -*- mode: python -*- # -*- coding: utf-8 -*- ############################################################################## # # Gestion scolarite IUT # # Copyright (c) 1999 - 2021 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Emmanuel Viennet emmanuel.viennet@viennet.net # ############################################################################## """Dump base de données pour debug et support technique Le principe est le suivant: 1- S'il existe une base en cours d'anonymisation, s'arrête et affiche un msg d'erreur l'utilisateur, qui peut décider de la supprimer. 2- ScoDoc lance un script qui duplique la base (la copie de SCORT devient ANORT) - (si elle existe deja, s'arrête) createdb -E UTF-8 ANORT pg_dump SCORT | psql ANORT 3- ScoDoc lance le script d'anonymisation config/anonymize_db.py qui: - vide ou anonymise certaines colonnes - dump cette base modifiée - supprime cette base. 4- La copie dump anonymisé est uploadée. """ import os import fcntl import subprocess import requests from email.MIMEMultipart import ( # pylint: disable=no-name-in-module,import-error MIMEMultipart, ) from email.MIMEText import MIMEText # pylint: disable=no-name-in-module,import-error from email.MIMEBase import MIMEBase # pylint: disable=no-name-in-module,import-error from email.Header import Header # pylint: disable=no-name-in-module,import-error from email import Encoders # pylint: disable=no-name-in-module,import-error import notesdb as ndb import sco_utils as scu from notes_log import log from sco_exceptions import ScoValueError SCO_DUMP_LOCK = "/tmp/scodump.lock" def sco_dump_and_send_db(context, REQUEST=None): """Dump base de données du département courant et l'envoie anonymisée pour debug""" H = [context.sco_header(REQUEST, page_title="Assistance technique")] # get currect (dept) DB name: cursor = ndb.SimpleQuery(context, "SELECT current_database()", {}) db_name = cursor.fetchone()[0] ano_db_name = "ANO" + db_name # Lock try: x = open(SCO_DUMP_LOCK, "w+") fcntl.flock(x, fcntl.LOCK_EX | fcntl.LOCK_NB) except (IOError, OSError): # exception changed from Python 2 to 3 raise ScoValueError( "Un envoi de la base " + db_name + " est déjà en cours, re-essayer plus tard" ) try: # Drop if exists _drop_ano_db(ano_db_name) # Duplicate database _duplicate_db(db_name, ano_db_name) # Anonymisation _anonymize_db(ano_db_name) # Send r = _send_db(context, REQUEST, ano_db_name) if ( r.status_code == requests.codes.INSUFFICIENT_STORAGE # pylint: disable=no-member ): H.append( """

Erreur: espace serveur trop plein. Merci de contacter {0}

""".format( scu.SCO_DEV_MAIL ) ) elif r.status_code == requests.codes.OK: # pylint: disable=no-member H.append("""

Opération effectuée.

""") else: H.append( """

Erreur: code {0} {1} Merci de contacter {2}

""".format( r.status_code, r.reason, scu.SCO_DEV_MAIL ) ) finally: # Drop anonymized database _drop_ano_db(ano_db_name) # Remove lock fcntl.flock(x, fcntl.LOCK_UN) log("sco_dump_and_send_db: done.") return "\n".join(H) + context.sco_footer(REQUEST) def _duplicate_db(db_name, ano_db_name): """Create new database, and copy old one into""" cmd = ["createdb", "-E", "UTF-8", ano_db_name] log("sco_dump_and_send_db/_duplicate_db: {}".format(cmd)) try: _ = subprocess.check_output(cmd) except subprocess.CalledProcessError as e: log("sco_dump_and_send_db: exception createdb {}".format(e)) raise ScoValueError( "erreur lors de la creation de la base {}".format(ano_db_name) ) cmd = "pg_dump {} | psql {}".format(db_name, ano_db_name) log("sco_dump_and_send_db/_duplicate_db: {}".format(cmd)) try: _ = subprocess.check_output(cmd, shell=1) except subprocess.CalledProcessError as e: log("sco_dump_and_send_db: exception {}".format(e)) raise ScoValueError( "erreur lors de la duplication de la base {} vers {}".format( db_name, ano_db_name ) ) def _anonymize_db(ano_db_name): """Anonymize a departement database""" cmd = os.path.join(scu.SCO_CONFIG_DIR, "anonymize_db.py") log("_anonymize_db: {}".format(cmd)) try: _ = subprocess.check_output([cmd, ano_db_name]) except subprocess.CalledProcessError as e: log("sco_dump_and_send_db: exception in anonymisation: {}".format(e)) raise ScoValueError( "erreur lors de l'anonymisation de la base {}".format(ano_db_name) ) def _get_scodoc_serial(context): try: return int(open(os.path.join(scu.SCODOC_VERSION_DIR, "scodoc.sn")).read()) except: return 0 def _send_db(context, REQUEST, ano_db_name): """Dump this (anonymized) database and send it to tech support""" log("dumping anonymized database {}".format(ano_db_name)) try: data = subprocess.check_output("pg_dump {} | gzip".format(ano_db_name), shell=1) except subprocess.CalledProcessError as e: log("sco_dump_and_send_db: exception in anonymisation: {}".format(e)) raise ScoValueError( "erreur lors de l'anonymisation de la base {}".format(ano_db_name) ) log("uploading anonymized dump...") files = {"file": (ano_db_name + ".gz", data)} r = requests.post( scu.SCO_DUMP_UP_URL, files=files, data={ "dept_name": context.get_preference("DeptName"), "serial": _get_scodoc_serial(context), "sco_user": str(REQUEST.AUTHENTICATED_USER), "sent_by": context.Users.user_info(str(REQUEST.AUTHENTICATED_USER))[ "nomcomplet" ], "sco_version": scu.SCOVERSION, "sco_subversion": scu.get_svn_version(scu.SCO_CONFIG_DIR), }, ) return r def _drop_ano_db(ano_db_name): """drop temp database if it exists""" existing_databases = [ s.split("|")[0].strip() for s in subprocess.check_output(["psql", "-l"]).split("\n")[3:] ] if ano_db_name not in existing_databases: log("_drop_ano_db: no temp db, nothing to drop") return cmd = ["dropdb", ano_db_name] log("sco_dump_and_send_db: {}".format(cmd)) try: _ = subprocess.check_output(cmd) except subprocess.CalledProcessError as e: log("sco_dump_and_send_db: exception dropdb {}".format(e)) raise ScoValueError( "erreur lors de la suppression de la base {}".format(ano_db_name) )