From ccf4dff4f872291b5cf131cbdfb3d3c5b2f0dd47 Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Fri, 7 Jul 2017 17:40:35 +0200 Subject: use github.com/libvirt-go-xml Replace the custom XML structs with libvirt's offcial ones. This resolves #143. Signed-off-by: Thomas Hipp --- libvirt/disk_def.go | 74 ++++----- libvirt/domain_def.go | 255 ++++++++------------------------ libvirt/network_def.go | 111 ++------------ libvirt/network_def_test.go | 25 ++-- libvirt/network_interface_def.go | 23 +-- libvirt/resource_libvirt_domain.go | 245 ++++++++++++++++++------------ libvirt/resource_libvirt_domain_test.go | 5 +- libvirt/resource_libvirt_network.go | 49 +++--- libvirt/utils_libvirt.go | 7 +- libvirt/utils_libvirt_test.go | 12 +- libvirt/volume_def.go | 2 +- 11 files changed, 302 insertions(+), 506 deletions(-) diff --git a/libvirt/disk_def.go b/libvirt/disk_def.go index 26cf513e..ed65c290 100644 --- a/libvirt/disk_def.go +++ b/libvirt/disk_def.go @@ -1,60 +1,40 @@ package libvirt import ( - "encoding/xml" "math/rand" + + "github.com/libvirt/libvirt-go-xml" ) const OUI = "05abcd" -type defDisk struct { - XMLName xml.Name `xml:"disk"` - Type string `xml:"type,attr"` - Device string `xml:"device,attr"` - Wwn string `xml:"wwn,omitempty"` - Format struct { - Type string `xml:"type,attr"` - } `xml:"format"` - Source struct { - File string `xml:"file,attr,omitempty"` - // retain Pool/Volume for compatibility with existing tfstate - Pool string `xml:"pool,attr,omitempty"` - Volume string `xml:"volume,attr,omitempty"` - } `xml:"source"` - Target struct { - Dev string `xml:"dev,attr"` - Bus string `xml:"bus,attr"` - } `xml:"target"` - Driver struct { - Name string `xml:"name,attr"` - Type string `xml:"type,attr"` - } `xml:"driver"` -} - -func newDefDisk() defDisk { - disk := defDisk{} - disk.Type = "file" - disk.Device = "disk" - disk.Format.Type = "qcow2" - disk.Target.Bus = "virtio" - - disk.Driver.Name = "qemu" - disk.Driver.Type = "qcow2" - - return disk +func newDefDisk() libvirtxml.DomainDisk { + return libvirtxml.DomainDisk{ + Type: "file", + Device: "disk", + Target: &libvirtxml.DomainDiskTarget{ + Bus: "virtio", + }, + Driver: &libvirtxml.DomainDiskDriver{ + Name: "qemu", + Type: "qcow2", + }, + } } -func newCDROM() defDisk { - disk := defDisk{} - disk.Type = "file" - disk.Device = "cdrom" - disk.Target.Dev = "hda" - disk.Target.Bus = "ide" - - disk.Driver.Name = "qemu" - disk.Driver.Type = "raw" - - return disk +func newCDROM() libvirtxml.DomainDisk { + return libvirtxml.DomainDisk{ + Type: "file", + Device: "cdrom", + Target: &libvirtxml.DomainDiskTarget{ + Dev: "hda", + Bus: "ide", + }, + Driver: &libvirtxml.DomainDiskDriver{ + Name: "qemu", + Type: "raw", + }, + } } func randomWWN(strlen int) string { diff --git a/libvirt/domain_def.go b/libvirt/domain_def.go index c656d095..250fc642 100644 --- a/libvirt/domain_def.go +++ b/libvirt/domain_def.go @@ -1,216 +1,77 @@ package libvirt import ( - "encoding/xml" "os" -) - -type defDomain struct { - XMLName xml.Name `xml:"domain"` - Name string `xml:"name"` - Type string `xml:"type,attr"` - Xmlns string `xml:"xmlns:qemu,attr"` - Os defOs `xml:"os"` - Memory defMemory `xml:"memory"` - VCpu defVCpu `xml:"vcpu"` - Metadata struct { - XMLName xml.Name `xml:"metadata"` - TerraformLibvirt defMetadata - } - Features struct { - Acpi string `xml:"acpi"` - Apic string `xml:"apic"` - Pae string `xml:"pae"` - } `xml:"features"` - Devices struct { - Controller []defController `xml:"controller,omitempty"` - Disks []defDisk `xml:"disk"` - NetworkInterfaces []defNetworkInterface `xml:"interface"` - Console []defConsole `xml:"console"` - Filesystems []defFilesystem `xml:"filesystem"` - Graphics *defGraphics `xml:"graphics,omitempty"` - // QEMU guest agent channel - QemuGAChannel struct { - Type string `xml:"type,attr"` - Source struct { - Mode string `xml:"mode,attr"` - } `xml:"source"` - Target struct { - Type string `xml:"type,attr"` - Name string `xml:"name,attr"` - } `xml:"target"` - } `xml:"channel"` - Rng struct { - Model string `xml:"model,attr"` - Backend struct { - Model string `xml:"model,attr"` - } `xml:"backend"` - } `xml:"rng"` - } `xml:"devices"` - CmdLine struct { - XMLName xml.Name `xml:"qemu:commandline"` - Cmd []defCmd `xml:"qemu:arg"` - } - Cpu struct { - Mode string `xml:"mode,attr,omitempty"` - } `xml:"cpu,omitempty"` -} - -type defGraphics struct { - Type string `xml:"type,attr"` - Autoport string `xml:"autoport,attr"` - Listen struct { - Type string `xml:"type,attr"` - } `xml:"listen"` -} - -type defMetadata struct { - XMLName xml.Name `xml:"http://github.com/dmacvicar/terraform-provider-libvirt/ user_data"` - Xml string `xml:",cdata"` -} - -type defOs struct { - Type defOsType `xml:"type"` - Loader *defLoader `xml:"loader,omitempty"` - NvRam *defNvRam `xml:"nvram,omitempty"` -} - -type defOsType struct { - Arch string `xml:"arch,attr,omitempty"` - Machine string `xml:"machine,attr,omitempty"` - Name string `xml:",chardata"` -} - -type defMemory struct { - Unit string `xml:"unit,attr"` - Amount int `xml:",chardata"` -} - -type defVCpu struct { - Placement string `xml:"unit,attr"` - Amount int `xml:",chardata"` -} - -type defCmd struct { - Value string `xml:"value,attr"` -} - -type defLoader struct { - ReadOnly string `xml:"readonly,attr,omitempty"` - Type string `xml:"type,attr,omitempty"` - File string `xml:",chardata"` -} - -// /var/lib/libvirt/qemu/nvram/sled12sp1_VARS.fd -type defNvRam struct { - File string `xml:",chardata"` -} - -type defController struct { - Type string `xml:"type,attr,omitempty"` - Model string `xml:"model,attr,omitempty"` -} - -type defConsole struct { - Type string `xml:"type,attr"` - Source struct { - Path string `xml:"path,attr,omitempty"` - } `xml:"source"` - Target struct { - Type string `xml:"type,attr,omitempty"` - Port string `xml:"port,attr"` - } `xml:"target"` -} - -type defFilesystemReadOnly bool - -type defFilesystem struct { - Type string `xml:"type,attr"` - AccessMode string `xml:"accessmode,attr"` - ReadOnly defFilesystemReadOnly `xml:"readonly",omitempty` - Source struct { - Dir string `xml:"dir,attr,omitempty"` - } `xml:"source"` - Target struct { - Dir string `xml:"dir,attr,omitempty"` - } `xml:"target"` -} -// The filesystem element has a tag when -// the host directory cannot be written by the guest. When -// the tag is omitted the read-write permissions -// are granted. -// To show the empty tag we have to define a -// "alias" of bool and provide a custom marshaller for it. -func (r defFilesystemReadOnly) MarshalXML(e *xml.Encoder, start xml.StartElement) error { - if !r { - return nil - } - err := e.EncodeElement("", start) - return err -} + "github.com/libvirt/libvirt-go-xml" +) -func (r *defFilesystemReadOnly) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { - var value string - err := d.DecodeElement(&value, &start) - if err != nil { - return err +func newFilesystemDef() libvirtxml.DomainFilesystem { + return libvirtxml.DomainFilesystem{ + Type: "mount", // This is the only type used by qemu/kvm + AccessMode: "mapped", // A safe default value + ReadOnly: &libvirtxml.DomainFilesystemReadOnly{}, } - - *r = value == "" - - return nil -} - -func newFilesystemDef() defFilesystem { - fs := defFilesystem{} - - // This is the only type used by qemu/kvm - fs.Type = "mount" - - // A safe default value - fs.AccessMode = "mapped" - fs.ReadOnly = true - - return fs } // Creates a domain definition with the defaults // the provider uses -func newDomainDef() defDomain { - // libvirt domain definition - domainDef := defDomain{} +func newDomainDef() libvirtxml.Domain { + domainDef := libvirtxml.Domain{ + OS: &libvirtxml.DomainOS{ + Type: &libvirtxml.DomainOSType{ + Type: "hvm", + }, + }, + Memory: &libvirtxml.DomainMemory{ + Unit: "MiB", + Value: 512, + }, + VCPU: &libvirtxml.DomainVCPU{ + Placement: "static", + Value: 1, + }, + CPU: &libvirtxml.DomainCPU{}, + Devices: &libvirtxml.DomainDeviceList{ + Graphics: []libvirtxml.DomainGraphic{ + libvirtxml.DomainGraphic{ + Type: "spice", + AutoPort: "yes", + }, + }, + Channels: []libvirtxml.DomainChannel{ + libvirtxml.DomainChannel{ + Type: "unix", + Source: &libvirtxml.DomainChardevSource{ + Mode: "bind", + }, + Target: &libvirtxml.DomainChannelTarget{ + Type: "virtio", + Name: "org.qemu.guest_agent.0", + }, + }, + }, + RNGs: []libvirtxml.DomainRNG{ + libvirtxml.DomainRNG{ + Model: "virtio", + Backend: &libvirtxml.DomainRNGBackend{ + Model: "random", + }, + }, + }, + }, + Features: &libvirtxml.DomainFeatureList{ + PAE: &libvirtxml.DomainFeature{}, + ACPI: &libvirtxml.DomainFeature{}, + APIC: &libvirtxml.DomainFeatureAPIC{}, + }, + } + if v := os.Getenv("TERRAFORM_LIBVIRT_TEST_DOMAIN_TYPE"); v != "" { domainDef.Type = v } else { domainDef.Type = "kvm" } - domainDef.Xmlns = "" - - domainDef.Os = defOs{} - domainDef.Os.Type = defOsType{} - domainDef.Os.Type.Name = "hvm" - - domainDef.Memory = defMemory{} - domainDef.Memory.Unit = "MiB" - domainDef.Memory.Amount = 512 - - domainDef.VCpu = defVCpu{} - domainDef.VCpu.Placement = "static" - domainDef.VCpu.Amount = 1 - - domainDef.Devices.Graphics = &defGraphics{} - domainDef.Devices.Graphics.Type = "spice" - domainDef.Devices.Graphics.Autoport = "yes" - domainDef.Devices.Graphics.Listen.Type = "none" - - domainDef.Devices.QemuGAChannel.Type = "unix" - domainDef.Devices.QemuGAChannel.Source.Mode = "bind" - domainDef.Devices.QemuGAChannel.Target.Type = "virtio" - domainDef.Devices.QemuGAChannel.Target.Name = "org.qemu.guest_agent.0" - - domainDef.Devices.Rng.Model = "virtio" - domainDef.Devices.Rng.Backend.Model = "random" return domainDef } diff --git a/libvirt/network_def.go b/libvirt/network_def.go index bd758747..dcfa7eb0 100644 --- a/libvirt/network_def.go +++ b/libvirt/network_def.go @@ -3,101 +3,12 @@ package libvirt import ( "encoding/xml" "fmt" -) - -type defNetworkIpDhcpRange struct { - XMLName xml.Name `xml:"range,omitempty"` - - Start string `xml:"start,attr,omitempty"` - End string `xml:"end,attr,omitempty"` -} - -type defNetworkIpDhcpHost struct { - XMLName xml.Name `xml:"host,omitempty"` - - Ip string `xml:"ip,attr,omitempty"` - Mac string `xml:"mac,attr,omitempty"` - Name string `xml:"name,attr,omitempty"` -} - -type defNetworkIpDhcp struct { - XMLName xml.Name `xml:"dhcp,omitempty"` - - Ranges []*defNetworkIpDhcpRange `xml:"range,omitempty"` - Hosts []*defNetworkIpDhcpHost `xml:"host,omitempty"` -} - -type defNetworkIp struct { - XMLName xml.Name `xml:"ip,omitempty"` - - Address string `xml:"address,attr"` - Netmask string `xml:"netmask,attr,omitempty"` - Prefix int `xml:"prefix,attr,omitempty"` - Family string `xml:"family,attr,omitempty"` - Dhcp *defNetworkIpDhcp `xml:"dhcp,omitempty"` -} - -type defNetworkBridge struct { - XMLName xml.Name `xml:"bridge,omitempty"` - - Name string `xml:"name,attr,omitempty"` - Stp string `xml:"stp,attr,omitempty"` -} - -type defNetworkDomain struct { - XMLName xml.Name `xml:"domain,omitempty"` - - Name string `xml:"name,attr,omitempty"` - LocalOnly string `xml:"localOnly,attr,omitempty"` -} - -type defNetworkForward struct { - Mode string `xml:"mode,attr"` - Device string `xml:"dev,attr,omitempty"` - Nat *struct { - Addresses []*struct { - Start string `xml:"start,attr"` - End string `xml:"end,attr"` - } `xml:"address,omitempty"` - Ports []*struct { - Start string `xml:"start,attr"` - End string `xml:"end,attr"` - } `xml:"port,omitempty"` - } `xml:"nat,omitempty"` -} - -type defNetworkDns struct { - Host []*defDnsHost `xml:"host,omitempty"` - Forwarder []*defDnsForwarder `xml:"forwarder,omitempty"` -} -type defDnsHost struct { - Ip string `xml:"ip,attr"` - HostName []string `xml:"hostname"` -} - -type defDnsForwarder struct { - Domain string `xml:"domain,attr,omitempty"` - Address string `xml:"addr,attr,omitempty"` -} - -// network definition in XML, compatible with what libvirt expects -// note: we have to use pointers or otherwise golang's XML will not properly detect -// empty values and generate things like "" that -// make libvirt crazy... -type defNetwork struct { - XMLName xml.Name `xml:"network"` - - Name string `xml:"name,omitempty"` - Domain *defNetworkDomain `xml:"domain,omitempty"` - Bridge *defNetworkBridge `xml:"bridge,omitempty"` - Forward *defNetworkForward `xml:"forward,omitempty"` - Ips []*defNetworkIp `xml:"ip,omitempty"` - Dns *defNetworkDns `xml:"dns,omitempty"` -} + "github.com/libvirt/libvirt-go-xml" +) // Check if the network has a DHCP server managed by libvirt -func (net defNetwork) HasDHCP() bool { +func HasDHCP(net libvirtxml.Network) bool { if net.Forward != nil { if net.Forward.Mode == "nat" || net.Forward.Mode == "route" || net.Forward.Mode == "" { return true @@ -107,30 +18,30 @@ func (net defNetwork) HasDHCP() bool { } // Creates a network definition from a XML -func newDefNetworkFromXML(s string) (defNetwork, error) { - var networkDef defNetwork +func newDefNetworkFromXML(s string) (libvirtxml.Network, error) { + var networkDef libvirtxml.Network err := xml.Unmarshal([]byte(s), &networkDef) if err != nil { - return defNetwork{}, err + return libvirtxml.Network{}, err } return networkDef, nil } -func newDefNetworkfromLibvirt(network LibVirtNetwork) (defNetwork, error) { +func newDefNetworkfromLibvirt(network LibVirtNetwork) (libvirtxml.Network, error) { networkXmlDesc, err := network.GetXMLDesc(0) if err != nil { - return defNetwork{}, fmt.Errorf("Error retrieving libvirt domain XML description: %s", err) + return libvirtxml.Network{}, fmt.Errorf("Error retrieving libvirt domain XML description: %s", err) } - networkDef := defNetwork{} + networkDef := libvirtxml.Network{} err = xml.Unmarshal([]byte(networkXmlDesc), &networkDef) if err != nil { - return defNetwork{}, fmt.Errorf("Error reading libvirt network XML description: %s", err) + return libvirtxml.Network{}, fmt.Errorf("Error reading libvirt network XML description: %s", err) } return networkDef, nil } // Creates a network definition with the defaults the provider uses -func newNetworkDef() defNetwork { +func newNetworkDef() libvirtxml.Network { const defNetworkXML = ` default diff --git a/libvirt/network_def_test.go b/libvirt/network_def_test.go index 14901f2f..6b0f9901 100644 --- a/libvirt/network_def_test.go +++ b/libvirt/network_def_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/davecgh/go-spew/spew" + "github.com/libvirt/libvirt-go-xml" ) func init() { @@ -77,14 +78,14 @@ func TestNetworkDefUnmarshall(t *testing.T) { if b.Forward.Mode != "nat" { t.Errorf("wrong forward mode: '%s'", b.Forward.Mode) } - if len(b.Forward.Nat.Addresses) == 0 { - t.Errorf("wrong number of addresses: %s", b.Forward.Nat.Addresses) + if len(b.Forward.NAT.Addresses) == 0 { + t.Errorf("wrong number of addresses: %s", b.Forward.NAT.Addresses) } - if b.Forward.Nat.Addresses[0].Start != "1.2.3.4" { - t.Errorf("wrong forward start address: %s", b.Forward.Nat.Addresses[0].Start) + if b.Forward.NAT.Addresses[0].Start != "1.2.3.4" { + t.Errorf("wrong forward start address: %s", b.Forward.NAT.Addresses[0].Start) } - if len(b.Ips) == 0 { - t.Errorf("wrong number of IPs: %d", len(b.Ips)) + if len(b.IPs) == 0 { + t.Errorf("wrong number of IPs: %d", len(b.IPs)) } if bs, err := xmlMarshallIndented(b); err != nil { t.Fatalf("marshalling error\n%s", spew.Sdump(b)) @@ -106,17 +107,17 @@ func TestBrokenNetworkDefUnmarshall(t *testing.T) { } func TestHasDHCPNoForwardSet(t *testing.T) { - net := defNetwork{} + net := libvirtxml.Network{} - if net.HasDHCP() { + if HasDHCP(net) { t.Error("Expected to not have forward enabled") } } func TestHasDHCPForwardSet(t *testing.T) { - createNet := func(mode string) defNetwork { - return defNetwork{ - Forward: &defNetworkForward{ + createNet := func(mode string) libvirtxml.Network { + return libvirtxml.Network{ + Forward: &libvirtxml.NetworkForward{ Mode: mode, }, } @@ -124,7 +125,7 @@ func TestHasDHCPForwardSet(t *testing.T) { for _, mode := range []string{"nat", "route", ""} { net := createNet(mode) - if !net.HasDHCP() { + if !HasDHCP(net) { t.Errorf( "Expected to have forward enabled with forward set to be '%s'", mode) diff --git a/libvirt/network_interface_def.go b/libvirt/network_interface_def.go index fe3d36d3..0af0cdf1 100644 --- a/libvirt/network_interface_def.go +++ b/libvirt/network_interface_def.go @@ -1,8 +1,6 @@ package libvirt -import ( - "encoding/xml" -) +import "github.com/libvirt/libvirt-go-xml" // An interface definition, as returned/understood by libvirt // (see https://libvirt.org/formatdomain.html#elementsNICS) @@ -12,20 +10,5 @@ import ( // // // -type defNetworkInterface struct { - XMLName xml.Name `xml:"interface"` - Type string `xml:"type,attr"` - Mac struct { - Address string `xml:"address,attr"` - } `xml:"mac"` - Source struct { - Network string `xml:"network,attr,omitempty"` - Bridge string `xml:"bridge,attr,omitempty"` - Dev string `xml:"dev,attr,omitempty"` - Mode string `xml:"mode,attr"` - } `xml:"source"` - Model struct { - Type string `xml:"type,attr"` - } `xml:"model"` - waitForLease bool -} + +var waitForLeases map[libvirtxml.DomainInterface]bool diff --git a/libvirt/resource_libvirt_domain.go b/libvirt/resource_libvirt_domain.go index 924467a6..a3ee7a2e 100644 --- a/libvirt/resource_libvirt_domain.go +++ b/libvirt/resource_libvirt_domain.go @@ -15,11 +15,12 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" libvirt "github.com/libvirt/libvirt-go" + "github.com/libvirt/libvirt-go-xml" ) type DomainMeta struct { domain *libvirt.Domain - ifaces chan defNetworkInterface + ifaces chan libvirtxml.DomainInterface } var PoolSync = NewLibVirtPoolSync() @@ -141,7 +142,7 @@ func resourceLibvirtDomain() *schema.Resource { ForceNew: true, }, "autostart": &schema.Schema{ - Type: schema.TypeBool, + Type: schema.TypeBool, Optional: true, Required: false, }, @@ -183,21 +184,20 @@ func resourceLibvirtDomainCreate(d *schema.ResourceData, meta interface{}) error domainDef.Name = name.(string) } - if metadata, ok := d.GetOk("metadata"); ok { - domainDef.Metadata.TerraformLibvirt.Xml = metadata.(string) - } - if ignition, ok := d.GetOk("coreos_ignition"); ok { - var fw_cfg []defCmd + //var fw_cfg []defCmd + var fw_cfg []libvirtxml.DomainQEMUCommandlineArg ignitionKey, err := getIgnitionVolumeKeyFromTerraformID(ignition.(string)) if err != nil { return err } ign_str := fmt.Sprintf("name=opt/com.coreos/config,file=%s", ignitionKey) - fw_cfg = append(fw_cfg, defCmd{"-fw_cfg"}) - fw_cfg = append(fw_cfg, defCmd{ign_str}) - domainDef.CmdLine.Cmd = fw_cfg - domainDef.Xmlns = "http://libvirt.org/schemas/domain/qemu/1.0" + fw_cfg = append(fw_cfg, libvirtxml.DomainQEMUCommandlineArg{"-fw_cfg"}) + fw_cfg = append(fw_cfg, libvirtxml.DomainQEMUCommandlineArg{ign_str}) + QemuCmdline := &libvirtxml.DomainQEMUCommandline{ + Args: fw_cfg, + } + domainDef.QEMUCommandline = QemuCmdline } arch, err := getHostArchitecture(virConn) @@ -210,14 +210,21 @@ func resourceLibvirtDomainCreate(d *schema.ResourceData, meta interface{}) error } else { if graphics, ok := d.GetOk("graphics"); ok { graphics_map := graphics.(map[string]interface{}) + domainDef.Devices.Graphics = []libvirtxml.DomainGraphic{ + libvirtxml.DomainGraphic{}, + } if graphics_type, ok := graphics_map["type"]; ok { - domainDef.Devices.Graphics.Type = graphics_type.(string) + domainDef.Devices.Graphics[0].Type = graphics_type.(string) } if autoport, ok := graphics_map["autoport"]; ok { - domainDef.Devices.Graphics.Autoport = autoport.(string) + domainDef.Devices.Graphics[0].AutoPort = autoport.(string) } if listen_type, ok := graphics_map["listen_type"]; ok { - domainDef.Devices.Graphics.Listen.Type = listen_type.(string) + domainDef.Devices.Graphics[0].Listeners = []libvirtxml.DomainGraphicListener{ + libvirtxml.DomainGraphicListener{ + Type: listen_type.(string), + }, + } } } } @@ -225,7 +232,9 @@ func resourceLibvirtDomainCreate(d *schema.ResourceData, meta interface{}) error if cpu, ok := d.GetOk("cpu"); ok { cpu_map := cpu.(map[string]interface{}) if cpu_mode, ok := cpu_map["mode"]; ok { - domainDef.Cpu.Mode = cpu_mode.(string) + domainDef.CPU = &libvirtxml.DomainCPU{ + Mode: cpu_mode.(string), + } } } @@ -234,10 +243,12 @@ func resourceLibvirtDomainCreate(d *schema.ResourceData, meta interface{}) error if _, err := os.Stat(firmwareFile); os.IsNotExist(err) { return fmt.Errorf("Could not find firmware file '%s'.", firmwareFile) } - domainDef.Os.Loader = &defLoader{ - File: firmwareFile, - ReadOnly: "yes", - Type: "pflash", + domainDef.OS = &libvirtxml.DomainOS{ + Loader: &libvirtxml.DomainLoader{ + Path: firmwareFile, + Readonly: "yes", + Type: "pflash", + }, } if nvram, ok := d.GetOk("nvram"); ok { @@ -245,39 +256,58 @@ func resourceLibvirtDomainCreate(d *schema.ResourceData, meta interface{}) error if _, err := os.Stat(nvramFile); os.IsNotExist(err) { return fmt.Errorf("Could not find nvram file '%s'.", nvramFile) } - domainDef.Os.NvRam = &defNvRam{ - File: nvramFile, + domainDef.OS.NVRam = &libvirtxml.DomainNVRam{ + NVRam: nvramFile, } } } - domainDef.Memory.Amount = d.Get("memory").(int) - domainDef.VCpu.Amount = d.Get("vcpu").(int) + domainDef.Memory = &libvirtxml.DomainMemory{ + Value: uint(d.Get("memory").(int)), + Unit: "MiB", + } + domainDef.VCPU = &libvirtxml.DomainVCPU{ + Value: d.Get("vcpu").(int), + } if consoleCount, ok := d.GetOk("console.#"); ok { - var consoles []defConsole + var consoles []libvirtxml.DomainConsole for i := 0; i < consoleCount.(int); i++ { - console := defConsole{} + console := libvirtxml.DomainConsole{} consolePrefix := fmt.Sprintf("console.%d", i) console.Type = d.Get(consolePrefix + ".type").(string) - console.Target.Port = d.Get(consolePrefix + ".target_port").(string) + console.Target = &libvirtxml.DomainConsoleTarget{ + Port: d.Get(consolePrefix + ".target_port").(*uint), + } if source_path, ok := d.GetOk(consolePrefix + ".source_path"); ok { - console.Source.Path = source_path.(string) + console.Source = &libvirtxml.DomainChardevSource{ + Path: source_path.(string), + } } if target_type, ok := d.GetOk(consolePrefix + ".target_type"); ok { console.Target.Type = target_type.(string) } consoles = append(consoles, console) } - domainDef.Devices.Console = consoles + domainDef.Devices.Consoles = consoles } disksCount := d.Get("disk.#").(int) - var disks []defDisk + var disks []libvirtxml.DomainDisk var scsiDisk bool = false for i := 0; i < disksCount; i++ { - disk := newDefDisk() - disk.Target.Dev = fmt.Sprintf("vd%s", DiskLetterForIndex(i)) + disk := libvirtxml.DomainDisk{ + Type: "file", + Device: "disk", + Target: &libvirtxml.DomainDiskTarget{ + Bus: "virtio", + Dev: fmt.Sprintf("vd%s", DiskLetterForIndex(i)), + }, + Driver: &libvirtxml.DomainDiskDriver{ + Name: "qemu", + Type: "qcow2", + }, + } diskKey := fmt.Sprintf("disk.%d", i) diskMap := d.Get(diskKey).(map[string]interface{}) @@ -286,9 +316,9 @@ func resourceLibvirtDomainCreate(d *schema.ResourceData, meta interface{}) error disk.Target.Bus = "scsi" scsiDisk = true if wwn, ok := diskMap["wwn"].(string); ok { - disk.Wwn = wwn + disk.WWN = wwn } else { - disk.Wwn = randomWWN(10) + disk.WWN = randomWWN(10) } } diskVolume, err := virConn.LookupStorageVolByKey(volumeKey) @@ -300,19 +330,21 @@ func resourceLibvirtDomainCreate(d *schema.ResourceData, meta interface{}) error return fmt.Errorf("Error retrieving volume file: %s", err) } - disk.Source.File = diskVolumeFile + disk.Source = &libvirtxml.DomainDiskSource{ + File: diskVolumeFile, + } disks = append(disks, disk) } log.Printf("[DEBUG] scsiDisk: %s", scsiDisk) if scsiDisk { - controller := defController{Type: "scsi", Model: "virtio-scsi"} - domainDef.Devices.Controller = append(domainDef.Devices.Controller, controller) + controller := libvirtxml.DomainController{Type: "scsi", Model: "virtio-scsi"} + domainDef.Devices.Controllers = append(domainDef.Devices.Controllers, controller) } filesystemsCount := d.Get("filesystem.#").(int) - var filesystems []defFilesystem + var filesystems []libvirtxml.DomainFilesystem for i := 0; i < filesystemsCount; i++ { fs := newFilesystemDef() @@ -322,17 +354,25 @@ func resourceLibvirtDomainCreate(d *schema.ResourceData, meta interface{}) error fs.AccessMode = accessMode.(string) } if sourceDir, ok := fsMap["source"]; ok { - fs.Source.Dir = sourceDir.(string) + fs.Source = &libvirtxml.DomainFilesystemSource{ + Dir: sourceDir.(string), + } } else { return fmt.Errorf("Filesystem entry must have a 'source' set") } if targetDir, ok := fsMap["target"]; ok { - fs.Target.Dir = targetDir.(string) + fs.Target = &libvirtxml.DomainFilesystemTarget{ + Dir: targetDir.(string), + } } else { return fmt.Errorf("Filesystem entry must have a 'target' set") } if readonly, ok := fsMap["readonly"]; ok { - fs.ReadOnly = readonly.(string) == "1" + if readonly.(string) == "1" { + fs.ReadOnly = &libvirtxml.DomainFilesystemReadOnly{} + } else { + fs.ReadOnly = nil + } } filesystems = append(filesystems, fs) @@ -358,13 +398,16 @@ func resourceLibvirtDomainCreate(d *schema.ResourceData, meta interface{}) error } netIfacesCount := d.Get("network_interface.#").(int) - netIfaces := make([]defNetworkInterface, 0, netIfacesCount) + netIfaces := make([]libvirtxml.DomainInterface, 0, netIfacesCount) partialNetIfaces := make(map[string]pendingMapping, netIfacesCount) + waitForLeases = make(map[libvirtxml.DomainInterface]bool, netIfacesCount) for i := 0; i < netIfacesCount; i++ { prefix := fmt.Sprintf("network_interface.%d", i) - netIface := defNetworkInterface{} - netIface.Model.Type = "virtio" + netIface := libvirtxml.DomainInterface{} + netIface.Model = &libvirtxml.DomainInterfaceModel{ + Type: "virtio", + } // calculate the MAC address var mac string @@ -377,12 +420,14 @@ func resourceLibvirtDomainCreate(d *schema.ResourceData, meta interface{}) error return fmt.Errorf("Error generating mac address: %s", err) } } - netIface.Mac.Address = mac + netIface.MAC = &libvirtxml.DomainInterfaceMAC{ + Address: mac, + } // this is not passed to libvirt, but used by waitForAddress - netIface.waitForLease = false + waitForLeases[netIface] = false if waitForLease, ok := d.GetOk(prefix + ".wait_for_lease"); ok { - netIface.waitForLease = waitForLease.(bool) + waitForLeases[netIface] = waitForLease.(bool) } // connect to the interface to the network... first, look for the network @@ -390,7 +435,9 @@ func resourceLibvirtDomainCreate(d *schema.ResourceData, meta interface{}) error // when using a "network_name" we do not try to do anything: we just // connect to that network netIface.Type = "network" - netIface.Source.Network = n.(string) + netIface.Source = &libvirtxml.DomainInterfaceSource{ + Network: n.(string), + } } else if networkUUID, ok := d.GetOk(prefix + ".network_id"); ok { // when using a "network_id" we are referring to a "network resource" // we have defined somewhere else... @@ -405,7 +452,7 @@ func resourceLibvirtDomainCreate(d *schema.ResourceData, meta interface{}) error return fmt.Errorf("Error retrieving network name: %s", err) } networkDef, err := newDefNetworkfromLibvirt(network) - if !networkDef.HasDHCP() { + if !HasDHCP(networkDef) { continue } @@ -430,7 +477,7 @@ func resourceLibvirtDomainCreate(d *schema.ResourceData, meta interface{}) error } else { // no IPs provided: if the hostname has been provided, wait until we get an IP if len(hostname) > 0 { - if !netIface.waitForLease { + if !waitForLeases[netIface] { return fmt.Errorf("Cannot map '%s': we are not waiting for lease and no IP has been provided", hostname) } // the resource specifies a hostname but not an IP, so we must wait until we @@ -447,22 +494,32 @@ func resourceLibvirtDomainCreate(d *schema.ResourceData, meta interface{}) error } } netIface.Type = "network" - netIface.Source.Network = networkName + netIface.Source = &libvirtxml.DomainInterfaceSource{ + Network: networkName, + } } else if bridgeNameI, ok := d.GetOk(prefix + ".bridge"); ok { netIface.Type = "bridge" - netIface.Source.Bridge = bridgeNameI.(string) + netIface.Source = &libvirtxml.DomainInterfaceSource{ + Bridge: bridgeNameI.(string), + } } else if devI, ok := d.GetOk(prefix + ".vepa"); ok { netIface.Type = "direct" - netIface.Source.Dev = devI.(string) - netIface.Source.Mode = "vepa" + netIface.Source = &libvirtxml.DomainInterfaceSource{ + Dev: devI.(string), + Mode: "vepa", + } } else if devI, ok := d.GetOk(prefix + ".macvtap"); ok { netIface.Type = "direct" - netIface.Source.Dev = devI.(string) - netIface.Source.Mode = "bridge" + netIface.Source = &libvirtxml.DomainInterfaceSource{ + Dev: devI.(string), + Mode: "bridge", + } } else if devI, ok := d.GetOk(prefix + ".passthrough"); ok { netIface.Type = "direct" - netIface.Source.Dev = devI.(string) - netIface.Source.Mode = "passthrough" + netIface.Source = &libvirtxml.DomainInterfaceSource{ + Dev: devI.(string), + Mode: "passthrough", + } } else { // no network has been specified: we are on our own } @@ -472,7 +529,7 @@ func resourceLibvirtDomainCreate(d *schema.ResourceData, meta interface{}) error domainDef.Devices.Disks = disks domainDef.Devices.Filesystems = filesystems - domainDef.Devices.NetworkInterfaces = netIfaces + domainDef.Devices.Interfaces = netIfaces connectURI, err := virConn.GetURI() if err != nil { @@ -522,7 +579,7 @@ func resourceLibvirtDomainCreate(d *schema.ResourceData, meta interface{}) error domainMeta := DomainMeta{ domain, - make(chan defNetworkInterface, len(netIfaces)), + make(chan libvirtxml.DomainInterface, len(netIfaces)), } // populate interface channels @@ -602,26 +659,6 @@ func resourceLibvirtDomainUpdate(d *schema.ResourceData, meta interface{}) error d.Partial(true) - if d.HasChange("metadata") { - metadata := defMetadata{} - metadata.Xml = d.Get("metadata").(string) - metadataToXml, err := xml.Marshal(metadata) - if err != nil { - return fmt.Errorf("Error serializing libvirt metadata: %s", err) - } - - err = domain.SetMetadata(libvirt.DOMAIN_METADATA_ELEMENT, - string(metadataToXml), - "terraform-libvirt", - "http://github.com/dmacvicar/terraform-provider-libvirt/", - libvirt.DOMAIN_AFFECT_LIVE|libvirt.DOMAIN_AFFECT_CONFIG) - if err != nil { - return fmt.Errorf("Error changing domain metadata: %s", err) - } - - d.SetPartial("metadata") - } - if d.HasChange("cloudinit") { cloudinit, err := newDiskForCloudInit(virConn, d.Get("cloudinit").(string)) if err != nil { @@ -725,12 +762,11 @@ func resourceLibvirtDomainRead(d *schema.ResourceData, meta interface{}) error { } d.Set("name", domainDef.Name) - d.Set("metadata", domainDef.Metadata.TerraformLibvirt.Xml) - d.Set("vpu", domainDef.VCpu) + d.Set("vpu", domainDef.VCPU) d.Set("memory", domainDef.Memory) - d.Set("firmware", domainDef.Os.Loader) - d.Set("nvram", domainDef.Os.NvRam) - d.Set("cpu", domainDef.Cpu) + d.Set("firmware", domainDef.OS.Loader) + d.Set("nvram", domainDef.OS.NVRam) + d.Set("cpu", domainDef.CPU) d.Set("autostart", autostart) running, err := isDomainRunning(*domain) @@ -804,11 +840,11 @@ func resourceLibvirtDomainRead(d *schema.ResourceData, meta interface{}) error { } netIfaces := make([]map[string]interface{}, 0) - for i, networkInterfaceDef := range domainDef.Devices.NetworkInterfaces { + for i, networkInterfaceDef := range domainDef.Devices.Interfaces { // we need it to read old values prefix := fmt.Sprintf("network_interface.%d", i) - mac := strings.ToUpper(networkInterfaceDef.Mac.Address) + mac := strings.ToUpper(networkInterfaceDef.MAC.Address) netIface := map[string]interface{}{ "network_id": "", "network_name": "", @@ -847,12 +883,12 @@ func resourceLibvirtDomainRead(d *schema.ResourceData, meta interface{}) error { netIface["network_name"] = networkInterfaceDef.Source.Network // try to look for this MAC in the DHCP configuration for this VM - if networkDef.HasDHCP() { + if HasDHCP(networkDef) { hostnameSearch: - for _, ip := range networkDef.Ips { - if ip.Dhcp != nil { - for _, host := range ip.Dhcp.Hosts { - if strings.ToUpper(host.Mac) == netIface["mac"] { + for _, ip := range networkDef.IPs { + if ip.DHCP != nil { + for _, host := range ip.DHCP.Hosts { + if strings.ToUpper(host.MAC) == netIface["mac"] { log.Printf("[DEBUG] read: hostname for '%s': '%s'", netIface["mac"], host.Name) netIface["hostname"] = host.Name break hostnameSearch @@ -980,21 +1016,21 @@ func resourceLibvirtDomainStateRefreshFunc( } } -func hasNetworkAddress(iface defNetworkInterface, +func hasNetworkAddress(iface libvirtxml.DomainInterface, domain libvirt.Domain) (found bool, ignore bool, err error) { - if !iface.waitForLease { + if !waitForLeases[iface] { return false, true, nil } - mac := strings.ToUpper(iface.Mac.Address) + mac := strings.ToUpper(iface.MAC.Address) if mac == "" { log.Printf("[DEBUG] Can't wait without a mac address.\n") // we can't get the ip without a mac address return false, true, nil } - log.Printf("[DEBUG] waiting for network address for interface with hwaddr: '%s'\n", iface.Mac.Address) + log.Printf("[DEBUG] waiting for network address for interface with hwaddr: '%s'\n", iface.MAC.Address) ifacesWithAddr, err := getDomainInterfaces(domain) if err != nil { return false, false, fmt.Errorf("Error retrieving interface addresses: %s", err) @@ -1052,8 +1088,19 @@ func isDomainRunning(domain libvirt.Domain) (bool, error) { return state == libvirt.DOMAIN_RUNNING, nil } -func newDiskForCloudInit(virConn *libvirt.Connect, volumeKey string) (defDisk, error) { - disk := newCDROM() +func newDiskForCloudInit(virConn *libvirt.Connect, volumeKey string) (libvirtxml.DomainDisk, error) { + disk := libvirtxml.DomainDisk{ + Type: "file", + Device: "cdrom", + Target: &libvirtxml.DomainDiskTarget{ + Dev: "hda", + Bus: "ide", + }, + Driver: &libvirtxml.DomainDiskDriver{ + Name: "qemu", + Type: "raw", + }, + } diskVolume, err := virConn.LookupStorageVolByKey(volumeKey) if err != nil { @@ -1064,7 +1111,9 @@ func newDiskForCloudInit(virConn *libvirt.Connect, volumeKey string) (defDisk, e return disk, fmt.Errorf("Error retrieving volume file: %s", err) } - disk.Source.File = diskVolumeFile + disk.Source = &libvirtxml.DomainDiskSource{ + File: diskVolumeFile, + } return disk, nil } diff --git a/libvirt/resource_libvirt_domain_test.go b/libvirt/resource_libvirt_domain_test.go index 7eec6e23..a3c4b895 100644 --- a/libvirt/resource_libvirt_domain_test.go +++ b/libvirt/resource_libvirt_domain_test.go @@ -478,7 +478,6 @@ func testAccCheckLibvirtDomainExists(n string, domain *libvirt.Domain) resource. func testAccCheckIgnitionXML(domain *libvirt.Domain, volume *libvirt.StorageVol) resource.TestCheckFunc { return func(s *terraform.State) error { - var cmdLine []defCmd xmlDesc, err := domain.GetXMLDesc(0) if err != nil { return fmt.Errorf("Error retrieving libvirt domain XML description: %s", err) @@ -496,7 +495,7 @@ func testAccCheckIgnitionXML(domain *libvirt.Domain, volume *libvirt.StorageVol) return fmt.Errorf("Error reading libvirt domain XML description: %s", err) } - cmdLine = domainDef.CmdLine.Cmd + cmdLine := domainDef.QEMUCommandline.Args for i, cmd := range cmdLine { if i == 1 && cmd.Value != ignStr { return fmt.Errorf("libvirt domain fw_cfg XML is incorrect %s", cmd.Value) @@ -533,7 +532,7 @@ func testAccCheckLibvirtScsiDisk(n string, domain *libvirt.Domain) resource.Test if diskBus := disk.Target.Bus; diskBus != "scsi" { return fmt.Errorf("Disk bus is not scsi") } - if wwn := disk.Wwn; wwn != n { + if wwn := disk.WWN; wwn != n { return fmt.Errorf("Disk wwn %s is not equal to %s", wwn, n) } } diff --git a/libvirt/resource_libvirt_network.go b/libvirt/resource_libvirt_network.go index 05a1db4c..344440c8 100644 --- a/libvirt/resource_libvirt_network.go +++ b/libvirt/resource_libvirt_network.go @@ -4,12 +4,14 @@ import ( "fmt" "log" "net" + "strconv" "strings" "time" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" libvirt "github.com/libvirt/libvirt-go" + "github.com/libvirt/libvirt-go-xml" ) const ( @@ -156,7 +158,7 @@ func resourceLibvirtNetworkCreate(d *schema.ResourceData, meta interface{}) erro networkDef := newNetworkDef() networkDef.Name = d.Get("name").(string) - networkDef.Domain = &defNetworkDomain{ + networkDef.Domain = &libvirtxml.NetworkDomain{ Name: d.Get("domain").(string), } @@ -165,13 +167,15 @@ func resourceLibvirtNetworkCreate(d *schema.ResourceData, meta interface{}) erro if b, ok := d.GetOk("bridge"); ok { bridgeName = b.(string) } - networkDef.Bridge = &defNetworkBridge{ + networkDef.Bridge = &libvirtxml.NetworkBridge{ Name: bridgeName, - Stp: "on", + STP: "on", } // check the network mode - networkDef.Forward.Mode = strings.ToLower(d.Get("mode").(string)) + networkDef.Forward = &libvirtxml.NetworkForward{ + Mode: strings.ToLower(d.Get("mode").(string)), + } if networkDef.Forward.Mode == netModeIsolated || networkDef.Forward.Mode == netModeNat || networkDef.Forward.Mode == netModeRoute { if networkDef.Forward.Mode == netModeIsolated { @@ -179,13 +183,13 @@ func resourceLibvirtNetworkCreate(d *schema.ResourceData, meta interface{}) erro networkDef.Forward = nil } else if networkDef.Forward.Mode == netModeRoute { // there is no NAT when using a routed network - networkDef.Forward.Nat = nil + networkDef.Forward.NAT = nil } // some network modes require a DHCP/DNS server // set the addresses for DHCP if addresses, ok := d.GetOk("addresses"); ok { - ipsPtrsLst := []*defNetworkIp{} + ipsPtrsLst := []libvirtxml.NetworkIP{} for _, addressI := range addresses.([]interface{}) { address := addressI.(string) _, ipNet, err := net.ParseCIDR(address) @@ -210,46 +214,49 @@ func resourceLibvirtNetworkCreate(d *schema.ResourceData, meta interface{}) erro start[len(start)-1]++ // assign the .1 to the host interface - dni := defNetworkIp{ + dni := libvirtxml.NetworkIP{ Address: start.String(), - Prefix: ones, + Prefix: strconv.Itoa(ones), Family: family, } start[len(start)-1]++ // then skip the .1 end[len(end)-1]-- // and skip the .255 (for broadcast) - dni.Dhcp = &defNetworkIpDhcp{ - Ranges: []*defNetworkIpDhcpRange{ - &defNetworkIpDhcpRange{ + dni.DHCP = &libvirtxml.NetworkDHCP{ + Ranges: []libvirtxml.NetworkDHCPRange{ + libvirtxml.NetworkDHCPRange{ Start: start.String(), End: end.String(), }, }, } - ipsPtrsLst = append(ipsPtrsLst, &dni) + ipsPtrsLst = append(ipsPtrsLst, dni) } - networkDef.Ips = ipsPtrsLst + networkDef.IPs = ipsPtrsLst } if dns_forward_count, ok := d.GetOk("dns_forwarder.#"); ok { - var dns defNetworkDns + dns := libvirtxml.NetworkDNS{ + Forwarders: []libvirtxml.NetworkDNSForwarder{}, + } + for i := 0; i < dns_forward_count.(int); i++ { - forward := defDnsForwarder{} + forward := libvirtxml.NetworkDNSForwarder{} forwardPrefix := fmt.Sprintf("dns_forwarder.%d", i) if address, ok := d.GetOk(forwardPrefix + ".address"); ok { ip := net.ParseIP(address.(string)) if ip == nil { return fmt.Errorf("Could not parse address '%s'", address) } - forward.Address = ip.String() + forward.Addr = ip.String() } if domain, ok := d.GetOk(forwardPrefix + ".domain"); ok { forward.Domain = domain.(string) } - dns.Forwarder = append(dns.Forwarder, &forward) + dns.Forwarders = append(dns.Forwarders, forward) } - networkDef.Dns = &dns + networkDef.DNS = &dns } } else if networkDef.Forward.Mode == netModeBridge { @@ -347,7 +354,7 @@ func resourceLibvirtNetworkRead(d *schema.ResourceData, meta interface{}) error d.Set("running", active) addresses := []string{} - for _, address := range networkDef.Ips { + for _, address := range networkDef.IPs { // we get the host interface IP (ie, 10.10.8.1) but we want the network CIDR (ie, 10.10.8.0/24) // so we need some transformations... addr := net.ParseIP(address.Address) @@ -358,7 +365,9 @@ func resourceLibvirtNetworkRead(d *schema.ResourceData, meta interface{}) error if addr.To4() != nil { bits = net.IPv4len * 8 } - mask := net.CIDRMask(address.Prefix, bits) + + prefix, _ := strconv.Atoi(address.Prefix) + mask := net.CIDRMask(prefix, bits) network := addr.Mask(mask) addresses = append(addresses, fmt.Sprintf("%s/%d", network, address.Prefix)) } diff --git a/libvirt/utils_libvirt.go b/libvirt/utils_libvirt.go index ae7943c5..7918aac6 100644 --- a/libvirt/utils_libvirt.go +++ b/libvirt/utils_libvirt.go @@ -5,12 +5,13 @@ import ( "log" libvirt "github.com/libvirt/libvirt-go" + "github.com/libvirt/libvirt-go-xml" ) func getHostXMLDesc(ip, mac, name string) string { - dd := defNetworkIpDhcpHost{ - Ip: ip, - Mac: mac, + dd := libvirtxml.NetworkDHCPHost{ + IP: ip, + MAC: mac, Name: name, } xml, err := xmlMarshallIndented(dd) diff --git a/libvirt/utils_libvirt_test.go b/libvirt/utils_libvirt_test.go index d73e81f7..fa76cd84 100644 --- a/libvirt/utils_libvirt_test.go +++ b/libvirt/utils_libvirt_test.go @@ -3,6 +3,8 @@ package libvirt import ( "encoding/xml" "testing" + + "github.com/libvirt/libvirt-go-xml" ) func TestGetHostXMLDesc(t *testing.T) { @@ -12,18 +14,18 @@ func TestGetHostXMLDesc(t *testing.T) { data := getHostXMLDesc(ip, mac, name) - dd := defNetworkIpDhcpHost{} + dd := libvirtxml.NetworkDHCPHost{} err := xml.Unmarshal([]byte(data), &dd) if err != nil { t.Errorf("error %v", err) } - if dd.Ip != ip { - t.Errorf("expected ip %s, got %s", ip, dd.Ip) + if dd.IP != ip { + t.Errorf("expected ip %s, got %s", ip, dd.IP) } - if dd.Mac != mac { - t.Errorf("expected mac %s, got %s", mac, dd.Mac) + if dd.MAC != mac { + t.Errorf("expected mac %s, got %s", mac, dd.MAC) } if dd.Name != name { diff --git a/libvirt/volume_def.go b/libvirt/volume_def.go index 7d338128..f667fe10 100644 --- a/libvirt/volume_def.go +++ b/libvirt/volume_def.go @@ -115,4 +115,4 @@ func newDefBackingStoreFromLibvirt(baseVolume *libvirt.StorageVol) (defBackingSt backingStoreDef.Path = baseVolPath backingStoreDef.Format.Type = baseVolumeDef.Target.Format.Type return backingStoreDef, nil -} \ No newline at end of file +} -- cgit v1.2.3