96 lines
3.0 KiB
Go
96 lines
3.0 KiB
Go
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" }
|
|
|
|
func baseVolumeName(key, format string) string { return "base-" + 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
|
|
}
|