summaryrefslogtreecommitdiff
path: root/libvirt
diff options
context:
space:
mode:
authorDean Smith <dean@zelotus.com>2017-09-08 12:49:37 +0100
committerDean Smith <dean@zelotus.com>2017-09-19 18:17:24 +0100
commit4d165b1838e4281d3a9847bc9a14d1ffe07a773c (patch)
tree952ce7015ca20e955e6746f13764106a73fa4e0f /libvirt
parenta605a9eb1332875feb9175563c7778d909ef5514 (diff)
downloadterraform-provider-libvirt-4d165b1838e4281d3a9847bc9a14d1ffe07a773c.tar
terraform-provider-libvirt-4d165b1838e4281d3a9847bc9a14d1ffe07a773c.tar.gz
Full support for arch,machine & emulator settings
Add emulator setting support Completes support from arch and machine when reading current status Adds a new function newDomainDefForConnection that takes a domainDef and populates with defaults from the current host which is more sensible than guessing across distributions Maps canonical machine types <> actual to avoid changes Due to the way libvirt works if you specific machine types like pc,isapc or q35 these get translated into 'canonical' forms, ie. the latest version When quering terraform will notice a change, as it would still be looking for pc and will get something like pc-i440fx-2.9. Code has been added to assume that if we see 'pc-i440fx-2.9' and it is canonical for pc then we translate back to pc for terraform. This could cause issues if you are specifically using pc-i440fx-2.9 as the machine type in your terraform file.
Diffstat (limited to 'libvirt')
-rw-r--r--libvirt/domain_def.go29
-rw-r--r--libvirt/resource_libvirt_domain.go49
-rw-r--r--libvirt/utils_domain_def.go50
-rw-r--r--libvirt/utils_libvirt.go15
-rw-r--r--libvirt/utils_libvirt_test.go96
5 files changed, 228 insertions, 11 deletions
diff --git a/libvirt/domain_def.go b/libvirt/domain_def.go
index 250fc642..1ade1f5a 100644
--- a/libvirt/domain_def.go
+++ b/libvirt/domain_def.go
@@ -1,9 +1,9 @@
package libvirt
import (
+ libvirt "github.com/libvirt/libvirt-go"
+ libvirtxml "github.com/libvirt/libvirt-go-xml"
"os"
-
- "github.com/libvirt/libvirt-go-xml"
)
func newFilesystemDef() libvirtxml.DomainFilesystem {
@@ -20,7 +20,9 @@ func newDomainDef() libvirtxml.Domain {
domainDef := libvirtxml.Domain{
OS: &libvirtxml.DomainOS{
Type: &libvirtxml.DomainOSType{
- Type: "hvm",
+ Type: "hvm",
+ Arch: "x86_64",
+ Machine: "pc",
},
},
Memory: &libvirtxml.DomainMemory{
@@ -75,3 +77,24 @@ func newDomainDef() libvirtxml.Domain {
return domainDef
}
+
+func newDomainDefForConnection(virConn *libvirt.Connect) (libvirtxml.Domain, error) {
+ d := newDomainDef()
+ caps, err := getHostCapabilities(virConn)
+ if err != nil {
+ return d, err
+ }
+ guest, err := getGuestForArchType(caps, d.OS.Type.Arch, d.OS.Type.Type)
+ if err != nil {
+ return d, err
+ }
+
+ d.Devices.Emulator = guest.Arch.Emulator
+
+ canonicalmachine, err := getCanonicalMachineName(caps, d.OS.Type.Arch, d.OS.Type.Type, d.OS.Type.Machine)
+ if err != nil {
+ return d, err
+ }
+ d.OS.Type.Machine = canonicalmachine
+ return d, nil
+}
diff --git a/libvirt/resource_libvirt_domain.go b/libvirt/resource_libvirt_domain.go
index 571b7359..64bdabe4 100644
--- a/libvirt/resource_libvirt_domain.go
+++ b/libvirt/resource_libvirt_domain.go
@@ -148,12 +148,14 @@ func resourceLibvirtDomain() *schema.Resource {
Required: false,
},
"machine": &schema.Schema{
- Type: schema.TypeString,
+ Type: schema.TypeString,
Optional: true,
+ Default: "pc",
},
"arch": &schema.Schema{
- Type: schema.TypeString,
+ Type: schema.TypeString,
Optional: true,
+ Default: "x86_64",
},
"boot_device": &schema.Schema{
Type: schema.TypeList,
@@ -163,6 +165,11 @@ func resourceLibvirtDomain() *schema.Resource {
Schema: bootDeviceSchema(),
},
},
+ "emulator": &schema.Schema{
+ Type: schema.TypeString,
+ Default: "/usr/bin/qemu-system-x86_64",
+ Optional: true,
+ },
},
}
}
@@ -208,8 +215,10 @@ func resourceLibvirtDomainCreate(d *schema.ResourceData, meta interface{}) error
return fmt.Errorf("The libvirt connection was nil.")
}
- domainDef := newDomainDef()
-
+ domainDef, err := newDomainDefForConnection(virConn)
+ if err != nil {
+ return err
+ }
if name, ok := d.GetOk("name"); ok {
domainDef.Name = name.(string)
}
@@ -270,7 +279,8 @@ func resourceLibvirtDomainCreate(d *schema.ResourceData, meta interface{}) error
domainDef.OS.Type.Arch = d.Get("arch").(string)
domainDef.OS.Type.Machine = d.Get("machine").(string)
-
+ domainDef.Devices.Emulator = d.Get("emulator").(string)
+
if firmware, ok := d.GetOk("firmware"); ok {
firmwareFile := firmware.(string)
if _, err := os.Stat(firmwareFile); os.IsNotExist(err) {
@@ -814,7 +824,11 @@ func resourceLibvirtDomainRead(d *schema.ResourceData, meta interface{}) error {
log.Printf("[DEBUG] read: obtained XML desc for domain:\n%s", xmlDesc)
- domainDef := newDomainDef()
+ domainDef, err := newDomainDefForConnection(virConn)
+ if err != nil {
+ return err
+ }
+
err = xml.Unmarshal([]byte(xmlDesc), &domainDef)
if err != nil {
return fmt.Errorf("Error reading libvirt domain XML description: %s", err)
@@ -831,7 +845,24 @@ func resourceLibvirtDomainRead(d *schema.ResourceData, meta interface{}) error {
d.Set("firmware", domainDef.OS.Loader)
d.Set("nvram", domainDef.OS.NVRam)
d.Set("cpu", domainDef.CPU)
+ d.Set("arch", domainDef.OS.Type.Arch)
d.Set("autostart", autostart)
+ d.Set("arch", domainDef.OS.Type.Arch)
+
+ caps, err := getHostCapabilities(virConn)
+ if err != nil {
+ return err
+ }
+ machine, err := getOriginalMachineName(caps, domainDef.OS.Type.Arch, domainDef.OS.Type.Type,
+ domainDef.OS.Type.Machine)
+ if err != nil {
+ return err
+ }
+ d.Set("machine", machine)
+
+ // Emulator is the same as the default don't set it in domainDef
+ // or it will show as changed
+ d.Set("emulator", domainDef.Devices.Emulator)
running, err := isDomainRunning(*domain)
if err != nil {
@@ -1012,7 +1043,11 @@ func resourceLibvirtDomainDelete(d *schema.ResourceData, meta interface{}) error
return fmt.Errorf("Error retrieving libvirt domain XML description: %s", err)
}
- domainDef := newDomainDef()
+ domainDef, err := newDomainDefForConnection(virConn)
+ if err != nil {
+ return err
+ }
+
err = xml.Unmarshal([]byte(xmlDesc), &domainDef)
if err != nil {
return fmt.Errorf("Error reading libvirt domain XML description: %s", err)
diff --git a/libvirt/utils_domain_def.go b/libvirt/utils_domain_def.go
new file mode 100644
index 00000000..2f7f74a6
--- /dev/null
+++ b/libvirt/utils_domain_def.go
@@ -0,0 +1,50 @@
+package libvirt
+
+import (
+ "fmt"
+ libvirtxml "github.com/libvirt/libvirt-go-xml"
+ "log"
+)
+
+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 architecure type %s/%s", virttype, arch)
+}
+
+func getCanonicalMachineName(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.Name == targetmachine {
+ if machine.Canonical != nil {
+ return *machine.Canonical, nil
+ }
+ return machine.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 != nil && *machine.Canonical == targetmachine {
+ return machine.Name, nil
+ }
+ }
+ return targetmachine, nil // There wasn't a canonical mapping to this
+}
diff --git a/libvirt/utils_libvirt.go b/libvirt/utils_libvirt.go
index cc86b1ec..22816769 100644
--- a/libvirt/utils_libvirt.go
+++ b/libvirt/utils_libvirt.go
@@ -5,7 +5,7 @@ import (
"log"
libvirt "github.com/libvirt/libvirt-go"
- "github.com/libvirt/libvirt-go-xml"
+ libvirtxml "github.com/libvirt/libvirt-go-xml"
)
func getHostXMLDesc(ip, mac, name string) string {
@@ -68,3 +68,16 @@ func getHostArchitecture(virConn *libvirt.Connect) (string, error) {
return capabilities.Host.CPU.Arch, nil
}
+
+func getHostCapabilities(virConn *libvirt.Connect) (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.GetCapabilities()
+ if err != nil {
+ return caps, err
+ }
+ xml.Unmarshal([]byte(capsXML), &caps)
+ log.Printf("[TRACE] Capabilities of host \n %+v", caps)
+ return caps, nil
+}
diff --git a/libvirt/utils_libvirt_test.go b/libvirt/utils_libvirt_test.go
index fa76cd84..38d90136 100644
--- a/libvirt/utils_libvirt_test.go
+++ b/libvirt/utils_libvirt_test.go
@@ -3,8 +3,11 @@ package libvirt
import (
"encoding/xml"
"testing"
+ "time"
+ libvirt "github.com/libvirt/libvirt-go"
"github.com/libvirt/libvirt-go-xml"
+ "os"
)
func TestGetHostXMLDesc(t *testing.T) {
@@ -32,3 +35,96 @@ func TestGetHostXMLDesc(t *testing.T) {
t.Errorf("expected name %s, got %s", name, dd.Name)
}
}
+
+func connect(t *testing.T) *libvirt.Connect {
+ conn, err := libvirt.NewConnect(os.Getenv("LIBVIRT_DEFAULT_URI"))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ return conn
+}
+
+func TestGetHostArchitecture(t *testing.T) {
+
+ conn := connect(t)
+ defer conn.Close()
+
+ arch, err := getHostArchitecture(conn)
+
+ if err != nil {
+ t.Errorf("error %v", err)
+ }
+
+ t.Logf("[DEBUG] arch - %s", arch)
+
+ if arch == "" {
+ t.Errorf("arch is blank.")
+ }
+}
+
+func TestGetCanonicalMachineName(t *testing.T) {
+
+ conn := connect(t)
+ defer conn.Close()
+ arch := "x86_64"
+ virttype := "hvm"
+ machine := "pc"
+
+ caps,err := getHostCapabilities(conn)
+ if err != nil {
+ t.Error(err)
+ }
+
+ name, err := getCanonicalMachineName(caps, arch, virttype, machine)
+
+ if err != nil {
+ t.Errorf("Could not get canonical name for %s/%s", arch, machine)
+ return
+ }
+
+ t.Logf("Canonical name for %s/%s = %s", arch, machine, name)
+}
+
+func TestGetOriginalMachineName(t *testing.T) {
+ conn := connect(t)
+ defer conn.Close()
+ arch := "x86_64"
+ virttype := "hvm"
+ machine := "pc"
+
+ caps,err := getHostCapabilities(conn)
+ if err != nil {
+ t.Error(err)
+ }
+
+ canonname, err := getCanonicalMachineName(caps, arch, virttype, machine)
+ if err != nil {
+ t.Error(err)
+ }
+ reversename, err := getOriginalMachineName(caps, arch, virttype, canonname)
+ if err != nil {
+ t.Error(err)
+ }
+ if reversename != machine {
+ t.Errorf("Cannot reverse canonical machine lookup")
+ }
+
+ t.Logf("Reverse canonical lookup for %s is %s which matches %s", canonname, reversename, machine)
+}
+
+func TestGetHostCapabilties(t *testing.T) {
+ start := time.Now()
+ conn := connect(t)
+ defer conn.Close()
+ caps,err := getHostCapabilities(conn)
+ if err != nil {
+ t.Errorf("Can't get host capabilties")
+ }
+ if caps.Host.UUID == "" {
+ t.Errorf("Host has no UUID!")
+ }
+
+ elapsed := time.Since(start)
+ t.Logf("[DEBUG] Get host capabilites took %s", elapsed)
+}