Skip to content

Commit

Permalink
Merge pull request #157 from imjoey/new_r_snapshot
Browse files Browse the repository at this point in the history
New Resource: ovirt_snapshot
  • Loading branch information
imjoey authored Jul 10, 2019
2 parents 9ce1914 + 4a22605 commit cefcd56
Show file tree
Hide file tree
Showing 5 changed files with 357 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ provider "ovirt" {
* ovirt_host
* ovirt_mac_pool
* ovirt_network
* ovirt_snapshot
* ovirt_storage_domain
* ovirt_tag
* ovirt_user
Expand Down
1 change: 1 addition & 0 deletions ovirt/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ func Provider() terraform.ResourceProvider {
"ovirt_network": resourceOvirtNetwork(),
"ovirt_vnic": resourceOvirtVnic(),
"ovirt_vnic_profile": resourceOvirtVnicProfile(),
"ovirt_snapshot": resourceOvirtSnapshot(),
"ovirt_storage_domain": resourceOvirtStorageDomain(),
"ovirt_tag": resourceOvirtTag(),
"ovirt_user": resourceOvirtUser(),
Expand Down
195 changes: 195 additions & 0 deletions ovirt/resource_ovirt_snapshot.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
package ovirt

import (
"fmt"
"log"
"strings"
"time"

"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
ovirtsdk4 "github.com/ovirt/go-ovirt"
)

func resourceOvirtSnapshot() *schema.Resource {
return &schema.Resource{
Create: resourceOvirtSnapshotCreate,
Read: resourceOvirtSnapshotRead,
Delete: resourceOvirtSnapshotDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(10 * time.Minute),
Update: schema.DefaultTimeout(10 * time.Minute),
Delete: schema.DefaultTimeout(10 * time.Minute),
},

Schema: map[string]*schema.Schema{
"vm_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"save_memory": {
Type: schema.TypeBool,
Optional: true,
Default: true,
ForceNew: true,
},
"description": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
// Computed
"status": {
Type: schema.TypeString,
Computed: true,
},
"type": {
Type: schema.TypeString,
Computed: true,
},
"date": {
Type: schema.TypeString,
Computed: true,
},
},
}
}

func resourceOvirtSnapshotCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*ovirtsdk4.Connection)
builder := ovirtsdk4.NewSnapshotBuilder()

builder.Description(d.Get("description").(string)).
PersistMemorystate(d.Get("save_memory").(bool))

vmID := d.Get("vm_id").(string)
snapshotsService := conn.SystemService().VmsService().VmService(vmID).SnapshotsService()

resp, err := snapshotsService.
Add().
Snapshot(builder.MustBuild()).
Send()
if err != nil {
log.Printf("[DEBUG] Error creating Snapshot for VM (%s): %s", vmID, err)
return nil
}

snapshotID := resp.MustSnapshot().MustId()
d.SetId(vmID + ":" + snapshotID)

// Wait for snapshot is OK
log.Printf("[DEBUG] Snapshot (%s) is created and wait for ready (status is OK)", d.Id())
okStateConf := &resource.StateChangeConf{
Pending: []string{string(ovirtsdk4.SNAPSHOTSTATUS_LOCKED)},
Target: []string{string(ovirtsdk4.SNAPSHOTSTATUS_OK)},
Refresh: SnapshotStateRefreshFunc(conn, vmID, snapshotID),
Timeout: d.Timeout(schema.TimeoutCreate),
Delay: 10 * time.Second,
MinTimeout: 3 * time.Second,
}
_, err = okStateConf.WaitForState()
if err != nil {
log.Printf("[DEBUG] Failed to wait for Snapshot (%s) to become OK: %s", d.Id(), err)
return err
}

return resourceOvirtSnapshotRead(d, meta)
}

func resourceOvirtSnapshotRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*ovirtsdk4.Connection)
vmID, snapshotID, err := getVMIDAndSnapshotID(d.Id())
if err != nil {
return err
}

d.Set("vm_id", vmID)

snapshotService := conn.SystemService().
VmsService().
VmService(vmID).
SnapshotsService().
SnapshotService(snapshotID)

snapshotResp, err := snapshotService.Get().Send()
if err != nil {
if _, ok := err.(*ovirtsdk4.NotFoundError); ok {
d.SetId("")
return nil
}
log.Printf("[DEBUG] Failed to get Snapshot (%s): %s", d.Id(), err)
return err
}
snapshot := snapshotResp.MustSnapshot()

d.Set("description", snapshot.MustDescription())
d.Set("save_memory", snapshot.MustPersistMemorystate())
d.Set("status", string(snapshot.MustSnapshotStatus()))
d.Set("type", string(snapshot.MustSnapshotType()))
d.Set("date", snapshot.MustDate().Format(time.RFC3339))

return nil
}

func resourceOvirtSnapshotDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*ovirtsdk4.Connection)
vmID, snapshotID, err := getVMIDAndSnapshotID(d.Id())
if err != nil {
return err
}
snapshotService := conn.SystemService().
VmsService().
VmService(vmID).
SnapshotsService().
SnapshotService(snapshotID)

return resource.Retry(d.Timeout(schema.TimeoutDelete), func() *resource.RetryError {
log.Printf("[DEBUG] Now to remove Snapshot (%s)", d.Id())
_, err := snapshotService.Remove().Send()
if err != nil {
if _, ok := err.(*ovirtsdk4.NotFoundError); ok {
// Wait until NotFoundError raises
log.Printf("[DEBUG] Snapshot (%s) has been removed", d.Id())
return nil
}
return resource.RetryableError(fmt.Errorf("Error removing Snapshot (%s): %s", d.Id(), err))
}
return resource.RetryableError(fmt.Errorf("Snapshot (%s) is still being removed", d.Id()))
})
}

// SnapshotStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch
// an oVirt Snapshot.
func SnapshotStateRefreshFunc(conn *ovirtsdk4.Connection, vmID, snapshotID string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
r, err := conn.SystemService().
VmsService().
VmService(vmID).
SnapshotsService().
SnapshotService(snapshotID).
Get().
Send()

if err != nil {
if _, ok := err.(*ovirtsdk4.NotFoundError); ok {
return nil, "", nil
}
return nil, "", err
}

return r.MustSnapshot, string(r.MustSnapshot().MustSnapshotStatus()), nil
}
}

func getVMIDAndSnapshotID(rsID string) (string, string, error) {
parts := strings.Split(rsID, ":")
if len(parts) != 2 {
return "", "", fmt.Errorf("Invalid Snapshot ID: %s", rsID)
}
return parts[0], parts[1], nil
}
114 changes: 114 additions & 0 deletions ovirt/resource_ovirt_snapshot_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package ovirt

import (
"fmt"
"testing"

"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
ovirtsdk4 "github.com/ovirt/go-ovirt"
)

func TestAccOvirtSnapshot_basic(t *testing.T) {
description := "description for snapshot"
vmID := "53000b15-82ad-4ed4-9f86-bffb95e3c28b"
saveMemory := true

var snapshot ovirtsdk4.Snapshot
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
IDRefreshName: "ovirt_snapshot.snapshot",
CheckDestroy: testAccCheckSnapshotDestroy,
Steps: []resource.TestStep{
{
Config: testAccSnapshotBasic(description, vmID, saveMemory),
Check: resource.ComposeTestCheckFunc(
testAccCheckOvirtSnapshotExists("ovirt_snapshot.snapshot", &snapshot),
resource.TestCheckResourceAttr("ovirt_snapshot.snapshot", "description", description),
resource.TestCheckResourceAttr("ovirt_snapshot.snapshot", "vm_id", vmID),
resource.TestCheckResourceAttr("ovirt_snapshot.snapshot", "save_memory", fmt.Sprintf("%t", saveMemory)),
resource.TestCheckResourceAttr("ovirt_snapshot.snapshot", "status", string(ovirtsdk4.SNAPSHOTSTATUS_OK)),
),
},
},
})
}

func testAccCheckSnapshotDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*ovirtsdk4.Connection)
for _, rs := range s.RootModule().Resources {
if rs.Type != "ovirt_snapshot" {
continue
}

vmID, snapshotID, err := getVMIDAndSnapshotID(rs.Primary.ID)
if err != nil {
return err
}

getResp, err := conn.SystemService().
VmsService().
VmService(vmID).
SnapshotsService().
SnapshotService(snapshotID).
Get().
Send()

if err != nil {
if _, ok := err.(*ovirtsdk4.NotFoundError); ok {
continue
}
return err
}
if _, ok := getResp.Snapshot(); ok {
return fmt.Errorf("Snapshot %s still exist", rs.Primary.ID)
}
}
return nil
}

func testAccCheckOvirtSnapshotExists(n string, v *ovirtsdk4.Snapshot) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}
if rs.Primary.ID == "" {
return fmt.Errorf("No Snapshot ID is set")
}

vmID, snapshotID, err := getVMIDAndSnapshotID(rs.Primary.ID)
if err != nil {
return err
}

conn := testAccProvider.Meta().(*ovirtsdk4.Connection)
getResp, err := conn.SystemService().
VmsService().
VmService(vmID).
SnapshotsService().
SnapshotService(snapshotID).
Get().
Send()
if err != nil {
return err
}
snapshot, ok := getResp.Snapshot()
if ok {
*v = *snapshot
return nil
}
return fmt.Errorf("Snapshot %s not exist", rs.Primary.ID)
}
}

func testAccSnapshotBasic(description, vmID string, saveMemory bool) string {
return fmt.Sprintf(`
resource "ovirt_snapshot" "snapshot" {
description = "%s"
vm_id = "%s"
save_memory = %t
}
`, description, vmID, saveMemory)
}
46 changes: 46 additions & 0 deletions website/docs/r/snapshot.html.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
layout: "ovirt"
page_title: "oVirt: ovirt_snapshot"
sidebar_current: "docs-ovirt-resource-snapshot"
description: |-
Manages a Snapshot of vm resource within oVirt.
---

# ovirt\_snapshot

Manages a Snapshot of VM resource within oVirt.

## Example Usage

```hcl
resource "ovirt_snapshot" "snapshot" {
description = "description-of-snasphot"
vm_id = "53000b15-82ad-4ed4-9f86-bffb95e3c28b"
save_memory = true
}
```

## Argument Reference

The following arguments are supported:

* `description` - (Required) A description of the snapshot. Changing this creates a new snapshot.
* `vm_id` - (Required) The ID of vm the snapshot taken from. Changing this creates a new snapshot.
* `save_memory` - (Optional) The flag to indicate whether the content of the memory of the vm is included in the snapshot. Default is `true`. Changing this creates a new snapshot.

## Attributes Reference

In addition to all arguments above, the following attributes are exported:

* `id` - The composite ID of the snapshot which is constituted by the ID of the vm the snapshot taken from and the ID of the snapshot within oVirt.
* `status` - The status of the snapshot. Can be "in_preview", "locked" or "ok".
* `type` - The type of the snapshot. Can be "active", "preview", "regular" or "stateless".
* `date` - The string representation of the creation time of the snapshot in RFC3339 format.

## Import

Snapshots can be imported using the composite `id`, e.g.

```
$ terraform import ovirt_snapshot.snapshot 53000b15-82ad-4ed4-9f86-bffb95e3c28b:df736600-b8be-4029-be98-4b0611be6be4
```

0 comments on commit cefcd56

Please sign in to comment.