Schon seit einiger Zeit läuft hier ein Backup-Setup im Produktivbetrieb, bei dem mein Heimserver diverse Rechner im LAN mittels Bacula auf LTO-Bänder in einer Tape Library sichert. Da aber die Lüfter der Library im Betrieb einen ziemlichen Radau verursachen und die Library auch viel zu viel Strom für einen Dauerbetrieb verbraucht, habe ich nach einem Weg gesucht, die Library „passgenau“ zur Durchführung von Backup-Jobs ein- und danach wieder auszuschalten.
Hardwareseitig war das kein großes Problem: Das Ein- und Ausschalten der 230-V-Spannungsversorgung übernimmt eine „IP-Steckdose“, konkret eine Allnet ALL3075. Zum Glück kommt die Bandbibliothek – eine Overland NEO2000 – problemlos mit dem externen Abschalten der Spannung klar. Es muss lediglich sichergestellt sein, dass sich beim Ausschalten keine Bänder mehr in den Laufwerken befinden, da dies zu Problemen beim nächsten Einschalten führt. Dass die Datenverbindung zum Heimserver durch Fibre Channel realisiert ist und damit elektrisch potentialfrei ist, sorgt für zusätzliche Sicherheit vor Hardwareproblemen.
Die Software-Seite war dagegen etwas aufwendiger: Ein zeitgesteuertes Ein- und Ausschalten z.B. mittels Cronjob schied aus. Zwar starten die Backup-Jobs jeden Abend zur selben Zeit, aber die Laufdauer der Jobs ist stark unterschiedlich: an einem normalen Wochentag, wenn nur inkrementelle Jobs laufen, dauert ein kompletter Lauf weniger als eine halbe Stunde. Ein Lauf von „Full“-Jobs an jedem ersten Sonntag im Monat dauert hingegen über zwölf Stunden. Auch darauf, dass alle Jobs jedes Mal und stets in der gleichen Reihenfolge laufen, wollte ich mich nicht verlassen. Daher habe ich mir einige Shell-Skripte gebastelt, die von den Bacula-Jobs aufgerufen werden. Beim Start eines Jobs trägt das erste Skript „request_tapelibrary.sh“ eine Art Merker in eine Textdatei ein und initalisiert die Tapelibrary, falls sie nicht ohnehin schon läuft:
#!/bin/bash # # This script powers up the tapelibrary and adds a line to a semphore file # 2018-02-12 Sebastian Suchanek SEMAPHORE='/etc/tapelibrary_semaphore' TURN_ON_CMD='/usr/local/bin/all3075_switch_on' TURN_OFF_CMD='/usr/local/bin/all3075_switch_off' GET_SWITCH_STATUS_CMD='/usr/local/bin/get_status_all3075' RESET_FC_CMD='sudo /usr/local/bin/reset_fc_hba' LIBRARY_DEVICE='/dev/sg4' LIBRARY_ID_STRING='mediumx' # Print usage help print_usage() { echo "Usage: request_tapelibrary <token>" echo "" echo "<token> is a string which will be used in the semaphore file." echo " It must be unique for ever concurrent backup job." echo "" } # Do a reboot via a power cycle reboot_library() { eval "$TURN_OFF_CMD" sleep 10s eval "$TURN_ON_CMD" for i in `seq 1 180`; do echo -n "+" sleep 1s done } # Check if token was passed if [ $# -eq 0 ]; then print_usage exit 1 fi # Check if semaphore file exists and create otherwise if ! [ -f $SEMAPHORE ]; then echo "Semaphore file not found, creating $SEMAPHORE" touch $SEMAPHORE chmod 664 $SEMAPHORE else echo "Semaphore file $SEMAPHORE found." fi # Get status of power switch and turn on library if is not running already SWITCH_STATUS=`$GET_SWITCH_STATUS_CMD` if [ "$SWITCH_STATUS" -eq 0 ]; then echo "Tapelibrary is powered off, turning on." eval "$TURN_ON_CMD" else echo "Tapelibrary is already powered on." # Add line to semaphore file CUR_TIME=`date +"%F %T"` echo "$CUR_TIME - $1" >> $SEMAPHORE exit 0 fi # Add line to semaphore file CUR_TIME=`date +"%F %T"` echo "$CUR_TIME - $1" >> $SEMAPHORE # Wait grace period to allow initialisation of library echo -n "Waiting grace period to allow initialisation " for i in `seq 1 180`; do echo -n "." sleep 1s done echo "" # Check, if library is properly initialised and initialise otherwise echo "Checking availability of tapelibrary." LIB_STATUS=`lsscsi | grep -c $LIBRARY_ID_STRING` if [ "$LIB_STATUS" -eq 0 ]; then echo "Tapelibrary is not initialised yet." i=1 echo -n "Reseting Fibre Channel link " while [ `lsscsi | grep -c $LIBRARY_ID_STRING` -eq 0 ]; do if [ $((i % 6)) -eq 0 ]; then echo "Tapelibrary seems to have a problem. Doing a cold reset." reboot_library else eval "$RESET_FC_CMD" sleep 5s echo -n "." fi let "i++" done echo "" else echo "Tapelibrary is accessible." fi # Library was just powered on, so check readiness and eject cartridges if [ "$SWITCH_STATUS" -eq 0 ]; then mtx -f $LIBRARY_DEVICE status > /dev/null 2>&1 STATUS=$? if [ $STATUS != 0 ]; then echo -n "Tapelibrary seems to be still initialising. Waiting " until [ $STATUS -eq 0 ] do sleep 5 mtx -f $LIBRARY_DEVICE status > /dev/null 2>&1 STATUS=$? echo -n "." done echo " " fi DRIVESTATE=`mtx -f $LIBRARY_DEVICE status | grep "Data Transfer Element 0" | grep -c Full` if [ $DRIVESTATE -eq 1 ]; then echo "A Cartridge is still in LTO-1 drive. Ejecting..." bconsole <<END_OF_COMMANDS release storage=LTO1-Drive quit END_OF_COMMANDS fi DRIVESTATE=`mtx -f $LIBRARY_DEVICE status | grep "Data Transfer Element 1" | grep -c Full` if [ $DRIVESTATE -eq 1 ]; then echo "A Cartridge is still in LTO-4 drive. Ejecting..." bconsole <<END_OF_COMMANDS release storage=LTO4-Drive quit END_OF_COMMANDS fi fi echo "Tapelibrary is now ready for operation." |
Die drei darin unter anderem aufgerufenen Kommandos „all3075_switch_on“, „all3075_switch_off“ und „get_status_all3075“ sind jeweils kleine ebenfalls selbstgeschrieben Shell-Skripte, die im Prinzip curl-Aufrufe zum Ein- und Ausschalten sowie zum Auslesen des Schalt-Status’ der Allnet ALL3075 dienen. Das Skript „reset_fc_hba“, das, wie man unschwer am Namen erkennen kann, den Fibre-Channel-Controller resettet und ihn damit veranlasst, die Verbindungen zur Tape Library neu zu initalisieren, sieht so aus:
#!/bin/sh echo 1 > /sys/class/fc_host/host0/issue_lip echo 1 > /sys/class/fc_host/host7/issue_lip echo "- - -" > /sys/class/scsi_host/host0/scan echo "- - -" > /sys/class/scsi_host/host7/scan |
Da das Initalisieren der Tape Library nach dem Einschalten eine Weile dauert, ist es notwendig, dass man bei einem Bacula-Job durch eine entsprechend niedrigen Prioritätswert dafür sorgt, dass er sicher als erstes läuft und während dessen Laufzeit auch keine weiteren Jobs laufen. Am Ende jeden Jobs wird dann von Bacula das Skript „release_tapelibrary.sh“ aufgerufen, das „seinen“ Merker wieder aus der zentralen Textdatei löscht:
#!/bin/sh #!/bin/bash # # This script checks if all concurrent jobs on the tapelibrary are finished # and powers down the library if yes. # 2018-02-12 Sebastian Suchanek SEMAPHORE='/etc/tapelibrary_semaphore' TURN_OFF_CMD='/usr/local/bin/all3075_switch_off' GET_SWITCH_STATUS_CMD='/usr/local/bin/get_status_all3075' # Print usage help print_usage() { echo "Usage: release_tapelibrary <token>" echo "" echo "<token> is a string which will be used in the semaphore file." echo " It must be unique for ever concurrent backup job." echo "" } # Check if token was passed if [ $# -eq 0 ]; then print_usage exit 1 fi # Check if semaphore file exists and exit otherwise if ! [ -f $SEMAPHORE ]; then echo "Semaphore file not found, quitting" exit 1 else echo "Semaphore file $SEMAPHORE found." fi # Check if token is included in semaphore file COUNT_TOKEN=`grep -c "$1" $SEMAPHORE` if [ "$COUNT_TOKEN" -gt 1 ]; then echo "Token found more then once in semaphore file. Something's wrong, quitting." exit 1 fi if [ "$COUNT_TOKEN" -eq 0 ]; then echo "Token not found in semaphore file. Something's wrong, quitting." exit 1 fi # Token is valid. Remove from semaphore file TFILE=`mktemp -t libsemtemp.XXXXX` sed "/$1/d" $SEMAPHORE > "$TFILE" cat "$TFILE" > $SEMAPHORE rm $TFILE # Check if semaphore file is empty, i.e. no jobs are left if ! [ -s $SEMAPHORE ]; then echo "No jobs left to process. Tapelibrary will be powered down soon." else COUNT_JOBS=`wc -l < $SEMAPHORE` echo "Still $COUNT_JOBS job(s) left to process. Leaving tapelibrary powered on." fi |
Zusammen sieht das in einer Bacula-Job-Definition dann zum Beispiel so aus:
Job { Name = "B_TigersclawServer" Client = tigersclaw-fd Type = Backup Storage = LTO4-Drive FileSet = "Tigersclaw Server" Schedule = "WeeklyCycle" Pool = File Full Backup Pool = LTO41 Differential Backup Pool = LTO40 Incremental Backup Pool = LTO40 RunBeforeJob = "/usr/local/bin/request_tapelibrary.sh TigersclawServer" RunScript { Command = "/usr/local/bin/release_tapelibrary.sh TigersclawServer" RunsWhen = After RunsOnFailure = yes RunsOnClient = no RunsOnSuccess = yes } Messages = Standard Priority = 10 Write Bootstrap = "/var/lib/bacula/%c_%n.bsr" Maximum Concurrent Jobs = 2 } |
Schlussendlich kommt noch ein drittes Skript, „poweroff_tapelibrary.sh“, ins Spiel. Dieses wird von einem Cronjob alle fünf Minuten aufgerufen und prüft, ob aktuell Backup-Jobs laufen. Ist der letzte Job abgeschlossen (die „Merker-Datei“ ist gerade leer geworden), wartet das Skript sicherheitshalber insgesamt drei Aufrufe (entspricht 15 Minuten) ab, veranlasst dann das Auswerfen aller LTO-Bänder aus den Laufwerken der Library und schaltet die Tape Library danach ab.
#!/bin/bash # # This script checks if the semaphore file is empty and powers down # the tapelibrary after a given amount of repeats. # 2018-03-12 MAXCOUNT=3 SEMAPHORE='/etc/tapelibrary_semaphore' COUNTF='/etc/tapelibrary_count' TURN_OFF_CMD='/usr/local/bin/all3075_switch_off' GET_SWITCH_STATUS_CMD='/usr/local/bin/get_status_all3075' LIBRARY_DEVICE='/dev/sg4' # Get status of tapelibrary SWITCH_STATUS=`$GET_SWITCH_STATUS_CMD` if [ "$SWITCH_STATUS" -eq 0 ]; then # echo "Tapelibrary is currently powered down." exit 0 fi # Check if semaphore file exists and is not empty if [ -f $SEMAPHORE ] && [ -s $SEMAPHORE ]; then # echo "Semaphore file exists and is not empty. Quitting." echo "1" > $COUNTF # As a precautionary measure, reset counting file exit 0 #else # echo "Semaphore file either doesn't exist or is empty." fi # Check if counting file exists. If so, read counting value and if not, create it. if [ ! -f $COUNTF ]; then echo "1" > $COUNTF CURCOUNT=1 else CURCOUNT=`cat $COUNTF` fi #echo "$CURCOUNT. read of status." # Check if tapes are left in drives and eject them if necessary if [ "$CURCOUNT" -gt "$MAXCOUNT" ]; then echo "Maximum re-checks of $MAXCOUNT reached." DRIVESTATE=`mtx -f $LIBRARY_DEVICE status | grep "Data Transfer Element 0" | grep -c Full` if [ "$DRIVESTATE" -eq 1 ]; then echo "A Cartridge is still in LTO-1 drive. Ejecting..." bconsole <<END_OF_COMMANDS release storage=LTO1-Drive quit END_OF_COMMANDS fi DRIVESTATE=`mtx -f $LIBRARY_DEVICE status | grep "Data Transfer Element 1" | grep -c Full` if [ "$DRIVESTATE" -eq 1 ]; then echo "A Cartridge is still in LTO-4 drive. Ejecting..." bconsole <<END_OF_COMMANDS release storage=LTO4-Drive quit END_OF_COMMANDS fi echo -n "Waiting grace period to allow tapelibrary to settle down " for i in `seq 1 10`; do echo -n "." sleep 1s done echo "" echo "Powering down tapelibrary now." echo "1" > $COUNTF eval "$TURN_OFF_CMD" else CURCOUNT=$((CURCOUNT+1)) # echo "Increasing count to $CURCOUNT" echo $CURCOUNT > $COUNTF fi |
Dieses Setup läuft nun schon seit über einem Jahr stabil.
0 comments on “Automatisches Ein- und Ausschalten einer Tape-Library für Bacula-Backups”
3 Pings/Trackbacks für "Automatisches Ein- und Ausschalten einer Tape-Library für Bacula-Backups"
[…] nach dem Finden der Lösung, über die Ursache nur den Kopf schütteln kann. Wie ich erst kürzlich wieder erwähnte, betreibe ich an meinem Heimserver eine Tapelibrary für Backups. Diese Tapelibrary ist per Fibre […]
[…] schreibe ich darüber, wie ich meine Tapelibrary nach Bedarf ein- und ausschalte, schon macht der Schalter, der das ermöglicht, Zicken. 😉 Konkret handelt es sich dabei um eine […]
[…] wesentlicher Grund dafür, mir meine Tapelibrary bedarfsgerecht ein- und auszuschalten war ja deren horrende Leistungsaufnahme im Betrieb. Nachdem ich die Netzwerksteckdose, die das Ein- […]