Kuendigung B1, Task 1: einzelner vzdump und Nachschau auf der Ablage
backupNow() faehrt einen einmaligen vzdump (mode=stop, weil der Abbau die Maschine vorher ohnehin herunterfaehrt) und liefert die Auftragskennung. backupsFor() ist das eigentliche Fundament fuer den spaeteren Abbau: sie fragt nach, was auf der Ablage wirklich liegt, statt der Auftragskennung zu vertrauen — ein vzdump kann enden und trotzdem kein Archiv hinterlassen. $storage ist an beiden Methoden ein Pflichtparameter, keine Einstellung: der Client raet nicht, welche Ablage gemeint ist, und wiederholt damit nicht den fest verdrahteten "local"-Namen aus createBackupJob(). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>feat/versandtakt
parent
df64b17c77
commit
6ab868c522
|
|
@ -345,4 +345,30 @@ class FakeProxmoxClient implements ProxmoxClient
|
|||
|
||||
return $id;
|
||||
}
|
||||
|
||||
/** @var array<int, array{node: string, vmid: int, storage: string}> jeder einzelne vzdump-Lauf, der angestossen wurde */
|
||||
public array $backupCalls = [];
|
||||
|
||||
/**
|
||||
* Was auf der Ablage wirklich liegt — vom Auftrag getrennt gefuehrt, weil
|
||||
* genau das der Punkt ist: backupNow() hinterlaesst hier standardmaessig
|
||||
* NICHTS, ein Test muss das Archiv wie ein echtes vzdump extra eintragen.
|
||||
* Schluessel ist die VMID als String, nicht die Ablage — reicht fuer die
|
||||
* Faelle, die dieser Fake abbilden muss.
|
||||
*
|
||||
* @var array<string, array<int, array<string, mixed>>>
|
||||
*/
|
||||
public array $backups = [];
|
||||
|
||||
public function backupNow(string $node, int $vmid, string $storage): string
|
||||
{
|
||||
$this->backupCalls[] = ['node' => $node, 'vmid' => $vmid, 'storage' => $storage];
|
||||
|
||||
return 'UPID:pve:vzdump:'.$vmid;
|
||||
}
|
||||
|
||||
public function backupsFor(string $node, int $vmid, string $storage): array
|
||||
{
|
||||
return $this->backups[(string) $vmid] ?? [];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -253,4 +253,35 @@ class HttpProxmoxClient implements ProxmoxClient
|
|||
|
||||
return $jobId;
|
||||
}
|
||||
|
||||
public function backupNow(string $node, int $vmid, string $storage): string
|
||||
{
|
||||
// mode=stop, nicht snapshot: der Abbau faehrt die Maschine vorher
|
||||
// ohnehin herunter (siehe ArchiveAndTearDown), und eine angehaltene
|
||||
// Maschine liefert das saubere Abbild ohne jede Einfrier-Frage. Die
|
||||
// naechtlichen Sicherungen oben (createBackupJob) laufen dagegen
|
||||
// gegen eine noch laufende Maschine und brauchen deshalb snapshot.
|
||||
return (string) $this->http()->asForm()
|
||||
->post("/nodes/{$node}/vzdump", [
|
||||
'vmid' => $vmid,
|
||||
'storage' => $storage,
|
||||
'mode' => 'stop',
|
||||
'compress' => 'zstd',
|
||||
])
|
||||
->throw()->json('data');
|
||||
}
|
||||
|
||||
public function backupsFor(string $node, int $vmid, string $storage): array
|
||||
{
|
||||
// content=backup allein liefert die Ablage aller VMs; Proxmox traegt
|
||||
// die VMID jedes Eintrags im Feld `vmid`, darauf wird gefiltert statt
|
||||
// dem Dateinamen (`volid`) etwas anzusehen.
|
||||
$content = $this->http()
|
||||
->get("/nodes/{$node}/storage/{$storage}/content", ['content' => 'backup'])
|
||||
->throw()->json('data', []);
|
||||
|
||||
return collect($content)
|
||||
->filter(fn (array $item) => (int) ($item['vmid'] ?? 0) === $vmid)
|
||||
->values()->all();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,4 +118,30 @@ interface ProxmoxClient
|
|||
|
||||
/** Create a scheduled vzdump backup job for the VM; returns the job id. */
|
||||
public function createBackupJob(string $node, int $vmid, string $schedule): string;
|
||||
|
||||
/**
|
||||
* Faehrt einen einzelnen vzdump fuer diese VM und liefert die Auftragskennung
|
||||
* (UPID) — das Gegenstueck zu createBackupJob() fuer einen einmaligen Lauf,
|
||||
* wie ihn der Abbau einer gekuendigten Instanz braucht.
|
||||
*
|
||||
* `$storage` ist bewusst ein Parameter und keine Einstellung an diesem
|
||||
* Client: welche Ablage fuer ein Archiv gemeint ist, weiss nur der
|
||||
* Aufrufer (createBackupJob() schreibt heute fest verdrahtet nach
|
||||
* "local" — eine Annahme ueber die Einrichtung der Hosts, keine
|
||||
* gemessene Tatsache). Ein hier geratener Name wuerde auf einem Host
|
||||
* ohne diese Ablage erst beim ersten Abbau scheitern.
|
||||
*/
|
||||
public function backupNow(string $node, int $vmid, string $storage): string;
|
||||
|
||||
/**
|
||||
* Was auf der Ablage tatsaechlich liegt — nicht ob der Auftrag lief.
|
||||
*
|
||||
* Der Kern des Abbaus: ein vzdump kann mit einer Auftragskennung enden und
|
||||
* trotzdem kein Archiv hinterlassen (volle Ablage, abgebrochener Lauf,
|
||||
* Fehler im Gast). Diese Methode ist die Nachfrage, die vor jedem Loeschen
|
||||
* gestellt wird, statt der Auftragskennung zu vertrauen.
|
||||
*
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function backupsFor(string $node, int $vmid, string $storage): array;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
<?php // tests/Feature/Provisioning/OneOffBackupTest.php
|
||||
|
||||
use App\Services\Proxmox\FakeProxmoxClient;
|
||||
|
||||
it('faehrt einen einzelnen vzdump und liefert die Auftragskennung', function () {
|
||||
$pve = new FakeProxmoxClient;
|
||||
|
||||
$upid = $pve->backupNow('pve', 201, 'local');
|
||||
|
||||
expect($upid)->not->toBe('')
|
||||
->and($pve->backupCalls)->toHaveCount(1)
|
||||
->and($pve->backupCalls[0])->toMatchArray(['node' => 'pve', 'vmid' => 201, 'storage' => 'local']);
|
||||
});
|
||||
|
||||
it('sagt, was auf der Ablage wirklich liegt', function () {
|
||||
// Der Kern: nicht "der Auftrag lief", sondern "da liegt eine Datei".
|
||||
// Ein vzdump, der mit einem Fehler endet, hinterlaesst auch eine
|
||||
// Auftragskennung — nur eben kein Archiv.
|
||||
$pve = new FakeProxmoxClient;
|
||||
$pve->backups['201'] = [['volid' => 'local:backup/vzdump-qemu-201-2026_08_04.vma.zst', 'size' => 4096]];
|
||||
|
||||
expect($pve->backupsFor('pve', 201, 'local'))->toHaveCount(1);
|
||||
});
|
||||
|
||||
it('liefert eine leere Liste, wenn nichts da ist', function () {
|
||||
expect((new FakeProxmoxClient)->backupsFor('pve', 999, 'local'))->toBe([]);
|
||||
});
|
||||
Loading…
Reference in New Issue