#!/bin/bash base_chemin_mv="/tmp/ramdisk/qemu-adm" bin_qemu="/usr/bin/qemu-system-x86_64" bin_qemu_img="/usr/bin/qemu-img" nom_script=$(basename $0) die() { echo -e "\033[0;31m${1}\033[0m" 1>&2; exit ${2}; } verifMVExiste() { if [ -e "${base_chemin_mv}/${nom_mv}" ]; then die "Le répertoire de la machine virtuelle ${base_chemin_mv}/${nom_mv} existe déjà." 4 fi } nouvMV() { local l_option=${1} echo "l_option=${l_option}" # Debogage shift case "${l_option}" in "nom") echo "-name ${1} \\" > ${chemin_mv} ;; "cpu") echo "-cpu ${1} -smp cores=${2},theads=${3},sockets=1 \\" >> ${chemin_mv} ;; "memoire") echo "-m ${1} \\" >> ${chemin_mv} ;; "bios") case "${1}" in "OVMF") cp /usr/share/ovmf/OVMF.fd ${base_chemin_mv}/OVMF.fd echo "-drive if=pflash,format=raw,file=${base_chemin_mv}/OVMF.fd \\" >> ${chemin_mv} ;; esac ;; "disque") echo "${2}" if [[ ! -z ${2} ]]; then if [[ ! -f ${bin_qemu_img} ]]; then die "Le paquet qemu-utils n'est pas installé." 6 fi echo "Création du disque..." # Debogage qemu-img create -f ${2} ${1} ${3} fi echo "-drive file=${1},if=virtio \\" >> ${chemin_mv} ;; "iso") echo "--cdrom ${1} \\" >> ${chemin_mv} ;; "reseau") if [[ "${4}" != "false" ]]; then echo "#PONT=${4}" >> ${chemin_mv} fi case "${6}" in "a") echo "#VLAN=${7}" >> ${chemin_mv} ;; "t") echo "#8021Q=${7}" >> ${chemin_mv} ;; esac echo "-device ${2},netdev=${1} -netdev tap,id=${1},ifname=${1},script=no,downscript=no \\" >> ${chemin_mv} ;; "parametres") echo "$@ \\" >> ${chemin_mv} ;; esac } execCmd() { #verifEnv for ((id_commande=0 ; "${#commandes_a_executer[*]}" - "${id_commande}"; id_commande++)); do ${commandes_a_executer["${id_commande}"]} done echo "########## FICHIER ##########" # Debogage cat ${chemin_mv} # Debogage } confInteractive() { echo "confInteractive" # Debogage } presenceParametre() { echo "presenceParametre" # Debogage local l_nombre_arguments="${1}" local l_nom_option="${2}" local l_nombre_parametres=${l_nombre_arguments}-1 # On soustrait l'option du nombre d'arguments resultant le nombre de parametres if [[ "${l_nombre_parametres}" -eq 0 ]]; then die "L'option ${l_nom_option} n'a pas de paramètre." 5 fi } creeMV() { nom_mv="$(basename ${1})" if [[ "$(dirname ${1})" != "." ]];then base_chemin_mv="$(dirname ${1})" fi chemin_mv="${base_chemin_mv}/${nom_mv}/qemu-${nom_mv}.mv" echo "nom_mv=${nom_mv}" # Debogage echo "base_chemin_mv=${base_chemin_mv}" # Debogage commandes_a_executer+=("nouvMV nom ${nom_mv}") shift # On retire le nom de la machine virtuelle passé en argument echo 1 "$#" # Debogage while [[ $# -gt 0 ]]; do case "${1}" in "-c") # Cpu (requis) echo "cpu" # Debogage parametre="${2}" echo "cpu parametre=${parametre}" # Debogage presenceParametre "$#" "$1" shift if [[ ${temoin_c} -ne 1 ]]; then # :: cpu_type=$( echo ${1} | awk -F: '{print $1}' ) echo "${cpu_type}" # Debogage if [[ -z "${cpu_type}" ]]; then die "Le type de CPU n'est pas renseigné.\nSyntaxe attendue : ::." 5 fi cpu_nombre_de_coeurs=$( echo ${1} | awk -F: '{print $2}' ) echo "${cpu_nombre_de_coeurs}" # Debogage if [[ -z "${cpu_nombre_de_coeurs}" ]]; then die "Le nombre de cœurs n'est pas renseigné.\nSyntaxe attendue : ::." 5 fi cpu_nombre_de_files_d_execution=$( echo ${1} | awk -F: '{print $3}' ) echo "${cpu_nombre_de_files_d_execution}" # Debogage if [[ -z "${cpu_nombre_de_files_d_execution}" ]]; then die "Le nombre de files d'execution n'est pas renseigné.\nSyntaxe attendue : ::." 5 fi commandes_a_executer+=("nouvMV cpu ${cpu_type} ${cpu_nombre_de_coeurs} ${cpu_nombre_de_files_d_execution}") temoin_c=1 else die "L'option ${1} a déjà été renseignée." 3 fi ;; "-m") # Memoire (requis) echo "memoire" # Debogage parametre="${2}" echo "memoire parametre=${parametre}" # Debogage presenceParametre "$#" "$1" shift if [[ ${temoin_m} -ne 1 ]]; then commandes_a_executer+=( "nouvMV memoire ${1}") temoin_m=1 else die "L'option ${1} a déjà été renseignée." 3 fi ;; "-b") # Bios echo "bios" # Debogage parametre="${2}" echo "bios parametre=${parametre}" # Debogage presenceParametre "$#" "$1" shift if [[ ${temoin_b} -ne 1 ]]; then case ${1} in "ovmf"|"OVMF") if [[ ! -f "/usr/share/ovmf/OVMF.fd" ]]; then die "Le paquet ovmf n'est pas installé." 6 fi commandes_a_executer+=( "nouvMV bios $(echo ${1} | tr '[:lower:]' '[:upper:]')") ;; esac temoin_b=1 else die "L'option ${1} a déjà été renseignée." 3 fi ;; "-d") # Disque echo "disque" # Debogage parametre="${2}" presenceParametre "$#" "$1" echo "disque parametre=${parametre}" # Debogage shift # |(|):(raw|qcow2):) disque_nom=$( echo ${1} | awk -F: '{print $1}' ) echo "disque_nom=${disque_nom}" # Debogage if [[ -z "${disque_nom}" ]]; then die "Le disque n'est pas renseigné.\nSyntaxe attendue : |(|):(raw|qcow2):)." 5 fi if [[ "$(dirname ${1})" == "." ]];then disque_nom="${base_chemin_mv}/${nom_mv}/${disque_nom}" fi disque_format=$( echo ${1} | awk -F: '{print $2}' ) echo "disque_format=${disque_format}" # Debogage if [[ -z "${disque_format}" && "$(dirname ${1})" == "." ]]; then die "Le format du disque n'est pas renseigné.\nSyntaxe attendue : |(|):(raw|qcow2):)." 5 fi disque_capacite=$( echo ${1} | awk -F: '{print $3}' ) echo "disque_capacite=${disque_capacite}" # Debogage if [[ -z "${disque_capacite}" && "$(dirname ${1})" == "." ]]; then die "La capacité du disque n'est pas renseigné.\nSyntaxe attendue : |(|):(raw|qcow2):)." 5 fi commandes_a_executer+=( "nouvMV disque ${disque_nom} ${disque_format} ${disque_capacite}") ;; "-i") # Iso echo "iso" # Debogage parametre="${2}" presenceParametre "$#" "$1" echo "iso parametre=${parametre}" # Debogage shift commandes_a_executer+=( "nouvMV iso ${1}") ;; '-r') # Reseau echo "reseau" # Debogage parametre="${2}" presenceParametre "$#" "$1" echo "reseau parametre=${parametre}" # Debogage shift # Interface réseau simple : ## :(virtio|e1000): # :(virtio|e1000):n # # Interface réseau dans un pont ## :(virtio|e1000):o:: # :(virtio|e1000):o::n # # Interface réseau dans un pont avec gestion des VLAN # :(virtio|e1000):o::o:(t:|a:) # :(virtio|e1000):(n|o::(n|o:(t:|a:))) # Initialisation des variables reseau_nom_interface=false reseau_type=false reseau_activ_pont=false reseau_nom_pont=false reseau_activ_8021q=false reseau_mode_8021q=false reseau_vid=false reseau_nom_interface=$( echo ${1} | awk -F: '{print $1}' ) if [[ -z "${reseau_nom_interface}" ]]; then die "L'interface réseau n'est pas renseigné.\nSyntaxe minimum attendue : :(virtio|e1000):n." 5 fi reseau_type=$( echo ${1} | awk -F: '{print $2}' ) if [[ -z "${reseau_type}" ]]; then die "Le type d'interface réseau n'est pas renseigné.\nSyntaxe minimum attendue : :(virtio|e1000):n." 5 fi case ${reseau_type} in "virtio") reseau_type="virtio-net-pci" ;; "e1000") ;; *) die "Le type d'interface réseau n'a pas une valeur connue.\nSyntaxe minimum attendue : :(virtio|e1000):n." 5 ;; esac reseau_activ_pont=$( echo ${1} | awk -F: '{print $3}' ) echo "reseau_activ_pont=${reseau_activ_pont}" # Debogage if [[ -z "${reseau_activ_pont}" ]]; then die "L'activation du pont n'est pas renseigné.\nVoici les deux syntaxes minimum posisbles : \n\tSans pont :\n\t\t:(virtio|e1000):n\n\tAvec pont :\n\t\t:(virtio|e1000):o::n" 5 fi case ${reseau_activ_pont} in "n"|"N") ;; "o"|"O") reseau_nom_pont=$( echo ${1} | awk -F: '{print $4}' ) if [[ -z "${reseau_nom_pont}" ]]; then die "Le nom du pont réseau n'est pas renseigné.\nSyntaxe minimum attendue : :(virtio|e1000):o::n." 5 fi reseau_activ_8021q=$( echo ${1} | awk -F: '{print $5}' ) if [[ -z "${reseau_activ_8021q}" ]]; then die "L'activation du VLAN n'est pas renseigné.\nVoici les deux syntaxes minimum posisbles :\n\tSans VLAN :\n\t\t:(virtio|e1000):o::n\n\tAvec VLAN :\n\t\t:(virtio|e1000):o::o:(t:|a:)" 5 fi case ${reseau_activ_8021q} in "n"|"N") ;; "o"|"O") reseau_mode_8021q=$( echo ${1} | awk -F: '{print $6}' ) if [[ -z "${reseau_mode_8021q}" ]]; then die "Le mode du 802.1Q n'est pas renseigné.\nSyntaxe attendue : :(virtio|e1000):o::o:(t:|a:)" 5 fi case ${reseau_mode_8021q} in "t"|"T") reseau_vid=$( echo ${1} | awk -F: '{print $7}' ) echo "reseau_vid=${reseau_vid}" # Debogage if [[ -z "${reseau_vid}" ]]; then die "Les identifiants de VLAN ne sont pas renseignés.\nSyntaxe attendue : :(virtio|e1000):o::o:(t:|a:).\nExemple pour un accès via trunk aux VLAN 10 et 20 :\n\t qemu-adm [...] -r tap0:virtio:o:br0:o:t:10,20" 5 fi for vid in $(echo $reseau_vid | sed 's/,/ /g'); do echo "vid=${vid}" # Debogage if [[ !(${vid} -ge 1 && ${vid} -le 4096) ]]; then die "${vid} n'est pas une valeur correcte. L'attendu est un ou plusieurs chiffres comprise entre 1 et 4096 séparé par une virgule.\nExemple pour un accès via trunk aux VLAN 10 et 20 :\n\t qemu-adm [...] -r tap0:virtio:o:br0:o:t:10,20" fi done ;; "a"|"A") reseau_vid=$( echo ${1} | awk -F: '{print $7}' ) if [[ -z "${reseau_vid}" ]]; then die "L'identifiant de VLAN n'est pas renseignés.\nSyntaxe attendue : :(virtio|e1000):o::o:(t:|a:).\nExemple pour un accès au VLAN 10 :\n\t qemu-adm [...] -r tap0:virtio:o:br0:o:a:10" 5 fi if [[ ! "${reseau_vid}" =~ ^[0-9]+$ ]]; then die "${reseau_vid} n'est pas une valeur correcte. L'attendu est un chiffre compris entre 1 et 4096.\nExemple pour un accès au VLAN 10 :\n\t qemu-adm [...] -r tap0:virtio:o:br0:o:a:10" fi if [[ !(${reseau_vid} -ge 1 && ${reseau_vid} -le 4096) ]]; then die "${reseau_vid} n'est pas une valeur correcte. L'attendu est un chiffre compris entre 1 et 4096.\nExemple pour un accès au VLAN 10 :\n\t qemu-adm [...] -r tap0:virtio:o:br0:o:a:10" fi ;; *) die "${reseau_mode_8021q} n'est pas une valeur connue.\nSyntaxe attendue : :(virtio|e1000):o::o:(t:|a:)\n" ;; esac ;; *) die "${reseau_activ_8021q} n'est pas une valeur connue.\nVoici les deux syntaxes minimum posisbles : \n\tSans VLAN :\n\t\t:(virtio|e1000):o::n\n\tAvec VLAN :\n\t\t:(virtio|e1000):o::o:(t:|a:)" 5 ;; esac ;; *) die "${reseau_activ_pont} n'est pas une valeur connue.\nVoici les deux syntaxes minimum posisbles : \n\tSans pont :\n\t\t:(virtio|e1000):n\n\tAvec pont :\n\t\t:(virtio|e1000):o::n" 5 ;; esac commandes_a_executer+=( "nouvMV reseau ${reseau_nom_interface} ${reseau_type} ${reseau_activ_pont} ${reseau_nom_pont} ${reseau_activ_8021q} ${reseau_mode_8021q} ${reseau_vid}") echo "nouvMV reseau ${reseau_nom_interface} ${reseau_type} ${reseau_activ_pont} ${reseau_nom_pont} ${reseau_activ_8021q} ${reseau_mode_8021q} ${reseau_vid}" # Debogage ;; "-p") # VFIO PCI echo "pci" # Debogage ;; "-s") # Sous-volume BTRFS echo "sous-volume" # Debogage if [[ ${temoin_s} -ne 1 ]]; then #commandes_a_executer+=( "nouvMV ${nom_mv}") temoin_s=1 else die "L'option ${1} a déjà été renseignée." 3 fi ;; "-f") # Force # Permet d'eviter la demande de confirmation de création echo "force" # Debogage if [[ ${temoin_f} -ne 1 ]]; then #commandes_a_executer+=( "nouvMV ${nom_mv}") temoin_s=f else die "L'option ${1} a déjà été renseignée." 3 fi ;; '-h') # Aide usage "cree" exit 0 ;; '--') # Autre parametres (champ libre) echo "parametres" # Debogage presenceParametre "$#" "$1" shift parametres="$@" echo "parametres=${parametres}" # Debogage commandes_a_executer+=( "nouvMV parametres $parametres") shift $# ;; *) # Autre usage "cree" ;; esac shift done if [[ ${temoin_c} -ne 1 || ${temoin_m} -ne 1 ]]; then die "Les parametres -c et -m sont obligatoires." 2 fi #verifMVExiste ${nom_mv} && mkdir -p ${base_chemin_mv}/${nom_mv} execCmd } usage() { case ${1} in cree) echo "usage_cree" # Debogage # qemu-img: Invalid image size specified. You may use k, M, G, T, P or E suffixes for # qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes. die "" 0 ;; demarre) echo "usage_demarre" # Debogage die "" 0 ;; arret) echo "usage_arret" # Debogage die "" 0 ;; active) echo "usage_active" # Debogage die "" 0 ;; desactive) echo "usage_desactive" # Debogage die "" 0 ;; *) echo "$(tput bold)Utilisation : $(basename ${0}) {-i|-s N} [OPTION]...$(tput sgr0)" 2>&1 echo -e "Permet de sauvegarder les sous-volumes BTRFS d'un serveur monté selon la procédure décrite par ycharbi.fr. Permet également d'effectuer un roulement des archives.\n" echo -e "L'option -i ou -s sont requises pour l'exécution du script. Les deux peuvent êtres utilisées simultanément.\n" echo -e "\n$(tput bold)Options :$(tput sgr0)" echo -e "L'ordre des paramètres n'a pas d'importance.\n" echo -e " -i\t\tExécute un instantané des sous-volumes BTRFS répertoriés dans le fichier texte passé en paramètre (via -f ou la variable \$fichierListeSousVolumes du présent script). Les entrées de cette liste doivent êtres conformes au retour de la commande \"btrfs subvolume list /\" pour être interprétés. Toute entrée invalide est ignorée" echo -e " -s N\t\tSupprime l'instantané le plus ancien de chaque entrée de la liste. Un nombre désignant le seuil maximal d'instantané à conserver doit obligatoirement être passé en paramètre. L'instantané le plus ancien au delà de cette limite sera supprimé. Par sécurité, cette option ne supprime que le plus ancien. Si vous avez beaucoup d'instantanés au dessus de votre seuil, vous devrez répeter l'opération autant de fois que nécessaire" echo -e " -f\t\tPermet de spécifier le fichier listant les sous-volumes à traiter pour gérer les instantanés. Ce fichier doit contenir des entrées correspondants parfaitement avec celles retournées par la commande \"btrfs subvolume list /\" pour être traités. Cette option est facultative. Si elle n'est pas renseignée, le script utilisera le fichier spécifié dans la variable \$fichierListeSousVolumes" echo -e " -h\t\tAffiche cette aide. Si passée avec d'autres options, celles-ci seront ignorées pour n'afficher que l'aide" echo -e "\n$(tput bold)Exemples :$(tput sgr0)" echo -e "Réalisation d'un instantané avec un seuil de 13 archives (l'ancienne numéro 13 sera supprimée) :\n\t${0} -i -s 13 -f /root/cpt_instents/listeSousVolumes.txt\n" echo -e "Peut être raccourci en :\n\t${0} -is 13 -f /root/cpt_instents/listeSousVolumes.txt" echo -e "\nContenu du fichier listeSousVolumes.txt :" echo -e "\tsystèmes/bullseye/@\n\tsystèmes/bullseye/@boot\n\ttmp/toto\n\tvar/lib/machines/web\n\tvar/lib/machines/courriel\n\tvar/lib/machines/routeur\n\tvar/lib/machines/wireguard" echo -e "\nLe sous-volume tmp/toto n'existant pas, cette entrée sera ignorée sans entraver l'exécution du script. Il est également possible de commenter des lignes (en les rendant invalides)." die "" ;; esac } case ${1} in cree) echo "Créer" # Debogage shift # On retire l'argument "cree" if [[ "$#" -eq 0 ]]; then confInteractive creeMV #Ajouter les parametre sous forme de tableau else if [[ "${1}" == "-h" ]]; then usage "cree" fi creeMV $* fi ;; demarre) echo "Démarre" # Debogage shift # On retire l'argument "demarre" if [[ "$#" -eq 0 ]]; then usage "demarre" else if [[ "${1}" == "-h" ]]; then usage "demarre" fi nom_mv="${1}" echo "nom_mv=${nom_mv}" # Debogage shift echo 1 "$#" # Debogage while [[ $# -gt 0 ]]; do case "${1}" in '-d') #demon echo "demon" # Debogage if [[ ${temoin_d} -ne 1 ]]; then #commandes_a_executer+=( "nouvMV ${nom_mv}") temoin_d=1 else die "L'option ${1} a déjà été renseignée." 3 fi ;; '-h') #aide usage "demarre" exit 0 ;; *) #autre usage "demarre" ;; esac shift done fi ;; arret) echo "Arrêt" # Debogage shift # On retire l'argument "arret" if [[ "${1}" == "-h" ]]; then usage "arret" fi nom_mv="${1}" echo "nom_mv=${nom_mv}" ;; active) echo "Active" # Debogage shift # On retire l'argument "active" if [[ "${1}" == "-h" ]]; then usage "active" fi nom_mv="${1}" echo "nom_mv=${nom_mv}" ;; desactive) echo "Désactive" # Debogage shift # On retire l'argument "desactive" if [[ "${1}" == "-h" ]]; then usage "desactive" fi nom_mv="${1}" echo "nom_mv=${nom_mv}" ;; liste) echo "Liste" # Debogage shift # On retire l'argument "liste" ;; *) usage ;; esac