Archiv des Autors: Christian

LVM Snapshots

Introduction

LVM, the Linux Logical Volume Manager, allows taking so-called snapshots of logical volumes (LVs). A snapshot has the same behavior as an independent copy of the original volume; however, the snapshot only stores the changes compared to the original volume, so it typically needs considerably less disk space. Actually, the size of the snapshot volume can be arbitrarily chosen (independent of the size of the original volume). However, when the disk space of the snapshot is not sufficient to store all changes, the snapshot becomes invalid. To be on the safe side, the snapshot should have the same size as the original volume.

Technically, the snapshot is a copy-on-write (COW) table. A change to the snapshot is stored in this table, and the original volume remains unchanged. On the other hand, when the original volume is changed, the previous data is copied into the copy-on-write table, so that the change to the original volume is not visible in the snapshot. If the same data in the snapshot has already been changed earlier (i.e. there is already an entry in the COW table), then this change is obviously not overwritten.

The device mapper

The LVM functionality is mostly handled by the device mapper. It can provide virtual block devices and redirects any access to these virtual devices to another low-level device. The device mapper has different targets (i.e. kernel modules) that are responsible for the actual mapping.

A simple LV is typically implemented by the target linear. This maps a continuous section of the virtual device to a likewise continuous section of the low-level device. Example:

test:~ # lvcreate -l 2 -n base vg0
 Logical volume "base" created
test:~ # dir /dev/vg0
total 0
lrwxrwxrwx 1 root root 7 Jul 4 09:33 base -> ../dm-0
test:~ # dir /dev/mapper
total 0
crw------- 1 root root 10, 236 Jul 4 09:28 control
lrwxrwxrwx 1 root root 7 Jul 4 09:33 vg0-base -> ../dm-0
test:~ # dmsetup table vg0-base
0 16384 linear 202:4 2048

The device 202,4 is the physical volume /dev/xvda4 that was used to create the volume group vg0:

test:~ # dir /dev/xvda*
brw-rw---- 1 root disk 202, 0 Jul 7 16:16 /dev/xvda
brw-rw---- 1 root disk 202, 1 Jul 7 16:16 /dev/xvda1
brw-rw---- 1 root disk 202, 2 Jul 7 16:16 /dev/xvda2
brw-rw---- 1 root disk 202, 3 Jul 7 16:16 /dev/xvda3
brw-rw---- 1 root disk 202, 4 Jul 7 16:16 /dev/xvda4

Preparation of the original volume

For the following tests, the LV is filled with well-defined content, e.g. with the text „base“. Since the volume group has a physical extent size of 4 MB and the LV was created with a size of 2 extents, the size is 8 MB:

test:~ # lvdisplay /dev/vg0/base | grep 'LV Size'
 LV Size 8.00 MiB

To fill the logical volume, the command dd is used:

test:~ # for i in $(seq 1 2097152); do 
> echo -n base
> done | dd of=/dev/vg0/base bs=4
 2097152+0 records in
 2097152+0 records out
 8388608 bytes (8.4 MB) copied, 56.5065 s, 148 kB/s

In order to check that the data has been correctly written, dd can be used again:

test:~ # dd if=/dev/vg0/base bs=1 count=32
 basebasebasebasebasebasebasebase

Creating the snapshot

We now create the snapshot with a size of 1 extent (4 MB):

test:~ # lvcreate -s /dev/vg0/base -n snap -l 1
Logical volume "snap" created

The directory /dev/vg0 now contains the snapshot as a separate LV. Of course, the original volume is still there, too:

test:~ # dir /dev/vg0
 total 0
 lrwxrwxrwx 1 root root 7 Jul 4 09:35 base -> ../dm-0
 lrwxrwxrwx 1 root root 7 Jul 4 09:35 snap -> ../dm-1

It is much more interesting to look at the directory /dev/mapper. Besides the two externally visible devices (which are also present in/dev/vg0), it contains two more devices, namely vg0-base-real and vg0-snap-cow:

test:~ # dir /dev/mapper
 total 0
 crw------- 1 root root 10, 236 Jul 4 09:28 control
 lrwxrwxrwx 1 root root 7 Jul 4 09:35 vg0-base -> ../dm-0
 lrwxrwxrwx 1 root root 7 Jul 4 09:35 vg0-snap -> ../dm-1
 lrwxrwxrwx 1 root root 7 Jul 4 09:35 vg0-snap-cow -> ../dm-3
 lrwxrwxrwx 1 root root 7 Jul 4 09:35 vg0-base-real -> ../dm-2

It is also interesting to look at the output of dmsetup for these devices:

test:~ # dmsetup table vg0-base
0 16384 snapshot-origin 253:2
test:~ # dmsetup table vg0-base-real
0 16384 linear 202:4 2048
test:~ # dmsetup table vg0-snap
0 16384 snapshot 253:2 253:3 P 8
test:~ # dmsetup table vg0-snap-cow
0 8192 linear 202:4 18432

It becomes visible that the snapshot created another layer of mapping. The existing device for the original LV (vg0-base) is no longer of type linear, but instead of type snapshot-origin. It refers to the device vg0-base-real. The new device vg0-snap is of type snapshot. It refers to the device vg0-base-real, too, and also to vg0-snap-cow. These two devices behave like „normal“ LVs, i.e. they refer to areas of the physical volume.

lvm-snapshots

When we look at the sizes of the 4 block devices, we find that the two devices vg0-base and vg0-base-real both have a size of 8 MB each. vg0-base-real is the original volume that was initially created, and vg0-base is just a mapping overlay. The device vg0-snap also has a (virtual) size of 8 MB because a snapshot always has the same size as the original volume. On the other hand, the COW table vg0-snap-cow has only a size of 4 MB, which is exactly the size that was specified when the snapshot was created.

test:~ # blockdev --getsize64 /dev/mapper/vg0-base /dev/mapper/vg0-snap /dev/mapper/vg0-snap-cow /dev/mapper/vg0-base-real
 8388608
 8388608
 4194304
 8388608

Changes to the snapshot

What happens when data is written to the snapshot?

test:~ # echo -n snapshot | dd of=/dev/vg0/snap bs=1
8+0 records in
8+0 records out
8 bytes (8 B) copied, 0.0341143 s, 0.2 kB/s

test:~ # dd if=/dev/mapper/vg0-snap bs=1 count=32
snapshotbasebasebasebasebasebase

test:~ # dd if=/dev/mapper/vg0-base bs=1 count=32
basebasebasebasebasebasebasebase

As expected, the data in the snapshot changed while the original volume remained unchanged. It is interesting to take a closer look at the underlying devices:

test:~ # dd if=/dev/mapper/vg0-base-real bs=1 count=32
basebasebasebasebasebasebasebase
test:~ # dd if=/dev/mapper/vg0-snap-cow | hexdump -C
00000000  53 6e 41 70 01 00 00 00  01 00 00 00 08 00 00 00  |SnAp............|
00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00001000  00 00 00 00 00 00 00 00  02 00 00 00 00 00 00 00  |................|
00001010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00002000  73 6e 61 70 73 68 6f 74  62 61 73 65 62 61 73 65  |snapshotbasebase|
00002010  62 61 73 65 62 61 73 65  62 61 73 65 62 61 73 65  |basebasebasebase|
*
00003000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00400000

The changed data has been written to the COW table, while the original LV remained unchanged. It should be noted that, even though only 4 bytes have been changed, the block that has been written to the COW table is 4096 bytes large. The size of the blocks that are written for each change is the snapshot chunk size that can be specified when the snapshot is created. Because the COW table is filled in chunks, it can be filled up with changes scattered over the snapshot, so that the snapshot becomes invalid although the total amount of changed data is much smaller than the snapshot size.

Changes to the original volume

What happens when the original volume is changed?

test:~ # echo -n test | dd of=/dev/vg0/base bs=1 seek=4096
4+0 records in
4+0 records out
4 bytes (4 B) copied, 0.0174384 s, 0.2 kB/s

test:~ # dd if=/dev/mapper/vg0-base bs=1 count=32 skip=4096
testbasebasebasebasebasebasebase

test:~ # dd if=/dev/mapper/vg0-base-real bs=1 count=32 skip=4096
testbasebasebasebasebasebasebase

test:~ # dd if=/dev/mapper/vg0-snap-cow | hexdump -C
00000000 53 6e 41 70 01 00 00 00 01 00 00 00 08 00 00 00 |SnAp............|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00001000 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 |................|
00001010 01 00 00 00 00 00 00 00 03 00 00 00 00 00 00 00 |................|
00001020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00002000 73 6e 61 70 73 68 6f 74 62 61 73 65 62 61 73 65 |snapshotbasebase|
00002010 62 61 73 65 62 61 73 65 62 61 73 65 62 61 73 65 |basebasebasebase|
*
00004000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00400000

It does not surprise that the devices vg0-base and vg0-base-real show changes. More interesting is the COW table: At a first glance, it looks as if it had not changed at all. In fact, another 4k block has been added, into which the original data („…basebasebasebase…“) has been copied. Hence, the change is not visible in the snapshot.

Sources / Links

VLAN7/VLAN8-Umschaltung

Neulich funktionierte mal wieder das Fernsehen über T-Home Entertain nicht richtig: Nach einem Kanalwechsel kam zwar zunächst das normale Fernsehbild (incl. Ton), das dann aber nach einigen Sekunden einfror. Videoload funktionierte ohne Probleme.

Ich rief daraufhin bei der Hotline der Telekom an und hatte das Glück, mit einem wirklich kompetenten Techniker zu sprechen (was mir übrigens bei der Telekom durchaus häufiger passiert – ein Grund, warum ich gern Telekom-Kunde bin). Dieser erklärte mir, dass das Fehlerbild typisch für ein Problem mit der „VLAN7/VLAN8-Umschaltung“ sei. Tatsächlich verwendet die Telekom das VLAN7 für das „normale“ Internet und das VLAN8 für den TV-Stream. Bei einem Kanalwechsel erhält man zunächst das Signal über das VLAN7 (und auch Videoload läuft darüber, weswegen das auch problemlos funktionierte). Nach einigen Sekunden wird dann aber auf VLAN8 umgeschaltet, und wenn das nicht richtig klappt, bleibt das Bild hängen.

Im Endeffekt konnte der Techniker auf der Telekom-Seite einen Reset durchführen, der das Problem gelöst hat. Sollte der Fehler mal wieder auftreten, kann ich jetzt wenigstens gleich den richtigen Hinweis geben.

LVM-Snapshots

Grundlagen

LVM, der Linux Logical Volume Manager, bietet die Möglichkeit, von einzelnen logischen Laufwerken (Logical Volumes, LVs) einen sog. Snapshot zu erzeugen. Ein Snapshot verhält sich wie eine unabhängige Kopie des ursprünglichen Volume. Allerdings speichert der Snapshot nur die Veränderungen gegenüber dem Original, d.h. es wird potenziell erheblich weniger Platz benötigt. Tatsächlich kann die Größe eines Snapshot-LV beliebig gewählt werden (unabhängig von der Größe des Originals). Wenn allerdings der Platz im Snapshot nicht ausreicht, um alle Veränderungen aufzunehmen, wird der Snapshot ungültig. Wenn man auf Nummer sicher gehen will, sollte daher der Snapshot die gleiche Größe haben wie das Original.

Technisch handelt es sich bei einem Snapshot um eine COW-Tabelle (copy on write). Das bedeutet: Jede Veränderung, die am Snapshot vorgenommen wird, wird in dieser Tabelle vermerkt, und das Original-Volume wird nicht verändert. Wird hingegen in das Original-Volume geschrieben, so werden die ursprünglichen Daten ebenfalls in die COW-Tabelle übertragen. Somit ist die Veränderung am Original im Snapshot nicht sichtbar. Falls die gleichen Daten im Snapshot bereits früher geändert worden sind (d.h. es existiert bereits ein Eintrag in der COW-Tabelle), so wird diese Veränderung natürlich nicht überschrieben.

Der Device Mapper

Die Funktionalität von LVM wird größtenteils durch den Device Mapper erledigt. Dieser kann virtuelle Block Devices bereitstellen und sorgt dafür, dass jeder Zugriff auf diese virtuellen Devices auf ein anderes darunter liegendes Device umgeleitet wird. Dabei gibt es verschiedene Targets, d.h. Kernel-Module, die das eigentliche Mapping übernehmen.

Ein einfaches LV wird normalerweise über das Target linear realisiert. Dieses bildet einen zusammenhängenden Bereich des virtuellen Devices auf einen ebenfalls zusammenhängenden Bereich des darunter liegenden Devices ab. Ein Beispiel:

test:~ # lvcreate -l 2 -n base vg0
 Logical volume "base" created
test:~ # dir /dev/vg0
total 0
lrwxrwxrwx 1 root root 7 Jul 4 09:33 base -> ../dm-0
test:~ # dir /dev/mapper
total 0
crw------- 1 root root 10, 236 Jul 4 09:28 control
lrwxrwxrwx 1 root root 7 Jul 4 09:33 vg0-base -> ../dm-0
test:~ # dmsetup table vg0-base
0 16384 linear 202:4 2048

Dabei ist das Gerät 202,4 das physische Laufwerk /dev/xvda4, das zum Aufbau der Volume Group vg0 verwendet wurde:

test:~ # dir /dev/xvda*
brw-rw---- 1 root disk 202, 0 Jul 7 16:16 /dev/xvda
brw-rw---- 1 root disk 202, 1 Jul 7 16:16 /dev/xvda1
brw-rw---- 1 root disk 202, 2 Jul 7 16:16 /dev/xvda2
brw-rw---- 1 root disk 202, 3 Jul 7 16:16 /dev/xvda3
brw-rw---- 1 root disk 202, 4 Jul 7 16:16 /dev/xvda4

Vorbereitung des Original-Volumes

Für die späteren Tests wird das LV mit definiertem Inhalt gefüllt, z.B. mit dem Text „base“. Da die Volume Group eine Physical Extent Size von 4 MB hat und das LV mit einer Größe von 2 Extents angelegt wurde, beträgt die Größe 8 MB:

test:~ # lvdisplay /dev/vg0/base | grep 'LV Size'
 LV Size 8.00 MiB

Zum Füllen wird der Befehl dd verwendet:

test:~ # for i in $(seq 1 2097152); do 
> echo -n base
> done | dd of=/dev/vg0/base bs=4
 2097152+0 records in
 2097152+0 records out
 8388608 bytes (8.4 MB) copied, 56.5065 s, 148 kB/s

Um zu überprüfen, ob die Daten korrekt geschrieben worden sind, kann ebenfalls dd verwendet werden:

test:~ # dd if=/dev/vg0/base bs=1 count=32
 basebasebasebasebasebasebasebase

Anlegen des Snapshot

Nun wird der Snapshot angelegt, und zwar mit einer Größe von 1 Extent (4 MB):

test:~ # lvcreate -s /dev/vg0/base -n snap -l 1
Logical volume "snap" created

Im Verzeichnis /dev/vg0 sieht man anschließend den Snapshot als eigenes LV. Außerdem ist natürlich weiterhin das Original-Volume vorhanden:

test:~ # dir /dev/vg0
 total 0
 lrwxrwxrwx 1 root root 7 Jul 4 09:35 base -> ../dm-0
 lrwxrwxrwx 1 root root 7 Jul 4 09:35 snap -> ../dm-1

Deutlich interessanter ist jedoch der Blick in das Verzeichnis /dev/mapper. Hier sieht man, dass neben den beiden nach außen sichtbaren Geräten (die man auch in /dev/vg0 findet) zwei weitere Geräte angelegt worden sind, nämlich vg0-base-real und vg0-snap-cow:

test:~ # dir /dev/mapper
 total 0
 crw------- 1 root root 10, 236 Jul 4 09:28 control
 lrwxrwxrwx 1 root root 7 Jul 4 09:35 vg0-base -> ../dm-0
 lrwxrwxrwx 1 root root 7 Jul 4 09:35 vg0-snap -> ../dm-1
 lrwxrwxrwx 1 root root 7 Jul 4 09:35 vg0-snap-cow -> ../dm-3
 lrwxrwxrwx 1 root root 7 Jul 4 09:35 vg0-base-real -> ../dm-2

Ebenfalls interessant ist die Ausgabe von dmsetup für diese Geräte:

test:~ # dmsetup table vg0-base
0 16384 snapshot-origin 253:2
test:~ # dmsetup table vg0-base-real
0 16384 linear 202:4 2048
test:~ # dmsetup table vg0-snap
0 16384 snapshot 253:2 253:3 P 8
test:~ # dmsetup table vg0-snap-cow
0 8192 linear 202:4 18432

Wie man sieht, ist durch den Snapshot eine weitere Mapping-Ebene hinzugekommen. Das bisherige Gerät für das Original-LV (vg0-base) ist nicht länger vom Typ linear, sondern stattdessen vom Typ snapshot-origin. Dieser verweist auf das Gerät vg0-base-real. Das neu hinzugekommene Gerät vg0-snap ist vom Typ snapshot. Dieser verweist ebenfalls auf das Gerät vg0-base-real sowie auf vg0-snap-cow. Diese beiden Geräte wiederum verhalten sich wie „normale“ LVs, d.h. sie verweisen auf einen Bereich des physischen Volumes.

lvm-snapshots

Betrachtet man die Größe der vier Block Devices, so sind die beiden Devices vg0-base und vg0-base-real natürlich jeweils 8 MB groß. vg0-base-real ist ja das anfangs angelegte Original-Volume, und vg0-base ist lediglich eine darüber gelegte Mapping-Schicht. Auch das Device vg0-snap ist (virtuell) 8 MB groß, da ein Snapshot immer die gleiche Größe hat wie das darunter liegende Original-Volume. Die COW-Tabelle vg0-snap-cow ist hingegen nur 4 MB groß, genau die Größe, die beim Anlegen des Snapshot angegeben wurde.

test:~ # blockdev --getsize64 /dev/mapper/vg0-base /dev/mapper/vg0-snap /dev/mapper/vg0-snap-cow /dev/mapper/vg0-base-real
 8388608
 8388608
 4194304
 8388608

Veränderungen am Snapshot

Was passiert, wenn der Snapshot mit Daten beschrieben wird?

test:~ # echo -n snapshot | dd of=/dev/vg0/snap bs=1
8+0 records in
8+0 records out
8 bytes (8 B) copied, 0.0341143 s, 0.2 kB/s

test:~ # dd if=/dev/mapper/vg0-snap bs=1 count=32
snapshotbasebasebasebasebasebase

test:~ # dd if=/dev/mapper/vg0-base bs=1 count=32
basebasebasebasebasebasebasebase

Erwartungsgemäß sind die Daten im Snapshot verändert worden, während das Original-Volume unverändert geblieben ist. Interessant ist der Blick auf die darunter liegenden Devices:

test:~ # dd if=/dev/mapper/vg0-base-real bs=1 count=32
basebasebasebasebasebasebasebase
test:~ # dd if=/dev/mapper/vg0-snap-cow | hexdump -C
00000000  53 6e 41 70 01 00 00 00  01 00 00 00 08 00 00 00  |SnAp............|
00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00001000  00 00 00 00 00 00 00 00  02 00 00 00 00 00 00 00  |................|
00001010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00002000  73 6e 61 70 73 68 6f 74  62 61 73 65 62 61 73 65  |snapshotbasebase|
00002010  62 61 73 65 62 61 73 65  62 61 73 65 62 61 73 65  |basebasebasebase|
*
00003000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00400000

Die veränderten Daten sind in die COW-Tabelle geschrieben worden, während das Original-LV unverändert geblieben ist. Auffällig ist, dass trotz der kleinen Änderung (4 Bytes) der in die COW-Tabelle geschriebene Block mit 4096 Bytes erheblich größer ist. Die Größe der pro Änderung geschriebenen Blöcke ist die Snapshot chunk size, die bei der Anlage des Snapshot eingestellt werden kann. Aufgrund der Belegung der COW-Tabelle in Chunks lässt sich mit gleichmäßig über den Snapshot verteilten kleinen Änderungen die COW-Tabelle füllen, so dass der Snapshot ungültig wird, obwohl die Gesamtmenge der veränderten Daten weit unterhalb der Größe des Snapshot liegt.

Veränderungen am Original-Volume

Was passiert, wenn das Original-Volume verändert wird?

test:~ # echo -n test | dd of=/dev/vg0/base bs=1 seek=4096
4+0 records in
4+0 records out
4 bytes (4 B) copied, 0.0174384 s, 0.2 kB/s

test:~ # dd if=/dev/mapper/vg0-base bs=1 count=32 skip=4096
testbasebasebasebasebasebasebase

test:~ # dd if=/dev/mapper/vg0-base-real bs=1 count=32 skip=4096
testbasebasebasebasebasebasebase

test:~ # dd if=/dev/mapper/vg0-snap-cow | hexdump -C
00000000 53 6e 41 70 01 00 00 00 01 00 00 00 08 00 00 00 |SnAp............|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00001000 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 |................|
00001010 01 00 00 00 00 00 00 00 03 00 00 00 00 00 00 00 |................|
00001020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00002000 73 6e 61 70 73 68 6f 74 62 61 73 65 62 61 73 65 |snapshotbasebase|
00002010 62 61 73 65 62 61 73 65 62 61 73 65 62 61 73 65 |basebasebasebase|
*
00004000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00400000

Dass die beiden Geräte vg0-base und vg0-base-real die Veränderung zeigen, ist nicht weiter überraschend. Spannender ist die COW-Tabelle: Auf den ersten Blick sieht es so aus, als hätte sich diese gar nicht verändert. Tatsächlich ist jedoch ein weiterer 4k-Block hinzugekommen, in den die ursprünglichen Daten („…basebasebasebase…“) kopiert worden sind. Daher ist die Veränderung im Snapshot nicht sichtbar. Die Tatsache, dass die COW-Tabelle in 4k-Blöcken belegt wird, ist übrigens auch der Grund, warum beim Beschreiben mittels seek=4096 weiter hinten in das Volume geschrieben worden ist.

Quellen / Links

Installieren des Linux-iSCSI-Target (LIO) unter openSUSE 12.2

Heute wollte ich mal wieder das Linux-iSCSI-Target (LIO) unter openSUSE 12.2 zum Laufen bringen. Die erforderlichen Module sind seit Version 2.6.38 im Kernel enthalten, allerdings müssen bei openSUSE die Tools zur Konfiguration (lio-utils, targetcli) nachträglich installiert werden. Das letzte Mal, dass ich eine solche Installation vorgenommen habe, liegt schon einige Zeit zurück, und damals habe ich mir dummerweise nicht aufgeschrieben, was dabei zu beachten ist. Außerdem hat sich bei LIO inzwischen einiges getan, einige Pakete sind deprecated, und die Installation hat sich dementsprechend geändert. Die Anleitung, die man im LIO-Wiki findet, funktioniert unter openSUSE leider nicht, da einige Python-Packages nicht verfügbar sind. Man muss daher ein wenig basteln.

Zunächst installiert man git (falls noch nicht vorhanden) sowie ein paar weitere Packages. Diese Aufgabe lässt sich noch bequem mittels zypper erledigen:

zypper in git python-devel python-ipaddr python-netifaces python-configobj \
    net-snmp-devel

Die oben erwähnten Python-Packages, die nicht mittels zypper installierbar sind, lädt man sich einfach aus dem Netz herunter. Da wäre zunächst epydoc, das man nach dem Herunterladen und Entpacken einfach mittels make install kompiliert und installiert. Die ebenfalls benötigten Pakete SimpleParse und urwid müssen im Python-Stil mittels python setup.py install installiert werden.

Als Werkzeug für die Konfiguration des LIO-Targets dient das Tool targetcli. Dieses kann zusammen mit weiteren erforderlichen Bibliotheken mittels git heruntergeladen werden. Wir beginnen zunächst mit der RTSlib, welche die API enthält, sowie configshell, das für die Text-Oberfläche verwendet wird:

git clone git://risingtidesystems.com/rtslib.git
(cd rtslib; ./setup.py install)
git clone git://risingtidesystems.com/configshell.git
(cd configshell; ./setup.py install)

Würde man die RTSlib über eine Paketverwaltung installieren (RPM o.ä.), würden auch noch ein paar Dateien an die richtige Stelle kopiert. (Zu sehen ist das z.B. in der Datei rpm/python-rtslib.spec.tmpl.) Bei einer manuellen Installation muss das Kopieren manuell erledigt werden:

cp -r rtslib/specs /var/target/fabric

Eigentlich sollte es jetzt mit targetcli weitergehen. Die Installation schlägt jedoch mit der Meldung „Module ‘tcm_dump’ not found“ fehl. Lustigerweise benötigt man anscheinend für targetcli die früher verwendeten und heute als deprecated gekennzeichneten lio-utils. Wir installieren selbige mal schnell:

git clone git://risingtidesystems.com/lio-utils.git
cd lio-utils
make
make install

Und schon ist man mit dem nächsten Problem konfrontiert: Die in den lio-utils enthaltenen Python-Module werden in das Verzeichnis /usr/local/lib/python2.7/site-packages installiert. Das scheint eine Besonderheit von OpenSUSE zu sein, denn das Installationsprogramm erwartet die Module in /usr/lib/python2.7/site-packages (ohne „local“) und legt dementsprechend einige symbolische Links falsch an. Zum Glück lässt sich das leicht korrigieren:

ln -sf /usr/local/lib/python2.7/site-packages/tcm_dump.py /usr/sbin/tcm_dump
ln -sf /usr/local/lib/python2.7/site-packages/tcm_fabric.py /usr/sbin/tcm_fabric
ln -sf /usr/local/lib/python2.7/site-packages/tcm_loop.py /usr/sbin/tcm_loop
ln -sf /usr/local/lib/python2.7/site-packages/tcm_node.py /usr/sbin/tcm_node
ln -sf /usr/local/lib/python2.7/site-packages/lio_dump.py /usr/sbin/lio_dump
ln -sf /usr/local/lib/python2.7/site-packages/lio_node.py /usr/sbin/lio_node

Außerdem müssen die frisch verlinkten Programme auch noch ausführbar gemacht werden:

chmod 755 /usr/sbin/tcm_* /usr/sbin/lio_*

Ob man abschließend wirklich noch targetcli installiert, ist Geschmackssache, denn die in den lio-utils enthaltenen Tools genügen eigentlich vollkommen, um das iSCSI-Target zu konfigurieren. (Details dazu findet man im „LIO User’s Reference Manual„.) Wenn man sich für die Installation von targetcli entscheidet, ist das aber auch nicht mehr weiter schwierig:

git clone git://risingtidesystems.com/targetcli.git
cd targetcli
./setup.py install

Bevor man nun wirklich iSCSI-Laufwerke anlegen kann, müssen die entsprechenden Kernel-Module geladen und das config-Dateisystem gemountet werden. Beides erledigt das init-Script /etc/init.d/target. Hat man dieses gestartet, kann über targetcli oder tcm_node / lio_node das iSCSI-Target konfiguriert werden.

Kopieren eines gesamten Laufwerks über eine DSL-Verbindung

Kürzlich wollte ich ein großes Laufwerk (genauer: ein Logical Volume) von einem lokalen auf einen entfernten Linux-Server kopieren. Problem: Der lokale Server ist nur mittels DSL angebunden, und das Volume ist 20 GB groß …

Da das Kopieren deutlich länger als 24 Stunden dauern würde, musste ich mit mindestens einem DSL-Disconnect rechnen. Somit konnte ich nicht einfach einen Kopierjob starten und das Ding beliebig lange laufen lassen. Andererseits wollte ich mir aber auch nicht die Mühe machen, einen Download-Manager (oder in diesem Fall: Upload-Manager) zu installieren.

Mittels ein paar einfache Linux-Befehlen ließ sich das Problem dann aber doch ganz elegant lösen. Das Kopieren erledigte folgender Befehl:

for i in $(seq -w 0 199); do
    file=$i.gz
    echo $file
    dd if=/dev/vg0/foo bs=100M count=1 skip=$i | gzip -c > $file
    md5sum -b $file >> md5.txt
    scp -i foo.key $file remote:/mnt/upload
    rm $file
done

Die Idee: Die 20 GB an Daten werden in 200 Pakete zu je 100 MB zerlegt. Jedes Paket wird mittels dd gelesen, wobei die Option skip dazu dient, an die richtige Stelle zu springen. Die Pakete werden dann gepackt, und zur späteren Kontrolle wird eine MD5-Prüfsumme berechnet. Anschließend kann das Paket zum Server kopiert und lokal gelöscht werden.

Nachdem das Kopieren abgeschlossen war, konnte ich auf dem Server zunächst prüfen, welche Pakete nicht sauber kopiert wurden. Dazu dient der folgende einfache Befehl:

md5sum -c md5.txt

Wie erwartet war genau das Paket kaputt, während dessen Übertragung der DSL-Reconnect erfolgt war. Das Paket konnte ich dann leicht noch einmal übertragen.

Zum Zusammensetzen auf dem Server dient der folgende Befehl:

for i in $(seq -w 0 199); do
    file=$i.gz
    echo $file
    gunzip -c $file | dd of=/dev/vg0/foo obs=100M seek=$i
done

Wichtig ist, dass hier seek anstelle von skip verwendet wird, um auf dem Ausgabegerät an die richtige Stelle zu springen. Außerdem darf nicht mit bs gearbeitet werden. Stattdessen kommt obs zum Einsatz. Die Option bs setzt sowohl die Input Block Size (ibs) als auch die Output Block Size (obs). Eine große Input Block Size führt jedoch dazu, dass nur ein Bruchteil der Daten verarbeitet wird, da der gunzip-Befehl nicht auf einen Schlag alle Daten liefern kann. Alternativ hätte man die Daten zunächst lokal entpacken können:

for i in $(seq -w 0 199); do
    file=$i.gz
    echo $file
    gunzip $file
    dd if=$file of=/dev/vg0/foo bs=100M seek=$i
    rm $file
done

Da das andere Verfahren aber ebenso gut funktioniert, habe ich mich für die Lösung entschieden.

Mein Leben

17.04.2003

Mein Leben ist nur ein Gefängnis.
Hab die Mauern selbst gezogen,
mir selber Lügen eingeredet,
mich mit mir selbst betrogen.

Mein Leben: Eine dunkle Straße.
Hab den falschen Weg gewählt
und kann doch nichts als weitergehen,
obwohl jeder Schritt mich quält.

Mein Leben ist nur eine Maske,
mein Ich dahinter gut versteckt.
Zeig Euch nur, was Ihr sehen wollt,
dass niemand meinen Schmerz entdeckt.

Silvestergedicht

Silvester 1996

Das Jahr verglüht in hellen Farben,
erstrahlt in blendend heißem Schmerz.
Gedanken, was das Morgen bringt,
zerreißen rücksichtslos mein Herz.

Erinnerung an schönes Damals
verblasst in zwanghaft lauten Tönen.
Graue Schatten, dünne Nebel
entstehen bald aus allem Schönen.

Ich schaue einsam in den Himmel,
wo Feuerwerk das Jahr vertreibt,
zerdrücke leise eine Träne
und frage mich, was noch verbleibt …

Verlust

Für Babs, 11. Mai 1996

Will alle Zeit der Welt
in Deine Hände legen,
dass Du Dein Herz, Dein Selbst sollst finden …
Doch sag mir nur: Warum?
Was ist passiert?
Dass sich in Dir die Zweifel regen,
Dich weiterhin an mich zu binden?

Was ist geschehen an dem Tag,
als morgens noch die Sonne blühte,
die Welt in hellen Farben stand;
und abends wir uns wieder sprachen
und ich mein Herz gebrochen fand?

Muss ich bei mir den Fehler suchen?
War ich zu schnell, zu ungestüm?
Hab Dir zu wenig Zeit gegeben?
Hilf mir! Lass mich nicht weiter zweifeln,
die Nacht verheult im Dunkeln liegen –
gib mir zurück mein Leben!

Ich habe Angst, was morgen bringt.
Der Traum, so schön und doch so flüchtig;
wird er schon bald vergangen sein
und die Gefühle: Null und nichtig?

Abschied

Für Katrin, 1. März 1996

„So endete das Sein für ihn
durch eine Kerze, die nicht schien …”

Hab’ Tausende Gelegenheiten
ungenutzt verstreichen lassen.
Begann für diese Dämlichkeiten
mich selbst zu hassen!

Nun will ich wenigstens die letzte
Gelegenheit, die ich noch habe,
beim Schopf ergreifen, und ich nehme
die Liebe für Dich mit ins Grabe …

Leb wohl, ach Welt, ich danke Dir
für all das Schöne, das Du mir
in dieser kurzen Zeit gegeben.

Geliebte, Freunde, die Ihr trauert,
lebt wohl, doch Liebe überdauert
bis in ein anderes Leben.

Noch ein Tag

Für Katrin, 25. Februar 1996

Noch ein Tag
vergangen, vertan, abgelaufen –
und nichts geändert.
Wollte Dinge tun,
hab sie nicht getan,
Dinge sagen,
hab sie nicht gesagt
aus Angst vor Deinen Gefühlen,
oder Nicht-Gefühlen?
Bin taub –
oder ist Dein Schweigen Reden?
Sagt: „Nein” mit jedem
nicht gesprochenen Wort?