summaryrefslogtreecommitdiff
path: root/libvirt
diff options
context:
space:
mode:
authorDuncan Mac-Vicar P <dmacvicar@suse.de>2016-03-06 23:34:19 +0100
committerDuncan Mac-Vicar P <dmacvicar@suse.de>2016-03-06 23:34:19 +0100
commit42cdfa92fb562195e94093c0077153433fc4177e (patch)
tree64ed2be88810478915b7879a4da6a01ce047880b /libvirt
parent372ede62452a0a11cc2227738a90a478e82d37da (diff)
downloadterraform-provider-libvirt-42cdfa92fb562195e94093c0077153433fc4177e.tar
terraform-provider-libvirt-42cdfa92fb562195e94093c0077153433fc4177e.tar.gz
implement a volume provider
Diffstat (limited to 'libvirt')
-rw-r--r--libvirt/domain_def.go16
-rw-r--r--libvirt/provider.go1
-rw-r--r--libvirt/resource_libvirt_domain.go1
-rw-r--r--libvirt/resource_libvirt_volume.go182
-rw-r--r--libvirt/volume_def.go36
5 files changed, 220 insertions, 16 deletions
diff --git a/libvirt/domain_def.go b/libvirt/domain_def.go
index 97f7eae3..660c6e5c 100644
--- a/libvirt/domain_def.go
+++ b/libvirt/domain_def.go
@@ -4,22 +4,6 @@ import (
"encoding/xml"
)
-type defVolume struct {
- XMLName xml.Name `xml:"volume"`
- Name string `xml:"name"`
- Target struct {
- Format struct {
- Type string `xml:"type,attr"`
- } `xml:"format"`
- } `xml:"target"`
- BackingStore struct {
- Path string `xml:"path"`
- Format struct {
- Type string `xml:"type,attr"`
- } `xml:"format"`
- } `xml:"backingStore"`
-}
-
type defDisk struct {
XMLName xml.Name `xml:"disk"`
Type string `xml:"type,attr"`
diff --git a/libvirt/provider.go b/libvirt/provider.go
index 769c743c..82dc1a39 100644
--- a/libvirt/provider.go
+++ b/libvirt/provider.go
@@ -18,6 +18,7 @@ func Provider() terraform.ResourceProvider {
ResourcesMap: map[string]*schema.Resource{
"libvirt_domain": resourceLibvirtDomain(),
+ "libvirt_volume": resourceLibvirtVolume(),
},
ConfigureFunc: providerConfigure,
diff --git a/libvirt/resource_libvirt_domain.go b/libvirt/resource_libvirt_domain.go
index fe9510bf..e38f9130 100644
--- a/libvirt/resource_libvirt_domain.go
+++ b/libvirt/resource_libvirt_domain.go
@@ -74,6 +74,7 @@ func resourceLibvirtDomainCreate(d *schema.ResourceData, meta interface{}) error
rootVolumeDef.Name = "__terraform_" + d.Get("name").(string) + "-rootdisk"
rootVolumeDef.Target.Format.Type = "qcow2"
// use the base image as backing store
+ rootVolumeDef.BackingStore = new(defBackingStore)
rootVolumeDef.BackingStore.Format.Type = "qcow2"
baseVolPath, err := baseVol.GetPath()
if err != nil {
diff --git a/libvirt/resource_libvirt_volume.go b/libvirt/resource_libvirt_volume.go
new file mode 100644
index 00000000..08c5f767
--- /dev/null
+++ b/libvirt/resource_libvirt_volume.go
@@ -0,0 +1,182 @@
+package libvirt
+
+import (
+ "encoding/xml"
+ "fmt"
+ "io"
+ "log"
+ "net/http"
+ "strconv"
+ //"github.com/hashicorp/terraform/helper/resource"
+ "github.com/hashicorp/terraform/helper/schema"
+ libvirt "gopkg.in/alexzorin/libvirt-go.v2"
+)
+
+func resourceLibvirtVolume() *schema.Resource {
+ return &schema.Resource{
+ Create: resourceLibvirtVolumeCreate,
+ Read: resourceLibvirtVolumeRead,
+ Update: resourceLibvirtVolumeUpdate,
+ Delete: resourceLibvirtVolumeDelete,
+ Schema: map[string]*schema.Schema{
+ "name": &schema.Schema{
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "pool": &schema.Schema{
+ Type: schema.TypeString,
+ Optional: true,
+ Default: "default",
+ },
+ "source": &schema.Schema{
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ "size": &schema.Schema{
+ Type: schema.TypeInt,
+ Optional: true,
+ Default: -1,
+ },
+ "base_volume": &schema.Schema{
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ },
+ }
+}
+
+func remoteImageSize(url string) (int, error) {
+ response, err := http.Head(url)
+ if err != nil {
+ return 0, err
+ }
+ length, err := strconv.Atoi(response.Header.Get("Content-Length"))
+ if err != nil {
+ return 0, err
+ }
+ return length, nil
+}
+
+
+func resourceLibvirtVolumeCreate(d *schema.ResourceData, meta interface{}) error {
+ virConn := meta.(*Client).libvirt
+ if virConn == nil {
+ return fmt.Errorf("The libvirt connection was nil.")
+ }
+
+ poolName := d.Get("pool").(string)
+ if poolName == "" {
+ poolName = "default"
+ }
+ pool, err := virConn.LookupStoragePoolByName(poolName)
+ if err != nil {
+ return fmt.Errorf("can't find storage pool '%s'", poolName)
+ }
+
+ volumeDef := newDefVolume()
+ volumeDef.Name = d.Get("name").(string)
+
+ // an existing image was given, this mean we can't choose size
+ url := d.Get("source").(string)
+ if url != "" {
+
+ // source and size conflict
+ if d.Get("size").(int) != -1 {
+ return fmt.Errorf("'size' can't be specified when also 'source' is given (the size will be set to the size of the source image.")
+ }
+
+ size, err := remoteImageSize(url)
+ if err != nil {
+ return err
+ }
+ log.Printf("Remote image is: %d bytes", size)
+ volumeDef.Capacity.Unit = "B"
+ volumeDef.Capacity.Amount = size
+
+ } else {
+ if d.Get("size").(int) == -1 {
+ return fmt.Errorf("'size' needs to be specified if no 'source' is given.")
+ }
+
+ volumeDef.Capacity.Unit = "MB"
+ volumeDef.Capacity.Amount = d.Get("size").(int)
+ }
+
+ volumeDefXml, err := xml.Marshal(volumeDef)
+ if err != nil {
+ return fmt.Errorf("Error serializing libvirt volume: %s", err)
+ }
+
+ // create the volume
+ volume, err := pool.StorageVolCreateXML(string(volumeDefXml), 0)
+ if err != nil {
+ return fmt.Errorf("Error creating libvirt volume: %s", err)
+ }
+ // we use the key as the id
+ key, err := volume.GetKey()
+ if err != nil {
+ return fmt.Errorf("Error retrieving volume key: %s", err)
+ }
+ d.SetId(key)
+ log.Printf("[INFO] Volume ID: %s", d.Id())
+
+ // upload source if present
+ if url != "" {
+ stream, err := libvirt.NewVirStream(virConn, 0)
+ defer stream.Close()
+
+ volume.Upload(stream, 0, uint64(volumeDef.Capacity.Amount), 0)
+ response, err := http.Get(url)
+ defer response.Body.Close()
+ if err != nil {
+ return fmt.Errorf("Error while downloading %s: %s", url, err)
+ }
+
+ n, err := io.Copy(stream, response.Body)
+ if err != nil {
+ return fmt.Errorf("Error while downloading %s: %s", url, err)
+ }
+ log.Printf("%d bytes uploaded\n", n)
+ }
+
+
+ return resourceLibvirtVolumeRead(d, meta)
+}
+
+func resourceLibvirtVolumeRead(d *schema.ResourceData, meta interface{}) error {
+ virConn := meta.(*Client).libvirt
+ if virConn == nil {
+ return fmt.Errorf("The libvirt connection was nil.")
+ }
+
+ _, err := virConn.LookupStorageVolByKey(d.Id())
+ if err != nil {
+ return fmt.Errorf("Can't retrieve volume %s", d.Id())
+ }
+
+ return nil
+}
+
+func resourceLibvirtVolumeUpdate(d *schema.ResourceData, meta interface{}) error {
+ return fmt.Errorf("Couldn't update libvirt domain")
+}
+
+func resourceLibvirtVolumeDelete(d *schema.ResourceData, meta interface{}) error {
+ virConn := meta.(*Client).libvirt
+ if virConn == nil {
+ return fmt.Errorf("The libvirt connection was nil.")
+ }
+
+ volume, err := virConn.LookupStorageVolByKey(d.Id())
+ if err != nil {
+ return fmt.Errorf("Can't retrieve volume %s", d.Id())
+ }
+
+ err = volume.Delete(0)
+ if err != nil {
+ return fmt.Errorf("Can't delete volume %s", d.Id())
+ }
+
+
+ return nil
+}
diff --git a/libvirt/volume_def.go b/libvirt/volume_def.go
new file mode 100644
index 00000000..34abd5ca
--- /dev/null
+++ b/libvirt/volume_def.go
@@ -0,0 +1,36 @@
+package libvirt
+
+import (
+ "encoding/xml"
+)
+
+type defBackingStore struct {
+ Path string `xml:"path"`
+ Format struct {
+ Type string `xml:"type,attr"`
+ } `xml:"format"`
+}
+
+type defVolume struct {
+ XMLName xml.Name `xml:"volume"`
+ Name string `xml:"name"`
+ Target struct {
+ Format struct {
+ Type string `xml:"type,attr"`
+ } `xml:"format"`
+ } `xml:"target"`
+ Allocation int `xml:"allocation"`
+ Capacity struct {
+ Unit string `xml:"unit,attr"`
+ Amount int `xml:"chardata"`
+ } `xml:"capacity"`
+ BackingStore *defBackingStore `xml:"backingStore,omitempty"`
+}
+
+func newDefVolume() defVolume {
+ volumeDef := defVolume{}
+ volumeDef.Target.Format.Type = "qcow2"
+ volumeDef.Capacity.Unit = "GB"
+ volumeDef.Capacity.Amount = 1
+ return volumeDef
+}