Skip to content

Commit

Permalink
Add directory-based storage pool resource
Browse files Browse the repository at this point in the history
This patch adds a resource type for directory-based libvirt storage pool.

Known issue:

The test `TestAccLibvirtPool_ManuallyDestroyed` and currently fail. I
failed to find out why yet.

Fixes #435.
  • Loading branch information
Zeeshan Ali authored and zeenix committed Apr 3, 2019
1 parent 6536f78 commit e3fdd8d
Show file tree
Hide file tree
Showing 6 changed files with 526 additions and 0 deletions.
15 changes: 15 additions & 0 deletions libvirt/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,21 @@ func getResourceFromTerraformState(resourceName string, state *terraform.State)

// ** resource specifics helpers **

// getPoolFromTerraformState lookup pool by name and return the libvirt pool from a terraform state
func getPoolFromTerraformState(name string, state *terraform.State, virConn libvirt.Connect) (*libvirt.StoragePool, error) {
rs, err := getResourceFromTerraformState(name, state)
if err != nil {
return nil, err
}

pool, err := virConn.LookupStoragePoolByUUIDString(rs.Primary.ID)
if err != nil {
return nil, err
}
log.Printf("[DEBUG]:The ID is %s", rs.Primary.ID)
return pool, nil
}

// getVolumeFromTerraformState lookup volume by name and return the libvirt volume from a terraform state
func getVolumeFromTerraformState(name string, state *terraform.State, virConn libvirt.Connect) (*libvirt.StorageVol, error) {
rs, err := getResourceFromTerraformState(name, state)
Expand Down
1 change: 1 addition & 0 deletions libvirt/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ func Provider() terraform.ResourceProvider {
"libvirt_domain": resourceLibvirtDomain(),
"libvirt_volume": resourceLibvirtVolume(),
"libvirt_network": resourceLibvirtNetwork(),
"libvirt_dir_pool": resourceLibvirtDirPool(),
"libvirt_cloudinit_disk": resourceCloudInitDisk(),
"libvirt_ignition": resourceIgnition(),
},
Expand Down
232 changes: 232 additions & 0 deletions libvirt/resource_libvirt_dir_pool.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
package libvirt

import (
"encoding/xml"
"fmt"
"log"

"github.com/hashicorp/terraform/helper/schema"
"github.com/libvirt/libvirt-go-xml"
)

func resourceLibvirtDirPool() *schema.Resource {
return &schema.Resource{
Create: resourceLibvirtDirPoolCreate,
Read: resourceLibvirtDirPoolRead,
Delete: resourceLibvirtDirPoolDelete,
Exists: resourceLibvirtDirPoolExists,
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"path": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"capacity": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
ForceNew: true,
},
"allocation": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
ForceNew: true,
},
"available": {
Type: schema.TypeString,
Computed: true,
Optional: true,
ForceNew: true,
},
"xml": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
ForceNew: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"xslt": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
},
},
},
},
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
}
}

func resourceLibvirtDirPoolCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Client)
if client.libvirt == nil {
return fmt.Errorf(LibVirtConIsNil)
}

poolName := d.Get("name").(string)

client.poolMutexKV.Lock(poolName)
defer client.poolMutexKV.Unlock(poolName)

// Check whether the storage pool already exists. Its name needs to be
// unique.
if _, err := client.libvirt.LookupStoragePoolByName(poolName); err == nil {
return fmt.Errorf("storage pool '%s' already exists", poolName)
}
log.Printf("[DEBUG] Pool with name '%s' does not exist yet", poolName)

poolPath := d.Get("path").(string)
poolDef := libvirtxml.StoragePool{
// We only support "dir" type at the moment
Type: "dir",
Name: poolName,
Target: &libvirtxml.StoragePoolTarget{
Path: poolPath,
},
}
data, err := xmlMarshallIndented(poolDef)
if err != nil {
return fmt.Errorf("Error serializing libvirt storage pool: %s", err)
}
log.Printf("[DEBUG] Generated XML for libvirt storage pool:\n%s", data)

data, err = transformResourceXML(data, d)
if err != nil {
return fmt.Errorf("Error applying XSLT stylesheet: %s", err)
}

// create the pool
pool, err := client.libvirt.StoragePoolDefineXML(data, 0)
if err != nil {
return fmt.Errorf("Error creating libvirt storage pool: %s", err)
}
defer pool.Free()

err = pool.Build(0)
if err != nil {
return fmt.Errorf("Error building libvirt storage pool: %s", err)
}

err = pool.SetAutostart(true)
if err != nil {
return fmt.Errorf("Error setting up libvirt storage pool: %s", err)
}

err = pool.Create(0)
if err != nil {
return fmt.Errorf("Error starting libvirt storage pool: %s", err)
}

err = pool.Refresh(0)
if err != nil {
return fmt.Errorf("Error refreshing libvirt storage pool: %s", err)
}

id, err := pool.GetUUIDString()
if err != nil {
return fmt.Errorf("Error retrieving libvirt pool id: %s", err)
}
d.SetId(id)

// make sure we record the id even if the rest of this gets interrupted
d.Partial(true)
d.Set("id", id)
d.SetPartial("id")
d.Partial(false)

log.Printf("[INFO] Pool ID: %s", d.Id())

return resourceLibvirtDirPoolRead(d, meta)
}

func resourceLibvirtDirPoolRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Client)
virConn := client.libvirt
if virConn == nil {
return fmt.Errorf(LibVirtConIsNil)
}

pool, err := virConn.LookupStoragePoolByUUIDString(d.Id())
if err != nil {
return fmt.Errorf("Error retrieving libvirt storage pool: %s", err)
}
defer pool.Free()

poolName, err := pool.GetName()
if err != nil {
return fmt.Errorf("error retrieving pool name: %s", err)
}
d.Set("name", poolName)

info, err := pool.GetInfo()
if err != nil {
return fmt.Errorf("error retrieving pool info: %s", err)
}
d.Set("capacity", info.Capacity)
d.Set("allocation", info.Allocation)
d.Set("available", info.Available)

poolDefXML, err := pool.GetXMLDesc(0)
if err != nil {
return fmt.Errorf("could not get XML description for pool %s: %s", poolName, err)
}

var poolDef libvirtxml.StoragePool
err = xml.Unmarshal([]byte(poolDefXML), &poolDef)
if err != nil {
return fmt.Errorf("could not get a pool definition from XML for %s: %s", poolDef.Name, err)
}

var poolPath string
if poolDef.Target != nil && poolDef.Target.Path != "" {
poolPath = poolDef.Target.Path
}

if poolPath == "" {
log.Printf("Pool %s has no path specified", poolName)
} else {
log.Printf("[DEBUG] Pool %s path: %s", poolName, poolPath)
d.Set("path", poolPath)
}

return nil
}

func resourceLibvirtDirPoolDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Client)
if client.libvirt == nil {
return fmt.Errorf(LibVirtConIsNil)
}

return removePool(client, d.Id())
}

func resourceLibvirtDirPoolExists(d *schema.ResourceData, meta interface{}) (bool, error) {
log.Printf("[DEBUG] Check if resource libvirt_dir_pool exists")
client := meta.(*Client)
virConn := client.libvirt
if virConn == nil {
return false, fmt.Errorf(LibVirtConIsNil)
}

pool, err := virConn.LookupStoragePoolByUUIDString(d.Id())
if err != nil {
return false, err
}

if pool == nil {
return false, fmt.Errorf("Failed to retrieve pool with ID %s", d.Id())
}
defer pool.Free()

return true, nil
}

0 comments on commit e3fdd8d

Please sign in to comment.