sophisticated Backups mit Rsync Part II

Version 3.1
Features:

  • Logs werden nun gzip komprimiert
  • wöchentliche Backups aus der vorherigen Woche werden tar.gz komprimiert

 

#!/bin/sh

# Philipp's backup-scripte version 3.1

ROOT=`pwd`
BACKDIR=$ROOT/backup

D=`eval date +%Y-%m-%d`
W=`eval date +%Y-%W`
w=`eval date +%w`
LATEST="latest"
EXCLUDE=$ROOT/exclude.txt
SOURCES=$ROOT/sources.txt

LOG=$ROOT/log/$D.log

# Array mit allen ben‚‚tigten Verzeichnissen und Ordnern
folders=( $ROOT/log )
files=( $EXCLUDE $SOURCES $LOG)

for folder in ${folders[@]}; do
    if [ ! -d $folder  ] ; then mkdir -p $folder; fi
done

for file in ${files[@]}; do
    if [ ! -f $file  ] ; then touch $file; fi
done

log () {
    if [  -z "$1" ] ; then echo "" > $LOG ; fi
    echo $1
    echo $1 >> $LOG    
}

# needs Hostname
gzipLastWeekly (){
    log "=================================================================="
    log "compress...";
    echo $1
    ROOT=`pwd`
    L_BACKDIR="$ROOT/backup/$1/weekly"; 
    if [ -d $L_BACKDIR ] ; then    
        NUM=${#L_BACKDIR}+1;
        L_LATEST=`ls -al $L_BACKDIR/latest`
        L_FOLDER="${L_LATEST#*-> }";
        L_FILE="${L_LATEST#*-> }";
        L_FILENAME="${L_FILE:$NUM:7}";
        log "L_BACKDIR $L_BACKDIR";
        #log "L_LATEST $L_LATEST";
        log "L_FOLDER $L_FOLDER";
        log "L_FILE $L_FILE";
        log "L_FILENAME $L_FILENAME";
        log "=================================================================="          
        if [ -d $L_FOLDER ] ; then
            if [ $L_FILENAME != "$W" ]; then
                log "compress  $L_FOLDER";
                cd $L_BACKDIR
                tar -cvf - $L_FILENAME/ | gzip > $L_FILENAME.tar.gz  | tee -a $LOG
                cd $ROOT
                rm -R $L_FOLDER
                log "MD5: "
                log "=================================================================="
                md5 $L_BACKDIR/$L_FILENAME.gz -out $LOG;
                log "=================================================================="
            fi
        fi    
    fi
    log "...done";
    log "==================================================================" 
}

# Ab hier gehts dann richtig los :-)

log

log "STARTING BACKUP..."
log "date: $D"

#stopping start time
#TIME0= expr `date +%s`;

# backup following sources
for SOURCE in `cat $SOURCES`; do
    log ""
    log "=================================================================="
    log "====== backup: $SOURCE "
    if [ ! -d $BACKDIR/$SOURCE/daily/$D  ] ; then mkdir -p $BACKDIR/$SOURCE/daily/$D; fi    
    if [ ! -f $BACKDIR/$SOURCE/src-root.txt  ] ; then touch $BACKDIR/$SOURCE/src-root.txt; fi    
    if [ ! -f $BACKDIR/$SOURCE/src-folders.txt  ] ; then touch $BACKDIR/$SOURCE/src-folders.txt; fi      
    #Logindaten lesen
    LOGINDATA=`cat $BACKDIR/$SOURCE/src-root.txt`
    if [ ! "$LOGINDATA" = "" ]; then
        log "====== benutze $LOGINDATA" 
        log "=================================================================="
        log ""
        for FOLDER in `cat $BACKDIR/$SOURCE/src-folders.txt`; do
            log "backup up... $FOLDER"
            mkdir -p $BACKDIR/$SOURCE/daily/$D/$FOLDER
            ### wenn schon latest-day vorhanden, dann sichere nur ‚nderungen
            if [ -d $BACKDIR/$SOURCE/daily/$LATEST  ] ; then 
		        log "using latest: $BACKDIR/$SOURCE/daily/$LATEST"
                rsync -zrva $OLD --exclude-from=$EXCLUDE --link-dest=$BACKDIR/$SOURCE/daily/$LATEST/$FOLDER  $LOGINDATA/$FOLDER/ $BACKDIR/$SOURCE/daily/$D/$FOLDER | tee -a $LOG
            else
                rsync -zrva $OLD --exclude-from=$EXCLUDE $LOGINDATA/$FOLDER/ $BACKDIR/$SOURCE/daily/$D/$FOLDER | tee -a $LOG
            fi   	
        done

        ### setze latest von altem Stand auf aktuelles Backup
        if [ -d $BACKDIR/$SOURCE/daily/$D ] ; then
            log "Daily: deleting ln last daily latest: $BACKDIR/$SOURCE/daily/$LATEST"
            if [ -d $BACKDIR/$SOURCE/daily/$LATEST ] ; then rm $BACKDIR/$SOURCE/daily/$LATEST; fi
            log "Daily: updating ln daily latest: $BACKDIR/$SOURCE/daily/$LATEST"
            ln -s $BACKDIR/$SOURCE/daily/$D $BACKDIR/$SOURCE/daily/$LATEST | tee -a $LOG     
            log "Daily: deleting ln last daily latest: $BACKDIR/$SOURCE/$LATEST"
            if [ -d $BACKDIR/$SOURCE/$LATEST ] ; then rm $BACKDIR/$SOURCE/$LATEST; fi
            log "Daily: updating ln daily latest: $BACKDIR/$SOURCE//$LATEST"
            ln -s $BACKDIR/$SOURCE/daily/$D $BACKDIR/$SOURCE/$LATEST | tee -a $LOG     
        fi

        ### setze latest-global von altem Stand auf aktuelles Backup
        log "Daily: deleting ln last global latest: $BACKDIR/$SOURCE/$LATEST"
        if [ -d $BACKDIR/$SOURCE/$LATEST ] ; then 
            rm $BACKDIR/$SOURCE/$LATEST;
        fi            
        log "Daily: updating ln global latest"
        ln -s $BACKDIR/$SOURCE/daily/$D $BACKDIR/$SOURCE/$LATEST | tee -a $LOG 

        ## wenn ende der Woche, dann erstelle Snapshot der Woche
        if [ "$w" = "0" ]; then
            log "Weekly: create week-backup for $W"
            for FOLDER in `cat $BACKDIR/$SOURCE/src-folders.txt`; do
                mkdir -p $BACKDIR/$SOURCE/weekly/$W/$FOLDER
                rsync -zrva $OLD --exclude-from=$EXCLUDE $BACKDIR/$SOURCE/daily/$D/$FOLDER/ $BACKDIR/$SOURCE/weekly/$W/$FOLDER | tee -a $LOG
            done

            ### komprimiere letztes week Backup
            log "==================================================================" 
            log "Weekly: compressing last weekly latest"
            gzipLastWeekly $SOURCE

            ### setze latest-day von altem Stand auf aktuelles Backup
            log "Weekly: deleting ln last daily latest: $BACKDIR/$SOURCE/daily/$LATEST"
            if [ -d $BACKDIR/$SOURCE/daily/$LATEST ] ; then 
                rm $BACKDIR/$SOURCE/daily/$LATEST;
            fi

            ### loesche alte Backups
            log "Weekly: delete all daily folders"
            rm -R $BACKDIR/$SOURCE/daily/*

            log "Weekly: updating ln daily latest: $BACKDIR/$SOURCE/daily/$LATEST"
            ln -s $BACKDIR/$SOURCE/weekly/$W $BACKDIR/$SOURCE/daily/$LATEST | tee -a $LOG            

            ### setze latest-week von altem Stand auf aktuelles Backup
            log "Weekly: deleting ln last weekly latest: $BACKDIR/$SOURCE/weekly/$LATEST"
            if [ -d $BACKDIR/$SOURCE/weekly/$LATEST ] ; then 
                rm $BACKDIR/$SOURCE/weekly/$LATEST;
            fi

            log "Weekly: updating ln weekly latest"
            ln -s $BACKDIR/$SOURCE/weekly/$W $BACKDIR/$SOURCE/weekly/$LATEST | tee -a $LOG 

            ### setze latest-global von altem Stand auf aktuelles Backup
            log "Weekly: deleting ln last global latest: $BACKDIR/$SOURCE/$LATEST"
            if [ -d $BACKDIR/$SOURCE/$LATEST ] ; then 
                rm $BACKDIR/$SOURCE/$LATEST;
            fi            
            log "Weekly: updating ln global latest"
            ln -s $BACKDIR/$SOURCE/weekly/$W $BACKDIR/$SOURCE/$LATEST | tee -a $LOG 
        fi
    fi
done

#stopping end time
#TIME1= expr `date +%s`;
#ERG = expr `$TIME1 - $TIME0`;

log "=================================================================="
log "DONE"
#log "using $ERG seconds"
log "=================================================================="

gzip $LOG -f

 

instant jruby & derby environment für eine RoR Anwendung

Als angestammter Java-Entwickler geht es mir oftmals schwer von der Hand, einer Ruby on Rails (RoR) Anwendung mit relativ wenig Aufwand eine brauchbare Laufzeitumgebung zu bieten.
Normalerweise sollte das OS (MacOS 10.5.6) alles Brauchbare bieten. So ist oftmals eine Rails-Version installiert und auch das (standardmäßig genutzte) SQlite 3 ist vorhanden.
Dennoch sind es oftmals Plugins (spezielle Rails Version / spezielle gems), welche einen zusätzlichen Aufwand benötigen.
Nicht zu vergessen, dass RoR nicht auf allen Systemen vorinstalliert ist und dementsprechend ein interessierter Entwicklung von einem Out-of-the-Box Erlebnis weit entfernt ist.
Sehen wir den Tatsachen ins Auge… die Wahrscheinlichkeit eine installierte JVM vorzufinden ist (noch?) deutlich höher, als eine Lauffähige Ruby-Installation.
Was liegt also näher, als die benötigte Umgebung auf Java fußen zu lassen.
Hierzu werden verwendet:

  • jRuby in Version 1.1.5 (http://jruby.codehaus.org)
  • Derby-DB in Version 10.4.2.0 (http://db.apache.org/derby)
  • weiterhin wird eine installierte JVM (>1.5) vorrausgesetzt

 

Alles weitere wird mit Hilfe von shell-Scripten bewerkstelligt. Wobei momentan nur Unix-Scripte benutzt werden. Eine Portierung auf Windows sollte aber nur eine Sache von Minuten sein.
Es liegt eine RoR-Anwendung in einem Entwicklungs-Status vor. Diese wurde bisher in einem Netbeans-Enviroment mit einer SQlite-DB betrieben.
Das Verzeichnis ist folgendermaßen aufgebaut:

ROOT
|
|- microblog (dies ist unsere RoR-Anwendung)
|
|- derby (derby-installtion - es werden jeweils das bin und lib Verzeichnis benötigt)
| |-bin
| |-lib
|
|- jruby (jruby-installtion - es werden jeweils das bin und lib Verzeichnis benötigt)
| |-bin
| |-lib

Das Hauptproblem besteht darin, dass alle benötigten gems in das entsprechende Unterverzeichnis installiert werden müssen.
Weiterhin muss die Derby-DB mit dem entsprechenden Rake-Task auf mit der aktuellen Schema-Datei instanziiert werden.
Zuletzt sollen die vorhandenen User-Daten in die Derby-DB eingefügt werden.

  1. Anpassen der database.yml
    Wir nutzen weiterhin eine jdbc-Connection. Allerdings ändert sich der Treiber auf den der Derby-DB:
    database.yml

    development:
    adapter: jdbc
    driver: org.apache.derby.jdbc.ClientDriver
    url: jdbc:derby://localhost/microblog_development;create=true
    encoding: utf8
    pool: 5
    username: microblog
    password: microblog
    host: localhost
  2. Export der alten DB-Daten:
    Wir benutzen hierzu das Tool sqlitebrowser (http://sqlitebrowser.sourceforge.net) und erzeugen uns so einen SQL-Dump der alten SQLite-DB. Wir benutzen hierbei nur die SQL-Inserts für den User-Import. Diese speichern wir in die Datei:
    microblog/db/microblog.sql
  3. Für den Import erstellen wir einen Rake-Task:
    microblog/lib/tasks/sql-import.rake

    namespace :microblog do
    desc 'Import old SQL Data'
    task :sqlimport => :environment do
    dbConn = ActiveRecord::Base.establish_connection :development
    sql = File.open("db/microblog.sql").read
    sql.split(';').each do |sql_statement|
    dbConn.connection.execute(sql_statement)
    end
    puts "imported user data '#{Time.now}' "
    end
    end
  4. Erstellen des Setup-Scriptes:
    Folgende Schritte sind notwendig:

    1. Setzen aller benötiger Verzeichnisse
    2. installieren aller benötigter gems
    3. Starten des Derby-DB-Servers
    4. Rake db:migrate
    5. import der alten Daten
    6. Beenden des Derby-DB-Servers

    Das Script sieht wie folgt aus:
    jruby-setup.sh

    #!/bin/sh
    BASE_DIR=`pwd`
    CP=".:$BASE_DIR/jruby/lib/*:$BASE_DIR/derby/lib/derbyclient.jar"
    JAVA_OPTS="-Djdbc.drivers=org.apache.derby.jdbc.EmbeddedDriver"
    JRUBY="$BASE_DIR/jruby/bin/jruby"
    DERBY_HOME=`cd derby && pwd`
    export DERBY_HOME=$DERBY_HOME
    cd $BASE_DIR
    echo "setting up jgems..."
    $BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/jgem update --system
    $BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/jgem install jruby-openssl --no-rdoc --no-ri
    $BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/jgem install -v=2.2.2 rails --no-rdoc --no-ri
    $BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/jgem install activerecord-jdbc-adapter activerecord-jdbcderby-adapter --no-rdoc --no-ri
    echo "starting derby..."
    $BASE_DIR/derby/bin/startNetworkServer &
    echo "setting up derby..."
    cd microblog
    $BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/rake db:migrate
    $BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/rake microblog:sqlimport
    cd $BASE_DIR
    echo "stopping derby..."
    $BASE_DIR/derby/bin/stopNetworkServer &

    Es ist zu erwähnen, dass es notwendig ist, jeweils auf die entsprechende jRuby-Installation zu verweisen.
    Weiterhin benötigt jRuby den entsprechenden derbyClientDriver, welcher in die (von jRuby später verwendete) JAVA_OPTS-Variabel eingetragen wird.
    Ebenfalls musst der Classpath soweit angepasst werden, dass sowohl jRuby, als auch Derby über die notwendigen Bibliotheken verfügen.
    Als letztes ist noch erwähnenswert, dass die beiden Rake-Tasks jeweils aus dem App-Verzeichnis ausgeführt werden.

  5. Das Start-Script.
    Letzendlich sind auch zum eigentlichen Betrieb des Servers Anpassungen notwendig, da auch hier die jRuby-Instanz mit den verwendeten gems benutzt werden sollen.
    Das Script sieht wie folgt aus:
    run.sh

    #!/bin/sh
    BASE_DIR=`pwd`
    CP=".:$BASE_DIR/jruby/lib/*:$BASE_DIR/derby/lib/derbyclient.jar"
    JAVA_OPTS="-Djdbc.drivers=org.apache.derby.jdbc.EmbeddedDriver"
    JRUBY="$BASE_DIR/jruby/bin/jruby"
    export BASE_DIR=$BASE_DIR
    export JRUBY=$JRUBY
    DERBY_HOME=`cd derby && pwd`
    export DERBY_HOME=$DERBY_HOME
    cd $BASE_DIR
    echo "starting derby..."
    $BASE_DIR/derby/bin/startNetworkServer &
    echo "setting up derby..."
    cd microblog
    $BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/rake db:migrate
    echo "starting microblog"
    $BASE_DIR/jruby/bin/jruby $BASE_DIR/microblog/script/server
    echo "stopping derby..."
    $BASE_DIR/derby/bin/stopNetworkServer &

    Es entspricht also einer abgespeckten Variante des Setup-Scriptes. Hierbei wird auch immer ein db:migrate aufgerufen, für den Fall, dass sich die DB-Struktur in der Zwischenzeit geändert haben sollte.

  6. Auslieferung ;-).
    Derby und jRuby belegen knapp 80 MB sodass es notwendig ist, die Dateigröße für den Transport zu verringern.
    Zuallererst sollten die benötigten gems am besten immer online bezogen werden, sodass man hier ein paar MB sparen kann.
    Weiterhin benutzen wir Jar um die vorhandenen Dateien auf ein 13 MB-Archiv zu packen.
    Die veränderten Scripte sehen wie folgt aus:
    Zuerst das Script, welches die vorhandenen Dateien packt:
    pack.sh

    #!/bin/sh
    find . -name '*.DS_Store' -type f -delete
    jar -cvf statusQ-runtime.jar derby/ jruby/ run.sh pack.sh microblog/db/microblog.sql microblog/lib/tasks/sql-import.rake
    rm -R jruby
    rm -R derby
    rm run.sh
    rm microblog/db/microblog.sql
    rm microblog/lib/tasks/sql-import.rake
    rm pack.sh

    Und nun das geänderte jruby-setup.sh Script, welches vor dem eigentlichen Setup noch für das Entpacken aller Dateien verantwortlich ist:
    jruby-setup.sh

    #!/bin/sh
    jar -xvf statusQ-runtime.jar
    rm -R META-INF
    chmod +x run.sh
    chmod +x setup.sh
    chmod +x pack.sh
    chmod +x jruby/bin/jruby
    chmod +x derby/bin/startNetworkServer
    chmod +x derby/bin/stopNetworkServer
    BASE_DIR=`pwd`
    CP=".:$BASE_DIR/jruby/lib/*:$BASE_DIR/derby/lib/derbyclient.jar"
    JAVA_OPTS="-Djdbc.drivers=org.apache.derby.jdbc.EmbeddedDriver"
    JRUBY="$BASE_DIR/jruby/bin/jruby"
    DERBY_HOME=`cd derby && pwd`
    export DERBY_HOME=$DERBY_HOME
    cd $BASE_DIR
    echo "setting up jgems..."
    $BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/jgem update --system
    $BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/jgem install jruby-openssl --no-rdoc --no-ri
    $BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/jgem install -v=2.2.2 rails --no-rdoc --no-ri
    $BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/jgem install activerecord-jdbc-adapter activerecord-jdbcderby-adapter --no-rdoc --no-ri
    echo "starting derby..."
    $BASE_DIR/derby/bin/startNetworkServer &
    echo "setting up derby..."
    cd microblog
    $BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/rake db:migrate
    $BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/rake microblog:sqlimport
    cd $BASE_DIR
    echo "stopping derby..."
    $BASE_DIR/derby/bin/stopNetworkServer &

Als nächstes sollte die Scripte auf Windows portiert werden.
Weiterhin wäre es interessant, die Derby/jRuby Binaries jeweils direkt online zu beziehen.

what to do, if you locked yourself out from Mac OS?

a wood with a chain and a padlock

Yesterday I found myself in a strange situation:

Every Shell Command I tried to use ended up with a “command not found“. This happened with sudo, nano … you name it.

After a short time, I figured out that the folder /usr/bin (where all these programs are stored) had only executable rights for the owner (so admin:wheel).

As a normal user I was not able to use them.

So how to change this if you cannot sudo ?

As always in MacOS, the best way to fix this, is to boot into Singe User Mode (restart and press the Apple/Command Key + “S”).
After you got into the Terminal view you have to scan the filesystem for errors:

/sbin/fsck -fy 

And then remount it as writable:

/sbin/mount -wu /

Now, you can alter the user rights for /usr/bin with:

chmod 755 /usr/bin

After a reboot you are again able to execute the commands.
If you still have problems you should control your PATH-settings.

echo $PATH 

It should contain /usr/bin near the beginning.

sophisticated Backups mit Rsync

Backups sind wichtig.
Jeder der einmal vor einer kaputten, ratternden Festplatte gesessen hat, weiß wie frustrierend das Wissen ist, alle seine Daten ins informationstechnische Nirwana entschwinden zu sehen.
Ist ein Backup der persönlichen Daten noch mit relativ geringem Aufwand möglich (so es denn regelmäßig veranstaltet wird), so wird ein Backup eines Server-Systems in vielen Dingen anspruchsvoller.
Dies fängt damit an, dass reinen Datenmengen oftmals das Vielfache eines Ein-Benutzer-Systems betragen. Okay in heutige Zeit, wo ein jeder zig GBs an Musik und Videos auf dem Rechner hat, muss man das natürlich relativieren.
In diesem Beitrag möchte ich ein Bash-Script vorstellen, welches folgende Dinge leistet:

  1. Sichern von N-Hosts über RSYNC
  2. Sichern von N-Verzeichnissen auf jedem Host
  3. Inkrementelles Sichern (es werden nur Unterschiede gesichert/übertragen)
  4. Zusammenführen von täglichen (inkrementellen) Backups zu wöchentlichen Vollbackups.
  5. die aktuellen Backups sind jeweils unter einem Hardlink “latest” erreichbar.

Nachfolgend erfolgt eine Beschreibung über den Ablauf des Scriptes.
Es wird Momentan durch eine Sicherung von 3 Serversystemen und ca. 20 GB an reinen Nutzdaten getestet.

  1. Stelle sicher, dass alle notwendigen Verzeichnisse vorhanden sind:
    • Verzeichnis “log” für log-Dateien der Sicherungen
    • Verzeichnis “backup” für die eigentlichen Sicherungen
    • Je Backupquelle ein Verzeichnis
    • Innerhalb jedes Quellverzeichnisses ein Verzeichnis für tägliche und eines für wöchentliche Sicherungen
  2. Stelle sicher, dass alle Config-Dateien vorhanden sind:
    • sources.txt mit den Namen der zu sichernden Hosts/Verzeichnisse
    • exclude.txt mit den Dateien, welche nicht gesichert werden sollten (z.B. Thumbs.db)
    • innerhalb jedes Quellverzeichnisses eine Datei namens src-root.txt (mit dem ROOT der Sicherung)
      und src-folders.txt (mit den einzelnen Verzeichnissen)

Der Aufbau des Root-Verzeichnisses sieht nun wie folgt aus.

ROOT
|
|-log
|-sources.txt
|-exclude.txt
|-backup
|-host1.de
|-host2.de
|-src-root.txt
|-src-folders.txt
|-daily
|-weekly

Ein Beispiel für eine sources.txt sieht wie folgt aus:

host1.de
host2.de

Ein Beispiel für eine exclude.txt sieht wie folgt aus:

Thumbs.db
.DS_Store

Eine src-root.txt könnte so aussehen:

user@host1.de:

Eine src-folders.txt könnte so aussehen:

/home
/etc
/var/log
/var/www

Ablauf der Sicherung:

  • Generiere Log-Datei.
  • Iteriere über alle Quellen aus sources.txt
  • Überprüfe ob alle Quellen Verzeichnisse zum Sichern enthalten.
  • Überprüfe ob lokal Verzeichnisse für die Sicherung existieren – wenn nicht lege sie an.
  • Erstelle tägliches Backup – existiert ein vorheriges Backup, so sichere nur die Änderungen.

Dies passiert mit folgendem Befehl:

rsync -zrva –exclude-from=exclude.txt –link-dest=<hardlink-zu-altem-backup>  <sourcefolder>/ <backupfolder>/daily/<tag>/<zu-sicherndes-Verzeichnis>
Hierbei werden alle Benutzerrechte mitgesichert.
Setzte Hardlink vom letztem täglichen Backup auf aktuelles tägliches Backup.
Überprüfe ob Sonntag ist ( date +%w = 0)
Wenn ja, synchronisiere letztes tägliches Backup mit wöchentlichem (neuen) Backup.
Setzte Hardlink vom letztem wöchentlichen Backup auf aktuelles wöchentliches Backup.
Lösche alle täglichen Backups.
Setzte Hardlink vom letztem täglichen Backup auf aktuelles wöchentliches Backup.
So werden maximal 7 tägliche inkrementelle Backups erstellt und pro Jahr 52 wöchentliche Full-Backups.

#!/bin/sh

# Philipp's backup-scripte version 2

ROOT=/tank/backup/server
BACKDIR=$ROOT/backup
D=`eval date +%Y-%m-%d`
W=`eval date +%Y-%W`
w=`eval date +%w`
LATEST=latest
EXCLUDE=$ROOT/exclude.txt
SOURCES=$ROOT/sources.txt

LOG=$ROOT/log/$D.log

# Array of all needed folders
folders=( $ROOT/log $ROOT/sources )
files=( $EXCLUDE $SOURCES $LOG)

for folder in ${folders[@]}; do
    if [ ! -d $folder  ] ; then mkdir -p $folder; fi
done

for file in ${files[@]}; do
    if [ ! -f $file  ] ; then touch $file; fi
done

log () {
    if [  -z "$1" ] ; then echo "" > $LOG ; fi
    echo $1
    echo $1 >> $LOG    
}

# Ab hier gehts dann richtig los :-)

log

log "STARTING BACKUP..."
log "date: $D"

#stopping start time
#TIME0= expr `date +%s`;

# backup following sources
for SOURCE in `cat $SOURCES`; do
    log ""
    log "=================================================================="
    log "====== backup: $SOURCE "
    if [ ! -d $BACKDIR/$SOURCE/daily/$D  ] ; then mkdir -p $BACKDIR/$SOURCE/daily/$D; fi    
    if [ ! -f $BACKDIR/$SOURCE/src-root.txt  ] ; then touch $BACKDIR/$SOURCE/src-root.txt; fi    
    if [ ! -f $BACKDIR/$SOURCE/src-folders.txt  ] ; then touch $BACKDIR/$SOURCE/src-folders.txt; fi      
    #Logindaten lesen
    LOGINDATA=`cat $BACKDIR/$SOURCE/src-root.txt`
    if [ ! "$LOGINDATA" = "" ]; then
        log "====== benutze $LOGINDATA" 
        log "=================================================================="
        log ""
        for FOLDER in `cat $BACKDIR/$SOURCE/src-folders.txt`; do
            log "backup up... $FOLDER"
            mkdir -p $BACKDIR/$SOURCE/daily/$D/$FOLDER
            ### wenn schon latest-day vorhanden, dann sichere nur ânderungen
            if [ ! -d $BACKDIR/$SOURCE/daily/$LATEST  ] ; then 
                rsync -zrva $OLD --exclude-from=$EXCLUDE --link-dest=$BACKDIR/$SOURCE/daily/$LATEST/$FOLDER  $LOGINDATA/$FOLDER/ $BACKDIR/$SOURCE/daily/$D/$FOLDER | tee -a $LOG
            else
                rsync -zrva $OLD --exclude-from=$EXCLUDE $LOGINDATA/$FOLDER/ $BACKDIR/$SOURCE/daily/$D/$FOLDER | tee -a $LOG
            fi   
        done
        ### setze latest-day von altem Stand auf aktuelles Backup
        if [ -d $BACKDIR/$SOURCE/daily/$D ] ; then
            if [ -d $BACKDIR/$SOURCE/daily/$LATEST ] ; then rm $BACKDIR/$SOURCE/daily/$LATEST; fi
            ln -s $BACKDIR/$SOURCE/daily/$D $BACKDIR/$SOURCE/daily/$LATEST    
        fi

        ## wenn ende der Woche, dann erstelle Snapshot der Woche
        if [ "$w" = "0" ]; then
            log "create week-backup for $W"
            for FOLDER in `cat $BACKDIR/$SOURCE/src-folders.txt`; do
                mkdir -p $BACKDIR/$SOURCE/weekly/$W/$FOLDER
                rsync -zrva $OLD --exclude-from=$EXCLUDE $BACKDIR/$SOURCE/daily/$D/$FOLDER/ $BACKDIR/$SOURCE/weekly/$W/$FOLDER | tee -a $LOG
            done
            ### setze latest-week von altem Stand auf aktuelles Backup
            if [ -d $BACKDIR/$SOURCE/weekly/$LATEST ] ; then rm $BACKDIR/$SOURCE/weekly/$LATEST; fi
            ln -s $BACKDIR/$SOURCE/weekly/$W $BACKDIR/$SOURCE/weekly/$LATEST | tee -a $LOG 
            log "updating weekly latest"

            ### lââsche alte Backups
            log "delete all daily folders"
            rm -R $BACKDIR/$SOURCE/daily/*

            ### setze latest-day von altem Stand auf aktuelles Backup
            if [ -d $BACKDIR/$SOURCE/daily/$LATEST ] ; then rm $BACKDIR/$SOURCE/daily/$LATEST; fi
            ln -s $BACKDIR/$SOURCE/weekly/$W $BACKDIR/$SOURCE/daily/$LATEST | tee -a $LOG
            log "updating daily latest"
        fi
    fi
done

#stopping end time
#TIME1= expr `date +%s`;
#ERG = expr `$TIME1 - $TIME0`;

log "=================================================================="
log "DONE"
#log "using $ERG seconds"
log "=================================================================="

Nexenta iSCSI Target Setup

root@sunny:~# zfs create tank/home
root@sunny:~# zfs create tank/home/philipp
root@sunny:~# zfs create -V 250M tank/home/philipp/TM
root@sunny:~# zfs set shareiscsi=on tank/home/philipp/TM
root@sunny:~# iscsitadm modify target -p 1 tank/home/philipp/TM
root@sunny:~# zfs list
NAME                             USED  AVAIL  REFER  MOUNTPOINT
syspool                          689M  16.6G    23K  none
syspool/rootfs-nmu-000           688M  16.6G   641M  legacy
syspool/rootfs-nmu-000@initial  47.3M      –   631M  –
tank                             250M  1.34T    21K  /tank
tank/home                        250M  1.34T  27.5K  /tank/home
tank/home/philipp                250M  1.34T    27K  /tank/home/philipp
tank/home/philipp/TM             250M  1.34T    24K  –
tank/media                        18K  1.34T    18K  /tank/media
root@sunny:~# iscsitadm list target -v
Target: tank/home/philipp/TM
    iSCSI Name: iqn.1986-03.com.sun:02:f683c44e-e774-c018-9f6f-89ef914db75b
    Alias: tank/home/philipp/TM
    Connections: 0
    ACL list:
    TPGT list:
        TPGT: 1
    LUN information:
        LUN: 0
            GUID: 0
            VID: SUN
            PID: SOLARIS
            Type: disk
            Size:  250M
            Backing store: /dev/zvol/rdsk/tank/home/philipp/TM
            Status: online

(via Solaris iSCSI Target with ESX 3.02 Server)

Blogged with the Flock Browser

eingescannte Bilder konvertieren und Timestamp setzen

Da ich die letzten Tage gezwungenermaßen sehr viel Zeit hatte (warum kommt später) und ich irgendwie mal Grund in meine Kisten und Boxen bringen möchte, habe ich angefangen, meine (Papier-) Fotos einzuscannen und in iPhoto zu übertragen.

Die Frage stelle sich mir nun, wie ich aus 200 Tiff Dateien die das aktuelle Datum tragen (also das des Scans) ordentliche Digitalbilder hinkriege, sodass sie A) nicht zuviel Platz wegnehmen und B) nicht alle auf einen Tag datiert sind. Als Datum reicht mir erstmal nur der jeweilige Monat des Jahres.

Nachdem ich von freundlichen Mitmenschen (danke Willem, danke Christoph) mit geeigneten Shellscripten versorgt worden bin, war die Sache mit der Dateigröße vom Tisch der folgende Einzeiler macht das recht gut:

     for i in `ls`; do convert $i neues_verzeichnis/$i.png; done

Nicht vergessen: “neues_Verzeichnis” natürlich anpassen und das ganze (wenn man es denn in eine Datei packt) chmod +x setzen.
Aber das reichte mir ja nicht ^^

Und was mache ich, wenn ich gar nicht mehr anders kann? Ich nutze PHP…. gute, alte, unübersichtliche Scriptsprache.
Well… thats the Code:


    <?php
    if(count($argv) < 2) die(“Usage:\n php -f convert.php — -filetype -month -year\n php -f convert.php — -png -07 – 1997\n _MIND THE — !!_ \n”);
    $sdir = getcwd ();
    echo “Sourcefolder: “.$sdir.” …\n”;
    $tdir = $sdir.”/converted”;
    $ext = substr($argv[1], 1);
    $month = substr($argv[2], 1);
    $year = substr($argv[3], 1);
    echo “$ext-$month-$year\n”;
    $time = mktime(12, 0, 0, $month, 15, $year);
    echo $time.”\n”;
    @mkdir($tdir);
    if ($handle = @opendir($sdir)) {
        while (false !== ($file = readdir($handle)))
            if($file != “.” and $file != “..” and $file != “converted”){
            $filearray = explode(“.”, $file);
            array_pop($filearray);
            $newfile = $tdir.”/”.implode(“.”, $filearray).”.png”;
            echo “convert $sdir/$file $newfile\n”;
            system ( “convert $sdir/$file $newfile” );
            echo “set Date to: “.date(“d.m.y”, $time).”\n”;
            touch ($newfile, $time);
        }
    }
    ?>


Tut genau das was es soll…
Aufgerufen wird er mit

    php -f convert.php

Danach wird einem gesagt, was man an Parametern übergeben kann/soll/darf/muss.
Nun bleibt mir nur noch, mich mit meiner eigenen Vergangenheit in Form von Fotos konfrontiert zu sehen ^^.

Blogged with the Flock Browser

my very first (ehm… useful ?!?!) ruby script.

So concerning my first try of a web based zfs managment interface i will restart the project with some new conditions:

  • 1. i will switch from the former language PHP to the – like everywhere told – dev-friendly language ruby.
  • 2. i will first concentrate on just the basic functions.
  • 3. i have to use the system build in security realms (so either PAM or an equal interface)
  • 4. i will use dynamic JS-Functions to enhance the GUI. But the system should also work with simple browser (e.g. lynx/links)
  • 5. i will offer a RESTful interface. So it should be very easy for 3rd party apps to connect to them and use alle the functionality.

The Beginning ^^:
So my first task is to bring an overview of all system-disks (other devices are not necessary at the moment) into my ruby context.
So basic system command is:

root@sunny:~# format < /dev/null
Searching for disks…

The device does not support mode page 3 or page 4,
or the reported geometry info is invalid.
WARNING: Disk geometry is based on capacity data.

The current rpm value 0 is invalid, adjusting it to 3600
done

c3t0d0: configured with capacity of 18.61GB

AVAILABLE DISK SELECTIONS:
0. c0d0 <DEFAULT cyl 3734 alt 2 hd 255 sec 63>
/pci@0,0/pci-ide@11,1/ide@0/cmdk@0,0
1. c3t0d0 <ST92011A–3.04 cyl 2430 alt 2 hd 255 sec 63>
/pci@0,0/pci925,1234@11,3/storage@2/disk@0,0
Specify disk (enter its number):

Beside of the interesting warning, this will do the job. The format command will normally lead to a prompt. To avoid this, you just pipe the output to /dev/null.

The really valuable stuff is in the upper part of the output.
After consulting the ruby API about strings and arrays i ended up with the following (dirty) lines of code:


#!ruby
#!/usr/local/bin/ruby -rubygems

s = %x{format < /dev/null}
sa = s.split(“\n”)
counter = 0
dsk = false
puts “Array: “+sa.length().to_s()
sa.map do |i|
if i.strip().length() > 0
print counter.to_s()+” ”
if counter == (sa.length()-1)
dsk = false
end
if dsk == true
puts “DSK: “+i.strip()+”\n”
else
puts i.strip()+”\n”
end
if i.eql?(“AVAILABLE DISK SELECTIONS:”)
dsk = true
end

end
counter += 1
end

I found a really nice post about the ruby command line interface. That post gave me a good start in experimenting with the produced string output. You just always remember the irb to test parts of your code. Of course there is _A_Lot_ of debugging code. But the end-result is correct. Of course there should no hard-coded string in there. A by the way: it was a little bit inconvenient to quarrel again with the integer<->string conversion after some relaxing and luxurious years coding Java ^^. Okay so no implicit casting in ruby (?). Oh wait a minute – no typed language, so no casting?.

Oh yes… i don’t want to hide the output:

root@sunny:~$ ruby cmd.rb

The device does not support mode page 3 or page 4,
or the reported geometry info is invalid.
WARNING: Disk geometry is based on capacity data.

The current rpm value 0 is invalid, adjusting it to 3600
Array: 12
0 Searching for disks…
1 done
3 c3t0d0: configured with capacity of 18.61GB
6 AVAILABLE DISK SELECTIONS:
7 DSK: 0. c0d0 <DEFAULT cyl 3734 alt 2 hd 255 sec 63>
8 DSK: /pci@0,0/pci-ide@11,1/ide@0/cmdk@0,0
9 DSK: 1. c3t0d0 <ST92011A–3.04 cyl 2430 alt 2 hd 255 sec 63>
10 DSK: /pci@0,0/pci925,1234@11,3/storage@2/disk@0,0
11 Specify disk (enter its number):


So i guess i found my disks. At the end, i can just iterate about the even number of entries :-).
Okay ruby-gurus. How can i optimize my code. I am beginner level and “slurred” during years, coding Java and anciently PHP.
I am sure there is a lot of potential to cleanup this mess ^^.

Blogged with the Flock Browser