Unterschiede
Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
Beide Seiten der vorigen RevisionVorhergehende ÜberarbeitungNächste Überarbeitung | Vorhergehende Überarbeitung | ||
dnbd3_fuse_cow [2022/09/08 16:07 CEST] – dnbd3-Autostart korr. verschoben chr | dnbd3_fuse_cow [2022/11/24 15:36 CET] (aktuell) – [cow_merger_service] bißchen verfl. chr | ||
---|---|---|---|
Zeile 77: | Zeile 77: | ||
== Ubuntu 22.04 == | == Ubuntu 22.04 == | ||
+ | |||
Beispiel Ubuntu 22.04 | Beispiel Ubuntu 22.04 | ||
Zeile 110: | Zeile 111: | ||
</ | </ | ||
- | === Bau des cow_merger_service | + | === Bau und Installation |
< | < | ||
Zeile 123: | Zeile 124: | ||
</ | </ | ||
- | Erzeugte Dateien (Serverprogramm und Konfigurationsdatei) werden in cow_merger_service/ | + | Erzeugte Dateien (Serverprogramm und Konfigurationsdatei) werden in cow_merger_service/ |
=== Autostart === | === Autostart === | ||
Zeile 149: | Zeile 150: | ||
… und führen anschließend den Befehl ' | … und führen anschließend den Befehl ' | ||
- | ==== Konfiguration ==== | + | ===== Konfiguration |
+ | |||
+ | ==== dnbd3-Server ==== | ||
+ | |||
+ | Die Konfigurationsdatei **server.conf** wird standardmässig in /etc/dnbd3 abgelegt. Eine Beispieldatei ist in im bauverzeichnis unter **pkg/ | ||
+ | |||
+ | Der dnbd3-Server wird üblicherweise beim Systemstart [[# | ||
+ | |||
+ | ==== cow_merger_service | ||
Serverseitig sollten 3 Verzeichnisse erstellt werden: | Serverseitig sollten 3 Verzeichnisse erstellt werden: | ||
Zeile 157: | Zeile 166: | ||
mkdir [OriginalImageDirectory] | mkdir [OriginalImageDirectory] | ||
mkdir [DestinationDirectory] | mkdir [DestinationDirectory] | ||
- | </ | ||
- | |||
- | === dnbd3-Server === | ||
- | |||
- | Die Konfiguration des dnbd3-Servers erfolgt per **server.conf**. Eine Beispielkonfiguration ist in **pkg/ | ||
- | |||
- | < | ||
- | dnbd3-server --config [Pfad zur Config] | ||
</ | </ | ||
Zeile 172: | Zeile 173: | ||
^Schlüssel | ^Schlüssel | ||
- | |WorkingDirectory | + | |WorkingDirectory |
- | |OriginalImageDirectory | Pfad | Ordner der die Originalen Image Dateien | + | |OriginalImageDirectory | Pfad | Ordner, der die originalen Imagedateien |
|DestinationDirectory | Pfad | In diesen Ordner werden die fertigen Images kopiert.| | |DestinationDirectory | Pfad | In diesen Ordner werden die fertigen Images kopiert.| | ||
- | |Urls | + | |Urls |
- | Sämtliche | + | Die genannten Werte müssen konfiguriert werden. Sollten sich die Serverprozesse cow_merger_service und der dnbd3-server auf dem selben System befinden, |
=== Serverstart === | === Serverstart === | ||
Zeile 216: | Zeile 217: | ||
Der Random Test führt zufällige Schreibvorgänge und Größenänderungen durch. Diese werden sowohl auf dem gemounteten Image als auch auf einer im Dateisystem befindlichen Kopie desselben durchgeführt. Beide Abbilder werden zum Abschluss auf Gleichheit getestet. | Der Random Test führt zufällige Schreibvorgänge und Größenänderungen durch. Diese werden sowohl auf dem gemounteten Image als auch auf einer im Dateisystem befindlichen Kopie desselben durchgeführt. Beide Abbilder werden zum Abschluss auf Gleichheit getestet. | ||
+ | |||
+ | Folgende Pfade werden bei den Tests verwendet: | ||
+ | |||
+ | * **mountPfad** Beliebiger Pfad an dem das Image gemounted wird, wichtig der Ordner muss leer sein. | ||
+ | * **tmpCowPfad** Beliebiger | ||
+ | * **Pfad im lokalen Dateisystem** Beliebiger | ||
==== Allgemeine Testvorbereitungen ==== | ==== Allgemeine Testvorbereitungen ==== | ||
Zeile 324: | Zeile 331: | ||
===== Funktionsweise ===== | ===== Funktionsweise ===== | ||
+ | ===Struktur der Daten=== | ||
+ | Die Datenstruktur ist in zwei Hauptteile unterteilt. Die eigentlichen Daten die die Änderungen enthalten und die dazugehörenden Metadaten. | ||
+ | Die Änderungen werden in der data Datei gespeichert während die Meta daten in der meta Datei gespeichert werden. | ||
+ | Die Daten werden in Blöcken gespeichert, | ||
+ | Ein cow block hat eine cow_block_metadata_t Struktur, die die entsprechenden Metadaten enthält. Die Metadaten werden verwendet, um festzustellen, | ||
+ | ob ein dnbd3-Block beschrieben wurde, wo dieser Block in der Datendatei gespeichert ist, wann er zuletzt geändert und wann er hochgeladen wurde. | ||
+ | Aber dazu später mehr. | ||
- | <note warning>To do</note> | + | |
+ | ===Blockmetadata=== | ||
+ | Die Datenstruktur zur Speicherung von " | ||
+ | Das gesamte L1-Array wird zu Beginn initialisiert und kann nicht in der Größe verändert werden, daher begrenzt die Größe des L1-Arrays die Gesamtgröße des Images. | ||
+ | Die L2 werden dynamisch erstellt, wenn sie benötigt werden. Zu Beginn sind also alle L1-Pointer Null. Die L2's sind Arrays, die 1024 cow_block_metadata_t structs enthalten. | ||
+ | |||
+ | <code> | ||
+ | typedef struct cow_block_metadata | ||
+ | { | ||
+ | atomic_int_least64_t offset; | ||
+ | atomic_uint_least64_t timeChanged; | ||
+ | atomic_uint_least64_t uploads; | ||
+ | atomic_char bitfield[40]; | ||
+ | } cow_block_metadata_t; | ||
+ | </code> | ||
+ | |||
+ | < | ||
+ | COW_L2_STORAGE_CAPACITY = 1024 * 320 * 4096 | ||
+ | COW_METADATA_STORAGE_CAPACITY = 320 * 4096 | ||
+ | </ | ||
+ | |||
+ | Jeder cow_block_metadata_t enthält ein 40 Byte langes, also 320 Bit großes Bitfeld. Das Bitfeld zeigt an, ob der entsprechende dnbd3-Block Daten enthält oder nicht. | ||
+ | Beginnt das Bitfeld z.B. mit 01..., so enthalten die ersten 4096 Byte keine Daten und die nächsten 4096 Byte enthalten Daten. | ||
+ | Jeder cow_block_metadata_t speichert also die Metadaten von bis zu 320*4096 Bytes, wenn alle Bits auf 1 gesetzt sind. | ||
+ | Das Offset-Feld enthält das Offset, an welcher Stelle | ||
+ | Die timeChanged Feld enthält die Unix-Zeit, zu der der Block zuletzt geändert wurde. | ||
+ | Sie ist 0, wenn er nie geändert wurde oder wenn die letzten Änderungen bereits hochgeladen wurden. | ||
+ | |||
+ | Die L2-Arrays und cow_block_metadata_t sind nach den ursprünglichen Offsets des Images sortiert. | ||
+ | So adressiert der erste L1-Zeiger, d.h. das erste L2-Array, die ersten 1024 * 320 * 4096 Bytes (L2Size * bitfieldsize * DNBD3Blocksize) der Daten und so weiter. | ||
+ | |||
+ | Um zum Beispiel die " | ||
+ | |||
+ | < | ||
+ | 4033085440 / ( COW_L2_STORAGE_CAPACITY ) ≈ 3.005 | ||
+ | </ | ||
+ | |||
+ | Dann würde man den fünften cow_block_metadata_t im L2-Array nehmen wegen | ||
+ | < | ||
+ | (4033085440 mod COW_L2_STORAGE_CAPACITY) / COW_METADATA_STORAGE_CAPACITY = 5 | ||
+ | </ | ||
+ | |||
+ | ===Leseanfrage=== | ||
+ | Wird eine Lese anfrage gestellt werden alle benötigten " | ||
+ | Falls ja werden die daten aus der Daten Datei geladen. Das Offset ist dabei das cow_block_metadata_t Offset plus 4096 * blocknummer im Cow Block. | ||
+ | Falls das bit im bitfield jedoch 0 ist, gibt es zwei Fälle. Ist das offset kleiner als die Origial Image Größe werden die Daten mit 0 aufegfüllt (die Origial Image Größe Variable kann bei einem truncate die kleiner ist als das uhrprünglich Image weiter reuziert werden). | ||
+ | Abdernfalls werden die Daten von dem dnbd3 server geladen. | ||
+ | Da die Leseanfragen an den dnbd3 server asyncron ausgeführt werden, gibt es eine workCounter variable, die Anzahl der Parallelen anfragen plus der einzelnen lokalen enthält. | ||
+ | Erst wenn diese Variable 0 ist, wird die Fuse-Anfrage beendet. | ||
+ | |||
+ | ===Schreibanfrage=== | ||
+ | Wenn bei einer Schreibanfrage der Anfang oder das Ende nicht mit einem Vielfachen von 4096 übereinstimmt, | ||
+ | Das liegt daran, dass jeder 4096-Byte-Block vollständige Daten benötigt, denn wenn das Bit im Bitfeld für diesen Block gesetzt ist, werden alle Daten lokal gelesen. | ||
+ | Um den Block aufzufüllen, | ||
+ | Liegt er außerhalb der ursprünglichen Imagegröße (weil z.B. das Image größer geworden ist), werden die fehlenden Bytes mit 0 aufgefüllt. | ||
+ | Die Schreibanfrage errechnet aus dem Offset die entsprechenden cow_block_metadata_t. | ||
+ | Wenn die entsprechende cow_block_metadata_t noch nicht existiert, wird sie angelegt. | ||
+ | Die Daten werden in die Datendatei geschrieben, | ||
+ | Dann wird das entsprechende Bit in den Bitfeldern gesetzt und die timeChanged aktualisiert. | ||
+ | Wenn mehr Daten zu schreiben sind, wird die nächste cow_block_metadata_t berechnet und die obigen Schritte werden wiederholt. | ||
+ | Die Variable workCounter wird auch hier verwendet, um sicherzustellen, | ||
+ | |||
+ | ===Block-Upload=== | ||
+ | Für das Hochladen von Blöcken gibt es einen Hintergrund-Thread, | ||
+ | ob timeChanged ungleich 0 ist und die Zeitdifferenz zwischen now und timeChanged größer als COW_MIN_UPLOAD_DELAY ist. | ||
+ | Ist dies der Fall, wird der Block hochgeladen. Die timeChanged Variable vor dem Upload wird zwischengespeichert. | ||
+ | Nach dem Hochladen wird timeChanged auf 0 gesetzt, wenn es noch die gleiche Zeit wie die zwischengespeicherte hat (wenn nicht, gab es eine Änderung während des Hochladens und es muss erneut hochgeladen werden). | ||
+ | Sobald das Image ausgehängt ist, wird COW_MIN_UPLOAD_DELAY ignoriert und alle Blöcke, die eine andere Zeit als 0 haben, werden hochgeladen. | ||
+ | Das Hochladen erfolgt über einen REST-Request. Es gibt zwei verschiedene Limits für die Anzahl der parallelen Uploads, diese können in der config.h konfiguriert werden. | ||
+ | |||
+ | ===Dateien=== | ||
+ | Wenn eine neue CoW-Sitzung gestartet wird, werden eine neue Meta-, Daten- und, falls in den Befehlszeilenargumenten festgelegt, eine status.txt-Datei erstellt. | ||
+ | |||
+ | ==status.txt== | ||
+ | Die Datei status.txt kann mit dem Kommandozeilenparameter --cowStatFile aktiviert werden. | ||
+ | |||
+ | Die Datei enthält die folgende Informationen: | ||
+ | < | ||
+ | uuid=< | ||
+ | state=backgroundUpload | ||
+ | inQueue=0 | ||
+ | modifiedBlocks=0 | ||
+ | idleBlocks=0 | ||
+ | totalBlocksUploaded=0 | ||
+ | activeUploads: | ||
+ | ulspeed=0.00 | ||
+ | </ | ||
+ | |||
+ | * Die **uuid** ist die Sitzungs-Uuid, | ||
+ | |||
+ | * Der **Status** ist **backgroundUpload**, | ||
+ | |||
+ | * **Queue** sind die CoW Blöcke, die gerade hochgeladen werden oder auf einen freien Slot warten. | ||
+ | |||
+ | * **ModifiedBlocks** sind CoW Blöcke, die Änderungen aufweisen, die noch nicht auf den Server hochgeladen wurden, weil die Änderungen zu aktuell sind. | ||
+ | |||
+ | * **totalBlocksUploaded** die Gesamtanzahl der CoW Blöcke, die seit dem Einhängen des Images hochgeladen wurden. | ||
+ | |||
+ | * **activeUploads** ist die Anzahl der Blöcke, die gerade hochgeladen werden. | ||
+ | |||
+ | * **ulspeed** die aktuelle Upload-Geschwindigkeit in kb/s. | ||
+ | |||
+ | Sobald alle Blöcke hochgeladen wurden, wird der Status auf erledigt gesetzt. Wenn Sie COW_DUMP_BLOCK_UPLOADS festlegen (in config.h), wird eine Liste aller Blöcke, sortiert nach der Anzahl der Uploads, in die Datei status.txt kopiert, nachdem der Block-Upload abgeschlossen ist. | ||
+ | |||
+ | Mit dem Kommandozeilenparameter --cowStatStdout wird die gleiche Ausgabe der Statistikdatei in stdout ausgegeben. | ||
+ | |||
+ | ==meta== | ||
+ | Die Metadatei enthält die folgenden Header: | ||
+ | < | ||
+ | // cowfile.h | ||
+ | typedef struct cowfile_metadata_header | ||
+ | { | ||
+ | uint64_t magicValue; | ||
+ | atomic_uint_least64_t imageSize; | ||
+ | int32_t version; | ||
+ | int32_t blocksize; | ||
+ | uint64_t originalImageSize; | ||
+ | uint64_t metaDataStart; | ||
+ | int32_t bitfieldSize; | ||
+ | int32_t nextL2; | ||
+ | atomic_uint_least64_t metadataFileSize; | ||
+ | atomic_uint_least64_t dataFileSize; | ||
+ | uint64_t maxImageSize; | ||
+ | uint64_t creationTime; | ||
+ | char uuid[40]; | ||
+ | char imageName[200]; | ||
+ | } cowfile_metadata_header_t; | ||
+ | </ | ||
+ | Nach diesem Header beginnt bei Byte 8192 die oben erwähnte l1- und dann die l2-Datenstruktur. | ||
+ | ==data== | ||
+ | Die Datendatei enthält den magicValue und am Offset 40 * 8 * 4096 (Kapazität eines cowfile_metadata_header_t) beginnt der erste Datenblock. | ||
+ | |||
+ | ==magic values in den Headern der Dateien== | ||
+ | Die magic values in beiden Dateien werden verwendet, um sicherzustellen, | ||
+ | < | ||
+ | // | ||
+ | #define COW_FILE_META_MAGIC_VALUE ((uint64_t)0xEBE44D6E72F7825E) // Magic Value to recognize a Cow meta file | ||
+ | #define COW_FILE_DATA_MAGIC_VALUE ((uint64_t)0xEBE44D6E72F7825F) // Magic Value to recognize a Cow data file | ||
+ | </ | ||
+ | |||
+ | ===Threads=== | ||
+ | Diese Erweiterung verwendet zwei neue Threads: | ||
+ | < | ||
+ | tidCowUploader | ||
+ | tidStatUpdater | ||
+ | </ | ||
+ | * **tidCowUploader** ist der Thread, der die Blöcke auf den Cow-Server hochlädt. | ||
+ | |||
+ | * **tidStatUpdater** aktualisiert die Statistiken in stdout oder die Statistikdateien (je nach Parametern). | ||
+ | |||
+ | ===Locks=== | ||
+ | Diese Erweiterung verwendet einen neuen Lock cow.l2CreateLock. Er wird verwendet, wenn ein neues L2-Array zugewiesen wird. | ||
+ | |||
+ | |||
+ | ===Config Variablen ==== | ||
+ | Die folgenden Konfigurationsvariablen wurden zu config.h hinzugefügt. Eine Änderung wird nur erfahrenen Nutzern empfohlen. | ||
+ | < | ||
+ | // | ||
+ | // +++++ COW +++++ | ||
+ | #define COW_BITFIELD_SIZE 40 // NEVER CHANGE THIS OR THE WORLD WILL ALSO END! | ||
+ | #define COW_FILE_META_MAGIC_VALUE ((uint64_t)0xEBE44D6E72F7825E) // Magic Value to recognize a Cow meta file | ||
+ | #define COW_FILE_DATA_MAGIC_VALUE ((uint64_t)0xEBE44D6E72F7825F) // Magic Value to recognize a Cow data file | ||
+ | #define COW_MIN_UPLOAD_DELAY 60 // in seconds | ||
+ | #define COW_STATS_UPDATE_TIME 5 // time in seconds the cow status files gets updated (while uploading blocks) | ||
+ | #define COW_MAX_PARALLEL_UPLOADS 10 // maximum number of parallel uploads | ||
+ | #define COW_MAX_PARALLEL_BACKGROUND_UPLOADS 2 // maximum number of parallel uploads while the image is still mounted | ||
+ | #define COW_URL_STRING_SIZE 500 // Max string size for an url | ||
+ | #define COW_SHOW_UL_SPEED 1 // enable display of ul speed in cow status file | ||
+ | #define COW_MAX_IMAGE_SIZE 1000LL * 1000LL * 1000LL * 1000LL; // Maximum size an image can have(tb*gb*mb*kb) | ||
+ | // +++++ COW API Endpoints +++++ | ||
+ | #define COW_API_CREATE " | ||
+ | #define COW_API_UPDATE " | ||
+ | #define COW_API_START_MERGE " | ||
+ | </ | ||
+ | |||
+ | * **COW_MIN_UPLOAD_DELAY** legt die Mindestzeit in Sekunden fest, die seit der letzten Änderung eines CoW Blocks verstrichen sein muss, bevor er hochgeladen wird. Ein größerer Wert verringert normalerweise das doppelte Hochladen von Blöcken. Ein kleinerer Wert verringert die Zeit für das abschließende Hochladen, nachdem das Image ausgehängt wurde. Wenn Sie COW_DUMP_BLOCK_UPLOADS setzen und den Kommandozeilenparameter --cowStatFile angeben, wird eine Liste aller Blöcke, sortiert nach der Anzahl der Uploads, in die Datei status.txt geschrieben, | ||
+ | |||
+ | * **COW_STATS_UPDATE_TIME** definiert die Aktualisierungsfrequenz der stdout-Ausgabe/ | ||
+ | |||
+ | * **COW_MAX_PARALLEL_BACKGROUND_UPLOADS** definiert die maximale Anzahl der parallelen Block-Uploads. Diese Zahl wird verwendet, wenn das Image noch eingehängt ist und der Benutzer es noch nutzt. | ||
+ | |||
+ | * **COW_MAX_PARALLEL_UPLOADS** definiert die maximale Anzahl der parallelen Block-Uploads. Diese Zahl wird verwendet, sobald das Image ausgehängt wurde, um die restlichen geänderten Blöcke hochzuladen. | ||
==== REST-API ==== | ==== REST-API ==== | ||
- | Die folgende REST API wird genutzt zur Komunikationund | + | Die folgende REST API wird genutzt zur Komunikation und Datenübertragung mit dem cow_merger_service. |
=== / | === / | ||
Zeile 427: | Zeile 622: | ||
| mergedBlocks | integer | | Yes | | | mergedBlocks | integer | | Yes | | ||
| totalBlocks | integer | | Yes | | | totalBlocks | integer | | Yes | | ||
+ | |||
+ | ==== Tests ==== | ||
+ | |||
+ | ===Standard Test=== | ||
+ | |||
+ | ==TestSingleBit== | ||
+ | Setzt das erste Bit des ersten dnbd3 Blocks auf 1 und das mittlere Bit des zweiten dnbd3 Blocks auf 1. | ||
+ | |||
+ | ==WriteOverTwoBlocks== | ||
+ | Dieser Test schreibt über zwei dnbd3 Blöcke. | ||
+ | |||
+ | ==WriteNotOnBlockBorder== | ||
+ | Dieser Test schreibt über drei Blöcke, jedoch beginnt er nicht an der Block grenze. | ||
+ | |||
+ | ==InterleavedTest== | ||
+ | Dieser Test schreibt mehrere dnbd3 Blöck mit verschiedenen Daten, jedoch lässt er Blöcke zwischendrin unbeschrieben. | ||
+ | |||
+ | ==WriteOverL2== | ||
+ | Dieser Test, testet das schreiben über eine L2 grenze. | ||
+ | |||
+ | ==MultipleWrites== | ||
+ | Dieser Test schreibt mehrmals auf dieselben Blöcke verschiedene Daten. Mit dem --delay Paramter kann die Wartezeit zwischen den Schreibvorgängen definiert werden. Dies ist nützlich, da je nach Delay die Blöcke zwischenzeitlich hochgeladen werden. | ||
+ | |||
+ | ==fileSizeChanges== | ||
+ | Testet Änderungen an der Dateigröße. Zuerst wird die Dateigröße um 2 * l2Capacity mit einem Truncate erhöht. Dann wird geprüft, ob alle Bits in dem neu zugewiesenen Speicherplatz auf 0 gesetzt sind. Dann werden Daten in die Datei geschrieben, | ||
+ | |||
+ | ==LongNonAlignedPattern== | ||
+ | Dieser Test schreibt ein langes Muster über 3 l2-Blöcke. Das Muster wiederholt Zeichen von 0 bis 254, ist also kein Vielfaches von 4096, was dazu führt, dass alle Blöcke mit unterschiedlichen Daten gefüllt werden. Außerdem ist dieser Test nicht blockorientiert. | ||
+ | |||
+ | ===Random Test=== | ||
+ | Dieser Test führt wie oben beschrieben zufällig Größenänderungen und Schreibvorgänge durch. Die Wahrscheinlichkeit für eine Größenänderung wird mit dem Macro RND_TRUNCATE_PROBABILITY definiert und ist standardmäßig 5 %. | ||
+ | Ansonsten wird ein Schreibvorgang ausgeführt. Des Weiteren gibt es noch das Macro RND_UNALIGNED_WRITE_PROBABILITY, | ||