terraform-provider-libvirt/libvirt/utils_domain_def.go

204 lines
5.4 KiB
Go

package libvirt
import (
"encoding/xml"
"fmt"
"log"
"strings"
libvirt "github.com/digitalocean/go-libvirt"
"libvirt.org/go/libvirtxml"
)
func getGuestForArchType(caps libvirtxml.Caps, arch string, virttype string) (libvirtxml.CapsGuest, error) {
for _, guest := range caps.Guests {
log.Printf("[TRACE] Checking for %s/%s against %s/%s\n", arch, virttype, guest.Arch.Name, guest.OSType)
if guest.Arch.Name == arch && guest.OSType == virttype {
log.Printf("[DEBUG] Found %d machines in guest for %s/%s", len(guest.Arch.Machines), arch, virttype)
return guest, nil
}
}
return libvirtxml.CapsGuest{}, fmt.Errorf("[DEBUG] Could not find any guests for architecture type %s/%s", virttype, arch)
}
func lookupMachine(machines []libvirtxml.CapsGuestMachine, targetmachine string) string {
for _, machine := range machines {
if machine.Name == targetmachine {
if machine.Canonical != "" {
return machine.Canonical
}
return machine.Name
}
}
return ""
}
func getCanonicalMachineName(caps libvirtxml.Caps, arch string, virttype string, targetmachine string) (string, error) {
guest, err := getGuestForArchType(caps, arch, virttype)
if err != nil {
return "", err
}
/* Machine entries can be in the guest.Arch.Machines level as well as
under each guest.Arch.Domains[].Machines */
name := lookupMachine(guest.Arch.Machines, targetmachine)
if name != "" {
return name, nil
}
for _, domain := range guest.Arch.Domains {
name := lookupMachine(domain.Machines, targetmachine)
if name != "" {
return name, nil
}
}
return "", fmt.Errorf("[WARN] Cannot find machine type %s for %s/%s in %v", targetmachine, virttype, arch, caps)
}
func getOriginalMachineName(caps libvirtxml.Caps, arch string, virttype string, targetmachine string) (string, error) {
guest, err := getGuestForArchType(caps, arch, virttype)
if err != nil {
return "", err
}
for _, machine := range guest.Arch.Machines {
if machine.Canonical != "" && machine.Canonical == targetmachine {
return machine.Name, nil
}
}
return targetmachine, nil // There wasn't a canonical mapping to this
}
func isMachineSupported(guest libvirtxml.CapsGuest, name string) bool {
for _, m := range guest.Arch.Machines {
if m.Name == name {
return true
}
}
for _, domain := range guest.Arch.Domains {
for _, m := range domain.Machines {
if m.Name == name {
return true
}
}
}
return false
}
// For backward compatibility Libvirt may pick the machine which has been
// implemented initially for an architecture as default machine type.
// This may not provide the hardware features expected and machine creation
// fails. To work around this, we pick a default type unless the user has
// specified one.
func getMachineTypeForArch(caps libvirtxml.Caps, arch string, virttype string) (string, error) {
guest, err := getGuestForArchType(caps, arch, virttype)
var machines []string
if err != nil {
return "", err
}
switch arch {
case "i686", "x86_64":
machines = []string{"pc"} // "q35"?
case "aarch64", "armv7l":
machines = []string{"virt", "vexpress-a15"}
case "s390x":
machines = []string{"s390-ccw-virtio"}
case "ppc64le", "ppc64":
machines = []string{"pseries"}
default:
return "", fmt.Errorf("[DEBUG] Could not get machine type for arch %s", arch)
}
for _, machine := range machines {
if isMachineSupported(guest, machine) {
return machine, nil
}
}
return "", fmt.Errorf("[DEBUG] Machine type for arch %s not available", arch)
}
// as kernal args allow duplicate keys, we use a list of maps
// we jump to a next map as soon as we find a duplicate
// key.
func splitKernelCmdLine(cmdLine string) []map[string]string {
var cmdLines []map[string]string
if len(cmdLine) == 0 {
return cmdLines
}
currCmdLine := make(map[string]string)
keylessCmdLineArgs := []string{}
argVals := strings.Split(cmdLine, " ")
for _, argVal := range argVals {
if !strings.Contains(argVal, "=") {
// keyless cmd line (eg: nosplash)
keylessCmdLineArgs = append(keylessCmdLineArgs, argVal)
continue
}
kv := strings.SplitN(argVal, "=", 2)
k, v := kv[0], kv[1]
// if the key is duplicate, start a new map
if _, ok := currCmdLine[k]; ok {
cmdLines = append(cmdLines, currCmdLine)
currCmdLine = make(map[string]string)
}
currCmdLine[k] = v
}
if len(currCmdLine) > 0 {
cmdLines = append(cmdLines, currCmdLine)
}
if len(keylessCmdLineArgs) > 0 {
cl := make(map[string]string)
cl["_"] = strings.Join(keylessCmdLineArgs, " ")
cmdLines = append(cmdLines, cl)
}
return cmdLines
}
func getHostArchitecture(virConn *libvirt.Libvirt) (string, error) {
type HostCapabilities struct {
XMLName xml.Name `xml:"capabilities"`
Host struct {
XMLName xml.Name `xml:"host"`
CPU struct {
XMLName xml.Name `xml:"cpu"`
Arch string `xml:"arch"`
}
}
}
info, err := virConn.Capabilities()
if err != nil {
return "", err
}
capabilities := HostCapabilities{}
err = xml.Unmarshal([]byte(info), &capabilities)
if err != nil {
return "", err
}
return capabilities.Host.CPU.Arch, nil
}
func getHostCapabilities(virConn *libvirt.Libvirt) (libvirtxml.Caps, error) {
// We should perhaps think of storing this on the connect object
// on first call to avoid the back and forth
caps := libvirtxml.Caps{}
capsXML, err := virConn.Capabilities()
if err != nil {
return caps, err
}
err = xml.Unmarshal([]byte(capsXML), &caps)
if err != nil {
return caps, err
}
log.Printf("[TRACE] Capabilities of host \n %+v", caps)
return caps, nil
}