package kvm import ( "encoding/xml" "fmt" ) const bytesPerGiB = 1 << 30 func overlayVolumeName(name string) string { return name + ".qcow2" } func seedVolumeName(name string) string { return name + "-seed.iso" } // volumeSpec describes a storage volume to create. A non-empty BackingPath // makes it a copy-on-write overlay of that file. type volumeSpec struct { Name string SizeBytes int64 Format string BackingPath string BackingFormat string } func overlayVolume(name string, sizeGB int64, basePath, baseFormat string) volumeSpec { return volumeSpec{ Name: overlayVolumeName(name), SizeBytes: sizeGB * bytesPerGiB, Format: formatQcow2, BackingPath: basePath, BackingFormat: baseFormat, } } func rawVolume(name string, sizeBytes int64) volumeSpec { return volumeSpec{Name: name, SizeBytes: sizeBytes, Format: formatRaw} } // buildVolumeXML renders the volume for StorageVolCreateXML. Capacity is // always given in bytes so no libvirt unit-suffix ambiguity (G vs GiB) can // creep in. func buildVolumeXML(v volumeSpec) (string, error) { x := volumeXML{ Type: "file", Name: v.Name, Capacity: sizeXML{Unit: "bytes", Value: v.SizeBytes}, Target: volumeTargetXML{Format: formatXML{Type: v.Format}}, } if v.BackingPath != "" { x.BackingStore = &backingStoreXML{Path: v.BackingPath, Format: formatXML{Type: v.BackingFormat}} } out, err := xml.Marshal(x) if err != nil { return "", fmt.Errorf("marshalling volume xml: %w", err) } return string(out), nil } type volumeXML struct { XMLName xml.Name `xml:"volume"` Type string `xml:"type,attr"` Name string `xml:"name"` Capacity sizeXML `xml:"capacity"` Target volumeTargetXML `xml:"target"` BackingStore *backingStoreXML `xml:"backingStore"` } type volumeTargetXML struct { Format formatXML `xml:"format"` } type formatXML struct { Type string `xml:"type,attr"` } type backingStoreXML struct { Path string `xml:"path"` Format formatXML `xml:"format"` }