package proxmox import ( "context" "fmt" "net/url" "strings" ) // StoragePath liefert das Verzeichnis eines Proxmox-Speichers auf einem Knoten. // // Der SSH-Transport braucht ihn, um aus einer Volumenkennung // ("local:backup/vzdump-...") einen Dateipfad zu bilden. Die Kennung allein // genuegt nicht: Wo ein Speicher liegt, weiss nur der Knoten. // // Der Provider erfuellt damit die Schnittstelle StoragePathResolver. func (provider *Provider) StoragePath(resolveContext context.Context, nodeName string, storageIdentifier string) (string, error) { statusPath := fmt.Sprintf("/nodes/%s/storage/%s/status", url.PathEscape(nodeName), url.PathEscape(storageIdentifier)) var storageStatus struct { // Path ist das Verzeichnis des Speichers auf dem Knoten. Path string `json:"path"` // Type benennt die Art des Speichers. Type string `json:"type"` } if statusError := provider.client.get(resolveContext, statusPath, &storageStatus); statusError != nil { return "", fmt.Errorf("der speicher %q auf knoten %q war nicht abrufbar: %w", storageIdentifier, nodeName, statusError) } if strings.TrimSpace(storageStatus.Path) == "" { // Ein Speicher ohne Pfad ist kein Verzeichnisspeicher — etwa ZFS, LVM // oder Ceph. Dort liegt kein Archiv als Datei, und ein geratener Pfad // fuehrte ins Leere. Der Fall wird benannt, statt einen Pfad zu // erfinden. return "", fmt.Errorf("der speicher %q auf knoten %q ist vom typ %q und stellt kein "+ "verzeichnis bereit. sicherungsarchive lassen sich von dort nicht als datei lesen; "+ "waehlen sie einen verzeichnisbasierten speicher (dir, nfs, cifs) fuer die "+ "proxmox-sicherungen", storageIdentifier, nodeName, storageStatus.Type) } return storageStatus.Path, nil } // RemoveArchive entfernt ein Sicherungsarchiv vom Proxmox-Speicher. // // **Der Aufraeumschritt ist keine Nebensaechlichkeit.** vzdump legt das Archiv // auf dem Knoten ab; Syncova liest es und uebernimmt es ins eigene Repository. // Bliebe es liegen, fuellte jede Sicherung den Proxmox-Speicher weiter — und // zwar mit einer zweiten, unverwalteten Kopie derselben Daten, fuer die keine // Aufbewahrungsregel gilt. // // Ein Fehler beim Aufraeumen ist kein Grund, die Sicherung als gescheitert zu // werten: Die Daten liegen bereits im Repository. Er wird gemeldet, damit // niemand den vollen Speicher fuer ein Raetsel haelt. func (provider *Provider) RemoveArchive(removeContext context.Context, nodeName string, volumeIdentifier string) error { deletePath := fmt.Sprintf("/nodes/%s/storage/%s/content/%s", url.PathEscape(nodeName), url.PathEscape(provider.options.BackupStorageID), url.PathEscape(volumeIdentifier)) var taskIdentifier TaskIdentifier if deleteError := provider.client.delete(removeContext, deletePath, &taskIdentifier); deleteError != nil { return fmt.Errorf("das sicherungsarchiv %q liess sich nicht entfernen: %w", volumeIdentifier, deleteError) } return nil }