Script migration depuis ScoDoc 7

This commit is contained in:
Emmanuel Viennet 2021-08-18 18:53:45 +02:00
parent c8df244d4c
commit bb6dc787f2
5 changed files with 308 additions and 63 deletions

View File

@ -149,6 +149,64 @@ Pour créer un utilisateur "super admin", c'est à dire admin dans tous les dép
## Migration d'une installation ScoDoc 7 ## Migration d'une installation ScoDoc 7
La migration va se faire en suivant les étapes:
1. sauvegarder les données de ScoDoc7 (depuis le serveur de production);
2. installer le nouveau serveur Linux et ScoDoc 9;
3. y charger les données ScoDoc 7;
4. importer ces données dans ScoDoc 9.
### Étape 1: sauvegarde des données du serveur ScoDoc 7
Se connecter en tant que `root`sur le serveur ScoDoc 7.
cd /opt/scodoc/Products/ScoDoc/config
# Mise à jour indispensable pour avoir le script de migration
./upgrade.sh
# Export des données
./save_scodoc7_data.sh /tmp/sauvegarde-scodoc7
Attention à l'espace disque: au besoin, faire le ménage ou montez un disque supplémentaire.
Le script indique le nom du fichier à transférer, qui sera dans cet
exemple `/tmp/sauvegarde-scodoc7.tgz`
Copier ce fichier sur le nouveau serveur.
### Étape 2
Installer le nouveau serveur avec Debian 11 et ScoDoc 9.
### Étape 3
Charger les données ScoDoc 7: en tant qu'utilisateur "`scodoc`"
cd /opt/scodoc
tools/restore_scodoc7_data.sh /tmp/sauvegarde-scodoc7.tgz
(adaptez l'argument si les données ont été copiées ailleurs)
Note: le message
`pg_restore: warning: restoring tables WITH OIDS is not supported anymore`
est normal et anodin.
A ce stade, vous avez rechargé les bases ScoDoc 7 mais il faut encore
les convertir vers la nouvelle structure ScoDoc 9.
### Étape 4
Importer les données dasn ScoDoc 9: les formats des bases ayant changé
l'opération est complexe et peut durer plusieurs minutes (ou dizaines
de minutes).
migrate_from_scodoc7.sh /tmp/sauvegarde-scodoc7
(le script de l'étape 3 a décompressé l'archive, d'où ici l'absence de l'extension `tgz`).
###
Le script `migrate_from_scodoc7.sh` va déplacer les donneés et reconfigurer les bases de données Le script `migrate_from_scodoc7.sh` va déplacer les donneés et reconfigurer les bases de données
de votre installation ScoDoc 7 pour passer à ScoDoc 8 (*ne pas utiliser en production !*). de votre installation ScoDoc 7 pour passer à ScoDoc 8 (*ne pas utiliser en production !*).
**Les modifications effectuées sont sans retour: ScoDoc 7 ne fonctionnera plus !** **Les modifications effectuées sont sans retour: ScoDoc 7 ne fonctionnera plus !**

View File

@ -4,6 +4,7 @@
import inspect import inspect
import logging import logging
import pdb import pdb
import time
import psycopg2 import psycopg2
import sqlalchemy import sqlalchemy
@ -109,12 +110,16 @@ def import_scodoc7_dept(dept_id: str, dept_db_uri=None):
# Utilisateur de rattachement par défaut: # Utilisateur de rattachement par défaut:
default_user = get_super_admin() default_user = get_super_admin()
# #
t0 = time.time()
for (table, id_name) in SCO7_TABLES_ORDONNEES: for (table, id_name) in SCO7_TABLES_ORDONNEES:
logging.info(f"{dept.acronym}: converting {table}...") logging.info(f"{dept.acronym}: converting {table}...")
klass = get_class_for_table(table) klass = get_class_for_table(table)
t1 = time.time()
n = convert_table(dept, cursor, id_from_scodoc7, klass, id_name, default_user) n = convert_table(dept, cursor, id_from_scodoc7, klass, id_name, default_user)
logging.info(f" inserted {n} objects.") logging.info(f" inserted {n} objects in {time.time()-t1}s.")
logging.info(f"All table imported: clearing app caches...")
logging.info(f"All table imported in {time.time()-t0}s")
logging.info(f"clearing app caches...")
clear_scodoc_cache() clear_scodoc_cache()
logging.info(f"Done.") logging.info(f"Done.")
logging.warning(f"Un redémarrage du serveur postgresql est conseillé.") logging.warning(f"Un redémarrage du serveur postgresql est conseillé.")
@ -183,13 +188,11 @@ def convert_table(
is_table = True is_table = True
current_id = get_table_max_id(klass) current_id = get_table_max_id(klass)
has_id = current_id != -1 has_id = current_id != -1
cnx = db.engine.connect()
table_name = str(klass.description) table_name = str(klass.description)
boolean_columns = [] boolean_columns = []
else: else:
is_table = False is_table = False
has_id = True has_id = True
cnx = None
table_name = klass.__tablename__ table_name = klass.__tablename__
# Colonnes booléennes (valeurs à convertir depuis int) # Colonnes booléennes (valeurs à convertir depuis int)
boolean_columns = get_boolean_columns(klass) boolean_columns = get_boolean_columns(klass)
@ -200,6 +203,7 @@ def convert_table(
current_id = 0 current_id = 0
else: else:
current_id = current_id[0] current_id = current_id[0]
cnx = db.engine.connect()
# mapping: login (scodoc7) : user id (scodoc8) # mapping: login (scodoc7) : user id (scodoc8)
login2id = {u.user_name: u.id for u in User.query} login2id = {u.user_name: u.id for u in User.query}
@ -207,6 +211,7 @@ def convert_table(
cursor.execute(f"SELECT * FROM {table_name}") cursor.execute(f"SELECT * FROM {table_name}")
objects = cursor.dictfetchall() objects = cursor.dictfetchall()
n = 0
for obj in objects: for obj in objects:
current_id += 1 current_id += 1
convert_object( convert_object(
@ -223,6 +228,11 @@ def convert_table(
default_user, default_user,
cnx, cnx,
) )
# commit progressif pour ne pas consommer trop de mémoire:
n += 1
if (not n % 1000) and cnx:
db.session.commit()
if cnx: if cnx:
cnx.close() cnx.close()
@ -244,7 +254,7 @@ def convert_object(
boolean_columns=None, boolean_columns=None,
login2id=None, login2id=None,
default_user=None, default_user=None,
cnx=None, cnx=None, # cnx à la base destination
): ):
# Supprime l'id ScoDoc7 (eg "formsemestre_id") qui deviendra "id" # Supprime l'id ScoDoc7 (eg "formsemestre_id") qui deviendra "id"
if id_name: if id_name:
@ -329,8 +339,9 @@ def convert_object(
statement = sqlalchemy.insert(klass).values(**obj) statement = sqlalchemy.insert(klass).values(**obj)
_ = cnx.execute(statement) _ = cnx.execute(statement)
else: else:
new_obj = klass(**obj) # ORM object # new_obj = klass(**obj) # ORM object
db.session.add(new_obj) # db.session.add(new_obj)
insert_object(cnx, table_name, obj)
# Stocke l'id pour les références (foreign keys): # Stocke l'id pour les références (foreign keys):
if id_name and has_id: if id_name and has_id:
@ -340,6 +351,14 @@ def convert_object(
id_from_scodoc7[old_id] = new_id id_from_scodoc7[old_id] = new_id
def insert_object(cnx, table_name: str, vals: dict) -> str:
"""insert tuple in db"""
cols = list(vals.keys())
colnames = ",".join(cols)
fmt = ",".join(["%%(%s)s" % col for col in cols])
cnx.execute("insert into %s (%s) values (%s)" % (table_name, colnames, fmt), vals)
# tables ordonnées topologiquement pour les clés étrangères: # tables ordonnées topologiquement pour les clés étrangères:
# g = nx.read_adjlist("misc/model-scodoc7.csv", create_using=nx.DiGraph,delimiter=";") # g = nx.read_adjlist("misc/model-scodoc7.csv", create_using=nx.DiGraph,delimiter=";")
# L = list(reversed(list(nx.topological_sort(g)))) # L = list(reversed(list(nx.topological_sort(g))))

View File

@ -1,25 +1,13 @@
#!/bin/bash #!/bin/bash
# Migre une install ScoDoc 7 vers ScoDoc 8 # Migre une install ScoDoc 7 vers ScoDoc 9
# Le ScoDoc7 est supposé être dans /opt/scodoc7/Products/ScoDoc # Les données ScoDoc7 sauvegardées par save_scodoc7_data.sh
# Le nouveau est /opt/scodoc/ # sont copiés au bon endroit
# puis les bases SQL ScoDoc 7 sont traduites dans la base ScoDoc 9
# #
# L'install ScoDoc7 de départ doit impérativement être à jour (upgrade.sh). # Fichiers de données et config locale:
#
# 1- Utilisateur Unix:
# scodoc7 tournait comme www-data
# scodoc >= 8 tourne sous l'utilisateur unix scodoc
#
# 2- Utilisateur postgresql (bases de données)
# scodoc7 les bases appartenaient à "www-data"
# s'assure que les bases de département (trouvées dans config/dept/*.cfg)
# appartiennent à "scodoc"
# idem pour la base SCOUSERS (avant sa migration vers SCO8USERS)
#
# 3- Fichiers de données et config locale:
# archives, photos: /opt/scodoc/var/ => /opt/scodoc-data # archives, photos: /opt/scodoc/var/ => /opt/scodoc-data
# #
# 4- TODO migrer de Apache à nginx, scripts service systemd
# #
set -euo pipefail set -euo pipefail
@ -30,14 +18,39 @@ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source "$SCRIPT_DIR/config.sh" source "$SCRIPT_DIR/config.sh"
source "$SCRIPT_DIR/utils.sh" source "$SCRIPT_DIR/utils.sh"
check_uid_root cd "$SCODOC_DIR" || die "ScoDoc 9 non installe"
source "venv/bin/activate"
SCODOC7_HOME="/opt/scodoc7" # Ce script doit tourner comme "root"
SCODOC7_DIR="${SCODOC7_HOME}/Products/ScoDoc" check_uid_root "$0"
# En principe, l'utilisateur "scodoc" existe déjà, mais au cas où on vérifie:
check_create_scodoc_user
# Usage
if [ ! $# -eq 1 ]
then
echo "Usage: $0 directory"
exit 1
fi
SCODOC7_HOME="$1" # racine de l'archive importée
#SCODOC7_HOME="/opt/scodoc7"
#SCODOC7_DIR="${SCODOC7_HOME}/Products/ScoDoc"
# if [ "${SRC##*.}" == "tgz" ]
# then
# if [ -e ${SRC%%.*} ]
# then
# echo "Error: ${SRC} is an archive but ${SRC%%.*} already exists"
# exit 2
# fi
# echo "extracting archive ${SRC}..."
# tar xvfz ...
# --- 2. Propriétaire des bases de données # --- 2. Propriétaire des bases de données
# Bases appartenant à www-data: # Bases appartenant à www-data:
# inutile si on importe via pg_restore (voir restore-scodoc7_data.sh)
migrate_database_ownership() { migrate_database_ownership() {
SCO7_BASES=$(su -c "psql -l -t | grep www-data" "$POSTGRES_SUPERUSER" | awk -F '|' '{print $1}') SCO7_BASES=$(su -c "psql -l -t | grep www-data" "$POSTGRES_SUPERUSER" | awk -F '|' '{print $1}')
for base in $SCO7_BASES for base in $SCO7_BASES
@ -49,6 +62,7 @@ migrate_database_ownership() {
# --- 3. Fichiers locaux: /opt/scodoc/var => /opt/scodoc-data # --- 3. Fichiers locaux: /opt/scodoc/var => /opt/scodoc-data
migrate_local_files() { migrate_local_files() {
echo "Déplacement des fichiers de configuration et des archives"
SCODOC_VAR_DIR_BACKUP="$SCODOC_VAR_DIR".bak SCODOC_VAR_DIR_BACKUP="$SCODOC_VAR_DIR".bak
if [ -e "$SCODOC_VAR_DIR_BACKUP" ] if [ -e "$SCODOC_VAR_DIR_BACKUP" ]
then then
@ -59,12 +73,19 @@ migrate_local_files() {
echo "renomme $SCODOC_VAR_DIR en $SCODOC_VAR_DIR_BACKUP" echo "renomme $SCODOC_VAR_DIR en $SCODOC_VAR_DIR_BACKUP"
mv "$SCODOC_VAR_DIR" "$SCODOC_VAR_DIR_BACKUP" mv "$SCODOC_VAR_DIR" "$SCODOC_VAR_DIR_BACKUP"
fi fi
mkdir "$SCODOC_VAR_DIR" || die "erreur creation repertoire"
mv "${SCODOC7_HOME}"/var/scodoc/* "$SCODOC_VAR_DIR" || die "migrate_local_files failed" mv "${SCODOC7_HOME}"/var/scodoc/* "$SCODOC_VAR_DIR" || die "migrate_local_files failed"
# Anciens logs ScoDoc7
old_logs_dest="$SCODOC_VAR_DIR/log/scodoc7"
echo "Copie des anciens logs ScoDoc 7 dans $old_logs_dest"
mkdir -p "$old_logs_dest" || die "erreur creation $old_logs_dest"
mv "${SCODOC7_HOME}"/log/* "$old_logs_dest" || die "erreur mv"
# Templates locaux poursuites etudes # Templates locaux poursuites etudes
if [ -e "${SCODOC7_DIR}"/config/doc_poursuites_etudes/local ] if [ -e "${SCODOC7_HOME}"/config/doc_poursuites_etudes/local ]
then then
mv "${SCODOC7_DIR}"/config/doc_poursuites_etudes/local "$SCODOC_VAR_DIR"/config/doc_poursuites_etudes || die "migrate_local_files failed to migrate doc_poursuites_etudes/local" mv "${SCODOC7_HOME}"/config/doc_poursuites_etudes/local "$SCODOC_VAR_DIR"/config/doc_poursuites_etudes || die "migrate_local_files failed to migrate doc_poursuites_etudes/local"
fi fi
# S'assure que le propriétaire est "scodoc": # S'assure que le propriétaire est "scodoc":
chown -R "${SCODOC_USER}:${SCODOC_GROUP}" "${SCODOC_VAR_DIR}" || die "change_scodoc_file_ownership failed on ${SCODOC_VAR_DIR}" chown -R "${SCODOC_USER}:${SCODOC_GROUP}" "${SCODOC_VAR_DIR}" || die "change_scodoc_file_ownership failed on ${SCODOC_VAR_DIR}"
@ -73,52 +94,31 @@ migrate_local_files() {
# ------ MAIN # ------ MAIN
check_create_scodoc_user change_scodoc_file_ownership
migrate_local_files
set_scodoc_var_dir
echo echo
echo -n "Changer si nécessaire le propriétaire des fichiers de $SCODOC_DIR ? (y/n) [y] " echo "Les fichiers locaux de ScoDoc: configuration, photos, procès-verbaux..."
read -r ans
if [ "$(norm_ans "$ans")" != 'N' ]
then
change_scodoc_file_ownership
fi
echo
echo -n "Changer si nécessaire le propriétaire des bases de données SQL ? (y/n) [y] "
read -r ans
if [ "$(norm_ans "$ans")" != 'N' ]
then
migrate_database_ownership
fi
echo
echo "Les fichiers locaux de ScoDoc (configuration, photos, procès-verbaux...)"
echo "sont maintenant stockées dans $SCODOC_VAR_DIR" echo "sont maintenant stockées dans $SCODOC_VAR_DIR"
echo -n "Copier les fichiers locaux de ScoDoc7 ? (y/n) [y] " echo
read -r ans
if [ "$(norm_ans "$ans")" != 'N' ] echo "XXX STOPPING XXX"
then exit 0
migrate_local_files
else
echo -n "Vérifier et créer si besoin le répertoire local /opt/scodoc-data ?"
read -r ans
if [ "$(norm_ans "$ans")" != 'N' ]
then
set_scodoc_var_dir
fi
fi
# ----- Migration base utilisateurs # ----- Migration base utilisateurs
echo echo
echo "-------------------------------------------------------------" echo "-------------------------------------------------------------"
echo "Importation des utilisateurs de ScoDoc7 dans ScoDoc8 " echo "Importation des utilisateurs de ScoDoc7 dans ScoDoc 9 "
echo "(la base SCOUSERS de ScoDoc7 sera laissée inchangée)" echo "(la base SCOUSERS de ScoDoc7 sera laissée inchangée)"
echo "(les utilisateurs ScoDoc8 existants seront laissés inchangés)" echo "(les utilisateurs ScoDoc 9 existants seront laissés inchangés)"
echo "-------------------------------------------------------------" echo "-------------------------------------------------------------"
echo echo
su -c "(cd $SCODOC_DIR && flask import-scodoc7-users)" "$SCODOC_USER" su -c "(cd $SCODOC_DIR && flask import-scodoc7-users)" "$SCODOC_USER"
# ----- Migration bases départements # ----- Migration bases départements
# les départements ScoDoc7 ont été déplacés dans /opt/scodoc-data/config/dept # les départements ScoDoc7 ont été déplacés dans /opt/scodoc-data/config/dept
for f in "$SCODOC_VAR_DIR"/config/depts/*.cfg for f in "$SCODOC_VAR_DIR"/config/depts/*.cfg
@ -128,7 +128,7 @@ do
echo "----------------------------------------------" echo "----------------------------------------------"
echo "| MIGRATION DU DEPARTEMENT $dept" echo "| MIGRATION DU DEPARTEMENT $dept"
echo "----------------------------------------------" echo "----------------------------------------------"
su -c "(cd $SCODOC_DIR && flask import-scodoc7-dept)" "$dept" su -c "(cd $SCODOC_DIR && flask import-scodoc7-dept $dept)" "$SCODOC_USER"
echo "restarting postgresql server..." echo "restarting postgresql server..."
systemctl restart postgresql systemctl restart postgresql
done done

98
tools/restore_scodoc7_data.sh Executable file
View File

@ -0,0 +1,98 @@
#!/bin/bash
#
# ScoDoc: restore data (saved by save_scodoc7_data)
# into current server
#
# Utile pour migrer de ScoDoc 7 à ScoDoc 9, d'un serveur à un autre
# A executer en tant que root sur le nouveau serveur
# Ce script va créer les base postgresql "scodoc7" (SCOUSERS, SCODEPTs...)
# afin que ScoDoc 9 puisse les importer avec ses scripts de migration.
# Les données (photos, etc) sont pas touchées et seront importées par
# la migration.
#
# E. Viennet, Sept 2011, Nov 2013, Mar 2017, Aug 2020, Jul 2021, Août 21
#
# Le répertoire de ce script:
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source "$SCRIPT_DIR/config.sh"
source "$SCRIPT_DIR/utils.sh"
if [ "$(id -nu)" != "$SCODOC_USER" ]
then
echo "Erreur: le script $0 doit être lancé par l'utilisateur $SCODOC_USER"
fi
# Usage
if [ ! $# -eq 1 ]
then
echo "Usage: $0 directory_or_archive"
exit 1
fi
SRC=$1
if [ "${SRC:0:1}" != "/" ]
then
echo "Usage: $0 directory_or_archive"
echo "Erreur: utiliser un chemin absolu (commencant par /)"
exit 1
fi
# Safety check
echo "Ce script recharge les donnees de votre installation ScoDoc 7"
echo "sur ce serveur pour migration vers ScoDoc 9."
echo "Ce fichier doit avoir ete cree par le script save_scodoc_data.sh, sur une autre machine ScoDoc7."
echo
echo -n "Voulez vous poursuivre cette operation ? (y/n) [n]"
read -r ans
if [ ! "$(norm_ans "$ans")" = 'Y' ]
then
echo "Annulation"
exit 1
fi
SCOBASES=$(psql -l | grep SCO | grep -v "$SCODOC_DB_PROD" | grep -v "$SCODOC_DB_DEV" | grep -v "$SCODOC_DB_TEST")
if [ -n "$SCOBASES" ]
then
echo "Attention: vous avez apparemment déjà des bases ScoDoc7 chargées"
echo "$SCOBASES"
echo
echo -n "poursuivre quand même ? (y/n) [n]"
read -r ans
if [ ! "$(norm_ans "$ans")" = 'Y' ]
then
echo "Annulation"
exit 1
fi
fi
# Source directory
if [ "${SRC##*.}" = 'tgz' ]
then
echo "Opening tgz archive..."
tmp=$(mktemp -d)
chmod a+rx "$tmp"
cd "$tmp" || terminate "directory error"
tar xfz "$SRC"
SRC=$(ls -1d "$tmp"/*)
fi
echo "Source is $SRC"
# Load postgresql dumps
for f in "$SRC"/SCO*.dump
do
echo "Loading postgres database from $f"
pg_restore --create -d SCODOC --no-owner "$f"
if [ ! "$?" -eq 0 ]
then
printf "Error restoring postgresql database\nPlease check that SQL server is running\nAborting."
exit 1
fi
done
#

70
tools/save_scodoc7_data.sh Executable file
View File

@ -0,0 +1,70 @@
#!/bin/bash
#
# ScoDoc: save all user data (database, configs, images, archives...) in separate directory
#
# Utile pour migrer ScoDoc d'un serveur a un autre
# Executer en tant que root sur le serveur d'origine
#
# E. Viennet, Sept 2011, Aug 2020, Jul 2021
#
# Le répertoire de ce script:
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source "$SCRIPT_DIR/config.sh"
source "$SCRIPT_DIR/utils.sh"
check_uid_root "$0"
# Destination directory
if [ ! $# -eq 1 ]
then
echo "Usage: $0 destination_directory"
exit 1
fi
DEST=$1
# remove trailing slashs if needed:
shopt -s extglob
DEST="${DEST%%+(/)}"
if [ ! -e "$DEST" ]
then
echo Creating directory "$DEST"
mkdir "$DEST"
else
echo "Error: Directory " "$DEST" " exists"
echo "remove it or specify another destination !"
exit 2
fi
echo "Stopping ScoDoc..."
scodocctl stop
# Dump all postgres databases
echo "Dumping SQL database..."
chown postgres "$DEST"
su -c "pg_dumpall > \"$DEST\"/scodoc.dump.txt" postgres
if [ ! "$?" -eq 0 ]
then
printf "Error dumping postgresql database\nPlease check that SQL server is running\nAborting.\n"
exit 1
fi
chown root "$DEST"
# ScoDoc archives, configuration, photos, etc.
echo "Copying var/ ..."
cp -rp "$SCODOC_DIR/var" "$DEST"
echo "Copying server logs..."
cp -rp "$SCODOC_DIR/log" "$DEST"
# --- Archive all files in a tarball to ease transfer
echo
echo "Archiving backup files in a $DEST.tgz..."
base=$(basename "$DEST")
(cd "$DEST"/.. || terminate "directory error"; tar cfz "$DEST".tgz "$base")
echo "Done (you can copy " "$DEST"".tgz to destination machine)."