Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Resource: ovirt_snapshot #157

Merged
merged 1 commit into from
Jul 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
```