Das Problem

Du migrierst von Kopano nach Grommunio. Das Tool der Wahl ist gromox-kdb2mt gepiped nach gromox-mt2exm. Die Dokumentation und diverse Anleitungen im Internet empfehlen das -c Flag (Continuous-Mode), damit der Import bei einzelnen Fehlern nicht abbricht.

Der Import läuft durch. Keine Fehlermeldung. Exit code 0.

Du loggst dich in die Grommunio WebApp ein — und siehst nur den leeren Posteingang und die Standard-Ordner. Von den 7.000 Nachrichten im Zabbix-Ordner, den 3.000 im srvmailgw-Archiv, den 1.800 aus ecodms: keine Spur.

In dieser Liste gibt es keine Elemente anzuzeigen

Willkommen in der Dunkelheit. Darth Vader wäre stolz.

TL;DR

Nicht -c verwenden. Das Flag ignoriert RPC-Timeouts bei der Ordnererstellung. Nachrichten werden trotzdem importiert, landen aber ohne parent_fid in der Datenbank — sie existieren, sind aber keinem Ordner zugeordnet. Vor dem Import gromox-http, gromox-midb und gromox-zcore neu starten. Dann ohne -c laufen lassen.

Die Diagnose

Das Erste was auffällt: Beim Import mit -c erscheinen ein paar harmlos wirkende Warnungen:

exm: allocate_message_id RPC failed (timeout?)
exm: allocate_message_id RPC failed (timeout?)
exm: allocate_message_id RPC failed (timeout?)

Mit -c werden diese ignoriert. Der Import macht weiter. Exit code 0.

Der Store hat danach immerhin 282 MB — da ist was. Nur nicht sichtbar.

Ein Blick in die exchange.sqlite3 des frisch migrierten Users (mail-chewbacca) zeigt das Problem:

sqlite3 /var/lib/gromox/user/rebellion.local/c3po/exmdb/exchange.sqlite3 \
  "SELECT COUNT(*) FROM messages WHERE parent_fid IS NULL;"
6927

6.927 Nachrichten ohne Ordnerzuordnung. Die Ordner selbst — zabbix, vzdump, ecodms und Konsorten — existieren nicht in der folders-Tabelle. Die Nachrichten wurden importiert, aber die Ordner, in die sie gehören sollten, wurden wegen der RPC-Timeouts nie angelegt.

-c hat die Fehler bei der Ordnererstellung verschluckt und anschließend die Nachrichten trotzdem importiert. Weil: Continuous-Mode.

Die Ursache

gromox-mt2exm -c bedeutet: Bei Fehlern weitermachen, nicht abbrechen.

Das klingt robust. Ist es auch — für unwichtige Fehler. Aber die allocate_message_id RPC-Aufrufe sind nicht unwichtig: Sie sind der Handshake mit dem gromox-http-Dienst, um neue Folder-IDs zu reservieren. Wenn dieser Aufruf bei der Ordnererstellung scheitert, wird der Ordner nicht angelegt. Die Nachrichten, die in diesen Ordner sollten, werden danach trotzdem importiert — aber parent_fid ist NULL.

Grommunio zeigt nur Nachrichten an, die einem Ordner zugeordnet sind. Die 6.927 Nachrichten existieren in der Datenbank, sind aber für die WebApp (und IMAP) unsichtbar.

Die RPC-Timeouts entstehen, weil gromox-http und gromox-midb bei einem großen Import (28.000+ Elemente) unter Last geraten. Ein Neustart der Services vor dem Import löst das Problem.

Die Lösung

Schritt 1: Services neustarten

systemctl restart gromox-http gromox-midb gromox-zcore
sleep 5
systemctl is-active gromox-http gromox-midb gromox-zcore

Schritt 2: Kaputten Store löschen und neu anlegen

# Variablenname: MIGUSER, NICHT USER — $USER ist eine reservierte Shell-Variable!
MIGUSER="c3po"
TARGET="${MIGUSER}@rebellion.local"

rm -rf /var/lib/gromox/user/rebellion.local/${MIGUSER}/
gromox-mkprivate ${TARGET}   # Volle Email-Adresse, nicht nur Username!

Schritt 3: Import ohne -c

export SQLPASS='<DEIN-DB-PASSWORT>'

gromox-kdb2mt \
    --sql-host=127.0.0.1 --sql-port=3307 \
    --sql-db=zarafa --sql-user=zarafaDbUser \
    --src-attach=/mnt/kopano-attach \
    --mbox-guid=<KORREKTE-GUID> -s 2>/tmp/kdb2mt_${MIGUSER}.log \
| gromox-mt2exm -u "${TARGET}" 2>&1

Kein -c. Wenn jetzt ein RPC-Timeout auftritt, bricht der Import ab — und du siehst es sofort.

Schritt 4: Verifikation

# Keine verwaisten Nachrichten?
sqlite3 /var/lib/gromox/user/rebellion.local/${MIGUSER}/exmdb/exchange.sqlite3 \
  "SELECT COUNT(*) FROM messages WHERE parent_fid IS NULL;"
# Erwartet: 0

# Store-Größe plausibel?
du -sh /var/lib/gromox/user/rebellion.local/${MIGUSER}/

# Log auf Fehler prüfen
grep -iE "error|fail|panic|timeout" /tmp/kdb2mt_${MIGUSER}.log

Schritt 5: Falls der WebApp-Layout komisch aussieht

Nach einem Re-Import kann das Grommunio-Theme zerschossen wirken (seltsame Icon-Positionen, leere Bereiche). Fix: In den Einstellungen ein anderes Theme auswählen, speichern, dann zurück zum Original-Theme. Das ist ein reines Frontend-Cache-Problem.

Weitere Fallstricke bei der Kopano-Migration

Weil wir schon dabei sind:

$USER ist reserviert. In SSH-Heredocs wird $USER durch den lokalen Usernamen des aufrufenden Systems ersetzt, nicht durch den Wert den du zugewiesen hast. Immer MIGUSER oder einen anderen Namen verwenden.

gromox-mkprivate braucht die volle Email-Adresse. gromox-mkprivate c3po schlägt fehl oder verhält sich unerwartet. Richtig: gromox-mkprivate c3po@rebellion.local.

Niemals einen automatischen Sync einrichten. gromox-mt2exm hat keine Duplikat-Erkennung. Ein zweiter Lauf über dieselbe Mailbox verdoppelt alle Nachrichten. Das ist kein theoretisches Problem — wenn du das einmal live erlebt hast und der Disk dabei auf 100% läuft, vergisst du es nicht mehr.

Die stores-Tabelle in Kopano lügt. Wenn ein User mehrere Stores hat (z.B. durch doppelte Anlage oder Case-Sensitivity-Probleme), zeigt die hierarchy-Tabelle möglicherweise hohe Objektzahlen für den falschen Store. Immer mit einem Test-Export (gromox-kdb2mt ... -s 2>&1 | head -30) prüfen ob der Store tatsächlich Daten enthält, bevor man ihn migriert.

Lessons Learned

Das -c Flag klingt wie ein Feature, ist aber ein Fallstrick wenn der Ziel-Server unter Last steht. “Weitermachen bei Fehlern” bedeutet bei Ordner-Erstellungsfehlern: Nachrichten landen im Nirvana.

Die Lösung ist einfach — Services neustarten, kein -c — aber der Fehler ist tückisch weil der Import scheinbar erfolgreich durchläuft. Wer nicht in der Datenbank nachschaut, merkt das Problem erst wenn der User sich beschwert dass seine Unterordner weg sind.

"Es gibt keinen Löffel."
"Es gibt keinen -c."