package kvm2 import ( "crypto/sha256" "encoding/hex" "encoding/xml" "gitea.home.hrajfrisbee.cz/kacerr/egress-proxies-operator/internal/provider" ) // Volume names in the host's pool (spec entries 4 and 10). The per-VM // volumes derive from the domain name; the base image from its catalogue // key or, for a URL image, from a hash of the URL. func overlayVolumeName(name string) string { return name + ".qcow2" } func seedVolumeName(name string) string { return name + "-seed.iso" } // baseVolumeName carries the provider entry because the base is shared by // every Proxy on a host but must not be shared by two entries: the kvm // provider builds base-. in the same pool from the same // catalogue shape, and two kvm2 entries pointing one key at different URLs // would otherwise agree on the name and disagree on the contents. func baseVolumeName(entry, key, format string) string { return "base-" + entry + "-" + key + "." + format } func markerVolumeName(baseName string) string { return baseName + ".sha256" } // urlImageKey stands in for the catalogue key of an image given as a URL: // the first 16 hex characters of sha256(url). func urlImageKey(url string) string { sum := sha256.Sum256([]byte(url)) return hex.EncodeToString(sum[:])[:16] } // volumeSpec is what StorageVolCreateXML needs. BackingPath and // BackingFormat are set for an overlay only. type volumeSpec struct { Name string CapacityBytes int64 Format string BackingPath string BackingFormat string } type volumeXML struct { XMLName xml.Name `xml:"volume"` Type string `xml:"type,attr"` Name string `xml:"name"` Capacity volumeCapacityXML `xml:"capacity"` Target volumeTargetXML `xml:"target"` BackingStore *backingStoreXML `xml:"backingStore"` } type volumeCapacityXML struct { Unit string `xml:"unit,attr"` Value int64 `xml:",chardata"` } type volumeTargetXML struct { Format volumeFormatXML `xml:"format"` } type volumeFormatXML struct { Type string `xml:"type,attr"` } type backingStoreXML struct { Path string `xml:"path"` Format volumeFormatXML `xml:"format"` } func validVolumeFormat(f string) bool { return f == provider.KVMFormatQcow2 || f == provider.KVMFormatRaw } func buildVolumeXML(v volumeSpec) (string, error) { if v.Name == "" { return "", permanentf("volume XML: name is empty") } if v.CapacityBytes <= 0 { return "", permanentf("volume XML for %q: capacity must be positive, got %d", v.Name, v.CapacityBytes) } if !validVolumeFormat(v.Format) { return "", permanentf("volume XML for %q: unknown format %q (want %s or %s)", v.Name, v.Format, provider.KVMFormatQcow2, provider.KVMFormatRaw) } x := volumeXML{ Type: "file", Name: v.Name, Capacity: volumeCapacityXML{Unit: "bytes", Value: v.CapacityBytes}, Target: volumeTargetXML{Format: volumeFormatXML{Type: v.Format}}, } if v.BackingPath != "" { if !validVolumeFormat(v.BackingFormat) { return "", permanentf("volume XML for %q: unknown backing format %q (want %s or %s)", v.Name, v.BackingFormat, provider.KVMFormatQcow2, provider.KVMFormatRaw) } x.BackingStore = &backingStoreXML{Path: v.BackingPath, Format: volumeFormatXML{Type: v.BackingFormat}} } out, err := xml.MarshalIndent(x, "", " ") if err != nil { return "", permanentf("volume XML for %q: %v", v.Name, err) } return string(out), nil }