-
Notifications
You must be signed in to change notification settings - Fork 455
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
Enhancement: Added resource for snapshot and revert snapshot #107
Changes from 10 commits
f71c9ab
43deced
c9c8f9b
4703a3c
9477f63
b96aee1
827577f
36e2031
28bbd45
b35714c
f746702
db8598a
ae583a9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package vsphere | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"log" | ||
|
||
"github.com/hashicorp/terraform/helper/schema" | ||
) | ||
|
||
func resourceVSphereRevertSnapshot() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceVSphereSnapshotRevert, | ||
Delete: resourceVSphereSnapshotDummyDelete, | ||
Read: resourceVSphereSnapshotDummyRead, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"datacenter": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
ForceNew: true, | ||
}, | ||
"vm_id": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"snapshot_id": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"suppress_power_on": { | ||
Type: schema.TypeBool, | ||
Optional: true, | ||
ForceNew: true, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceVSphereSnapshotRevert(d *schema.ResourceData, meta interface{}) error { | ||
vm, err := findVM(d, meta) | ||
if err != nil { | ||
return fmt.Errorf("Error while getting the VirtualMachine :%s", err) | ||
} | ||
task, err := vm.RevertToSnapshot(context.TODO(), d.Get("snapshot_id").(string), d.Get("suppress_power_on").(bool)) | ||
if err != nil { | ||
log.Printf("[ERROR] Error While Creating the Task for Revert Snapshot: %v", err) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A note on logs: Terraform currently does nothing with |
||
return fmt.Errorf("Error While Creating the Task for Revert Snapshot: %s", err) | ||
} | ||
log.Printf("[INFO] Task created for Revert Snapshot: %v", task) | ||
|
||
err = task.Wait(context.TODO()) | ||
if err != nil { | ||
log.Printf("[ERROR] Error While waiting for the Task of Revert Snapshot: %v", err) | ||
return fmt.Errorf("Error While waiting for the Task of Revert Snapshot: %s", err) | ||
} | ||
log.Printf("[INFO] Revert Snapshot completed %v", d.Get("snapshot_id").(string)) | ||
d.SetId(d.Get("snapshot_id").(string)) | ||
return nil | ||
|
||
} | ||
|
||
func resourceVSphereSnapshotDummyRead(d *schema.ResourceData, meta interface{}) error { | ||
return nil | ||
} | ||
|
||
func resourceVSphereSnapshotDummyDelete(d *schema.ResourceData, meta interface{}) error { | ||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
package vsphere | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"os" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform/helper/resource" | ||
"github.com/hashicorp/terraform/terraform" | ||
"github.com/vmware/govmomi" | ||
"github.com/vmware/govmomi/find" | ||
"github.com/vmware/govmomi/vim25/mo" | ||
) | ||
|
||
func testBasicPreCheckSnapshotRevert(t *testing.T) { | ||
testAccPreCheck(t) | ||
|
||
} | ||
|
||
func TestAccVmSnapshotRevert_Basic(t *testing.T) { | ||
var vmId, snapshotId, suppressPower string | ||
if v := os.Getenv("VSPHERE_VM_ID"); v != "" { | ||
vmId = v | ||
} | ||
if v := os.Getenv("VSPHERE_VM_SNAPSHOT_ID"); v != "" { | ||
snapshotId = v | ||
} | ||
if v := os.Getenv("VSPHERE_SUPPRESS_POWER_ON"); v != "" { | ||
suppressPower = v | ||
} | ||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
CheckDestroy: testAccCheckVmSnapshotRevertDestroy, | ||
Steps: []resource.TestStep{ | ||
resource.TestStep{ | ||
Config: testAccCheckVSphereVMSnapshotRevertConfig_basic(vmId, snapshotId, suppressPower), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckVmCurrentSnapshot("vsphere_virtual_machine_snapshot_revert.Test_terraform_cases", snapshotId), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccCheckVmSnapshotRevertDestroy(s *terraform.State) error { | ||
|
||
return nil | ||
} | ||
|
||
func testAccCheckVmCurrentSnapshot(n, snapshot_name string) 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 Vm Snapshot ID is set") | ||
} | ||
client := testAccProvider.Meta().(*govmomi.Client) | ||
|
||
dc, err := getDatacenter(client, "") | ||
if err != nil { | ||
return fmt.Errorf("error %s", err) | ||
} | ||
finder := find.NewFinder(client.Client, true) | ||
finder = finder.SetDatacenter(dc) | ||
vm, err := finder.VirtualMachine(context.TODO(), os.Getenv("VSPHERE_VM_ID")) | ||
if err != nil { | ||
return fmt.Errorf("error %s", err) | ||
} | ||
|
||
var vm_object mo.VirtualMachine | ||
|
||
err = vm.Properties(context.TODO(), vm.Reference(), []string{"snapshot"}, &vm_object) | ||
|
||
if err != nil { | ||
return nil | ||
} | ||
current_snap := vm_object.Snapshot.CurrentSnapshot | ||
snapshot, err := vm.FindSnapshot(context.TODO(), snapshot_name) | ||
|
||
if err != nil { | ||
return fmt.Errorf("Error while getting the snapshot %v", snapshot) | ||
} | ||
if fmt.Sprintf("<%s>", snapshot) == fmt.Sprintf("<%s>", current_snap) { | ||
return nil | ||
} | ||
|
||
return fmt.Errorf("Test Case failed for revert snapshot. Current snapshot does not match to reverted snapshot") | ||
} | ||
} | ||
|
||
func testAccCheckVSphereVMSnapshotRevertConfig_basic(vmId, snapshotId, suppressPowerOn string) string { | ||
return fmt.Sprintf(` | ||
resource "vsphere_virtual_machine_snapshot_revert" "Test_terraform_cases"{ | ||
vm_id = "%s" | ||
snapshot_id = "%s" | ||
suppress_power_on = %s | ||
}`, vmId, snapshotId, suppressPowerOn) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
package vsphere | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you change the file name here to |
||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"log" | ||
"strings" | ||
|
||
"github.com/hashicorp/terraform/helper/schema" | ||
"github.com/vmware/govmomi" | ||
"github.com/vmware/govmomi/find" | ||
"github.com/vmware/govmomi/object" | ||
"github.com/vmware/govmomi/vim25/types" | ||
) | ||
|
||
func resourceVSphereSnapshot() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceVSphereSnapshotCreate, | ||
Read: resourceVSphereSnapshotRead, | ||
Delete: resourceVSphereSnapshotDelete, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"vm_id": { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you change this to reference the the UUID of the virtual machine versus the VM path name?
|
||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"datacenter": { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After the VM reference has been changed to UUID, you can get rid of this attribute. |
||
Type: schema.TypeString, | ||
Optional: true, | ||
ForceNew: true, | ||
}, | ||
"snapshot_name": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"description": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"memory": { | ||
Type: schema.TypeBool, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"quiesce": { | ||
Type: schema.TypeBool, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"remove_children": { | ||
Type: schema.TypeBool, | ||
Optional: true, | ||
ForceNew: true, | ||
}, | ||
"consolidate": { | ||
Type: schema.TypeBool, | ||
Optional: true, | ||
ForceNew: true, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceVSphereSnapshotCreate(d *schema.ResourceData, meta interface{}) error { | ||
vm, err := findVM(d, meta) | ||
if err != nil { | ||
return fmt.Errorf("Error while getting the VirtualMachine :%s", err) | ||
} | ||
task, err := vm.CreateSnapshot(context.TODO(), d.Get("snapshot_name").(string), d.Get("description").(string), d.Get("memory").(bool), d.Get("quiesce").(bool)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd like to get out of the pattern of the vSphere provider using general blocking contexts for these operations - this could potentially mean the user may have to wait 20 minutes for a failed API call to return. Can you make sure to replace all calls to ctx, cancel := context.WithTimeout(context.Background(), defaultAPITimeout) // This is 5 mins
defer cancel()
// your function that needs a context here This will ensure that we put a timeout on function calls that are more reasonable for the operations that they are trying to perform. Use a separate context for each call. If you find the code getting a little messy, break the code up into different functions. I'd recommend no more than 2 contexts per function with the latter being for waiting on tasks - like what is below: ctx, cancel := context.WithTimeout(context.Background(), defaultAPITimeout) // This is 5 mins
defer cancel()
task, err := object.FunctionThatNeedsContext(ctx)
if err != nil {
return err
}
tctx, tcancel := context.WithTimeout(context.Background(), defaultAPITimeout)
defer tcancel()
info, err := task.WaitForResult(tctx, nil)
// Process info.Result here |
||
taskInfo, err := task.WaitForResult(context.TODO(), nil) | ||
if err != nil { | ||
log.Printf("[ERROR] Error While Creating the Task for Create Snapshot: %v", err) | ||
return fmt.Errorf(" Error While Creating the Task for Create Snapshot: %s", err) | ||
} | ||
log.Printf("[INFO] Task created for Create Snapshot: %v", task) | ||
err = task.Wait(context.TODO()) | ||
|
||
if err != nil { | ||
log.Printf("[ERROR] Error While waiting for the Task for Create Snapshot: %v", err) | ||
return fmt.Errorf(" Error While waiting for the Task for Create Snapshot: %s", err) | ||
} | ||
log.Printf("[INFO] Create Snapshot completed %v", d.Get("snapshot_name").(string)) | ||
log.Println("[INFO] Managed Object Reference: " + taskInfo.Result.(types.ManagedObjectReference).Value) | ||
d.SetId(taskInfo.Result.(types.ManagedObjectReference).Value) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 for using the MOID as the resource ID! |
||
return nil | ||
} | ||
|
||
func resourceVSphereSnapshotDelete(d *schema.ResourceData, meta interface{}) error { | ||
vm, err := findVM(d, meta) | ||
if err != nil { | ||
return fmt.Errorf("Error while getting the VirtualMachine :%s", err) | ||
} | ||
resourceVSphereSnapshotRead(d, meta) | ||
if d.Id() == "" { | ||
log.Printf("[ERROR] Error While finding the Snapshot: %v", err) | ||
return nil | ||
} | ||
log.Printf("[INFO] Deleting snapshot with name: %v", d.Get("snapshot_name").(string)) | ||
var consolidate_ptr *bool | ||
var remove_children bool | ||
|
||
if v, ok := d.GetOk("consolidate"); ok { | ||
consolidate := v.(bool) | ||
consolidate_ptr = &consolidate | ||
} else { | ||
|
||
consolidate := true | ||
consolidate_ptr = &consolidate | ||
} | ||
if v, ok := d.GetOk("remove_children"); ok { | ||
remove_children = v.(bool) | ||
} else { | ||
|
||
remove_children = false | ||
} | ||
|
||
task, err := vm.RemoveSnapshot(context.TODO(), d.Id(), remove_children, consolidate_ptr) | ||
|
||
if err != nil { | ||
log.Printf("[ERROR] Error While Creating the Task for Delete Snapshot: %v", err) | ||
return fmt.Errorf("Error While Creating the Task for Delete Snapshot: %s", err) | ||
} | ||
log.Printf("[INFO] Task created for Delete Snapshot: %v", task) | ||
|
||
err = task.Wait(context.TODO()) | ||
if err != nil { | ||
log.Printf("[ERROR] Error While waiting for the Task of Delete Snapshot: %v", err) | ||
return fmt.Errorf("Error While waiting for the Task of Delete Snapshot: %s", err) | ||
} | ||
log.Printf("[INFO] Delete Snapshot completed %v", d.Get("snapshot_name").(string)) | ||
|
||
return nil | ||
} | ||
|
||
func resourceVSphereSnapshotRead(d *schema.ResourceData, meta interface{}) error { | ||
vm, err := findVM(d, meta) | ||
if err != nil { | ||
return fmt.Errorf("Error while getting the VirtualMachine :%s", err) | ||
} | ||
snapshot, err := vm.FindSnapshot(context.TODO(), d.Id()) | ||
|
||
if err != nil { | ||
if strings.Contains(err.Error(), "No snapshots for this VM") || strings.Contains(err.Error(), "snapshot \""+d.Get("snapshot_name").(string)+"\" not found") { | ||
log.Printf("[ERROR] Error While finding the Snapshot: %v", err) | ||
d.SetId("") | ||
return nil | ||
} | ||
log.Printf("[ERROR] Error While finding the Snapshot: %v", err) | ||
return fmt.Errorf("Error while finding the Snapshot :%s", err) | ||
} | ||
log.Printf("[INFO] Snapshot found: %v", snapshot) | ||
return nil | ||
} | ||
|
||
func findVM(d *schema.ResourceData, meta interface{}) (*object.VirtualMachine, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Once this is changed to find by UUID, can you move this function to a new file called Also, make sure the function name is self-documenting - in this case, it would be |
||
client := meta.(*govmomi.Client) | ||
var dc *object.Datacenter | ||
var err error | ||
if v, ok := d.GetOk("datacenter"); ok { | ||
dc, err = getDatacenter(client, v.(string)) | ||
} else { | ||
dc, err = getDatacenter(client, "") | ||
} | ||
if err != nil { | ||
log.Printf("[ERROR] Error While getting the DC: %v", err) | ||
return nil, fmt.Errorf("Error While getting the DC: %s", err) | ||
} | ||
log.Printf("[INFO] DataCenter is: %v", dc) | ||
log.Println("[INFO] Getting Finder:") | ||
finder := find.NewFinder(client.Client, true) | ||
log.Printf("[INFO] Finder is: %v", finder) | ||
log.Println("[INFO] Setting DataCenter:") | ||
finder = finder.SetDatacenter(dc) | ||
log.Printf("[INFO] DataCenter is Set: %v", finder) | ||
log.Println("[INFO] Getting VM Object: ") | ||
vm, err := finder.VirtualMachine(context.TODO(), d.Get("vm_id").(string)) | ||
if err != nil { | ||
log.Printf("[ERROR] Error While getting the Virtual machine object: %v", err) | ||
return nil, err | ||
} | ||
log.Printf("[INFO] Virtual Machine FOUND: %v", vm) | ||
return vm, nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a note here to remove the
vsphere_virtual_machine_snapshot_revert
resource.