-
Notifications
You must be signed in to change notification settings - Fork 9.7k
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
Adding volume resource creation to vSphere provider #6273
Changes from all commits
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,259 @@ | ||
package vsphere | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
|
||
"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" | ||
"golang.org/x/net/context" | ||
) | ||
|
||
type virtualDisk struct { | ||
size int | ||
vmdkPath string | ||
initType string | ||
adapterType string | ||
datacenter string | ||
datastore string | ||
} | ||
|
||
// Define VirtualDisk args | ||
func resourceVSphereVirtualDisk() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceVSphereVirtualDiskCreate, | ||
Read: resourceVSphereVirtualDiskRead, | ||
Delete: resourceVSphereVirtualDiskDelete, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
// Size in GB | ||
"size": &schema.Schema{ | ||
Type: schema.TypeInt, | ||
Required: true, | ||
ForceNew: true, //TODO Can this be optional (resize)? | ||
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. it looks like these can be false if an upgrade supports the change 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. That would be changed in a PR that also adds vm Update functionality. For now it needs to stay this way, else TF would go looking for a |
||
}, | ||
|
||
"vmdk_path": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, //TODO Can this be optional (move)? | ||
}, | ||
|
||
"type": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Optional: true, | ||
ForceNew: true, | ||
Default: "eagerZeroedThick", | ||
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { | ||
value := v.(string) | ||
if value != "thin" && value != "eagerZeroedThick" { | ||
errors = append(errors, fmt.Errorf( | ||
"only 'thin' and 'eagerZeroedThick' are supported values for 'type'")) | ||
} | ||
return | ||
}, | ||
}, | ||
|
||
"adapter_type": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Optional: true, | ||
ForceNew: true, | ||
Default: "ide", | ||
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { | ||
value := v.(string) | ||
if value != "ide" && value != "busLogic" && value != "lsiLogic" { | ||
errors = append(errors, fmt.Errorf( | ||
"only 'ide', 'busLogic', and 'lsiLogic' are supported values for 'adapter_type'")) | ||
} | ||
return | ||
}, | ||
}, | ||
|
||
"datacenter": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Optional: true, | ||
ForceNew: true, | ||
}, | ||
|
||
"datastore": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Optional: true, | ||
ForceNew: true, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceVSphereVirtualDiskCreate(d *schema.ResourceData, meta interface{}) error { | ||
log.Printf("[INFO] Creating Virtual Disk") | ||
client := meta.(*govmomi.Client) | ||
|
||
vDisk := virtualDisk{ | ||
size: d.Get("size").(int), | ||
} | ||
|
||
if v, ok := d.GetOk("vmdk_path"); ok { | ||
vDisk.vmdkPath = v.(string) | ||
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. @stack72 you would expect the 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. @chrislovecnm the setting of the state usually happens I'm the read func so we are sure that we expected to happen has happened. |
||
} | ||
|
||
if v, ok := d.GetOk("type"); ok { | ||
vDisk.initType = v.(string) | ||
} | ||
|
||
if v, ok := d.GetOk("adapter_type"); ok { | ||
vDisk.adapterType = v.(string) | ||
} | ||
|
||
if v, ok := d.GetOk("datacenter"); ok { | ||
vDisk.datacenter = v.(string) | ||
} | ||
|
||
if v, ok := d.GetOk("datastore"); ok { | ||
vDisk.datastore = v.(string) | ||
} | ||
|
||
diskPath := fmt.Sprintf("[%v] %v", vDisk.datastore, vDisk.vmdkPath) | ||
|
||
err := createHardDisk(client, vDisk.size, diskPath, vDisk.initType, vDisk.adapterType, vDisk.datacenter) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
d.SetId(diskPath) | ||
log.Printf("[DEBUG] Virtual Disk id: %v", diskPath) | ||
|
||
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. Should we have some logging? In general a touch more logging would be nice ;) |
||
return resourceVSphereVirtualDiskRead(d, meta) | ||
} | ||
|
||
func resourceVSphereVirtualDiskRead(d *schema.ResourceData, meta interface{}) error { | ||
log.Printf("[DEBUG] Reading virtual disk.") | ||
client := meta.(*govmomi.Client) | ||
|
||
vDisk := virtualDisk{ | ||
size: d.Get("size").(int), | ||
} | ||
|
||
if v, ok := d.GetOk("vmdk_path"); ok { | ||
vDisk.vmdkPath = v.(string) | ||
} | ||
|
||
if v, ok := d.GetOk("type"); ok { | ||
vDisk.initType = v.(string) | ||
} | ||
|
||
if v, ok := d.GetOk("adapter_type"); ok { | ||
vDisk.adapterType = v.(string) | ||
} | ||
|
||
if v, ok := d.GetOk("datacenter"); ok { | ||
vDisk.datacenter = v.(string) | ||
} | ||
|
||
if v, ok := d.GetOk("datastore"); ok { | ||
vDisk.datastore = v.(string) | ||
} | ||
|
||
dc, err := getDatacenter(client, d.Get("datacenter").(string)) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
finder := find.NewFinder(client.Client, true) | ||
finder = finder.SetDatacenter(dc) | ||
|
||
ds, err := finder.Datastore(context.TODO(), d.Get("datastore").(string)) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
fileInfo, err := ds.Stat(context.TODO(), vDisk.vmdkPath) | ||
if err != nil { | ||
log.Printf("[DEBUG] resourceVSphereVirtualDiskRead - stat failed on: %v", vDisk.vmdkPath) | ||
d.SetId("") | ||
return err | ||
} | ||
fileInfo = fileInfo.GetFileInfo() | ||
log.Printf("[DEBUG] resourceVSphereVirtualDiskRead - fileinfo: %#v", fileInfo) | ||
size := fileInfo.(*types.FileInfo).FileSize / 1024 / 1024 / 1024 | ||
|
||
d.SetId(vDisk.vmdkPath) | ||
|
||
d.Set("size", size) | ||
d.Set("vmdk_path", vDisk.vmdkPath) | ||
d.Set("datacenter", d.Get("datacenter")) | ||
d.Set("datastore", d.Get("datastore")) | ||
// Todo collect and write type info | ||
|
||
return nil | ||
|
||
} | ||
|
||
func resourceVSphereVirtualDiskDelete(d *schema.ResourceData, meta interface{}) error { | ||
client := meta.(*govmomi.Client) | ||
|
||
vDisk := virtualDisk{} | ||
|
||
if v, ok := d.GetOk("vmdk_path"); ok { | ||
vDisk.vmdkPath = v.(string) | ||
} | ||
if v, ok := d.GetOk("datastore"); ok { | ||
vDisk.datastore = v.(string) | ||
} | ||
|
||
dc, err := getDatacenter(client, d.Get("datacenter").(string)) | ||
if err != nil { | ||
return err | ||
} | ||
diskPath := fmt.Sprintf("[%v] %v", vDisk.datastore, vDisk.vmdkPath) | ||
|
||
virtualDiskManager := object.NewVirtualDiskManager(client.Client) | ||
|
||
task, err := virtualDiskManager.DeleteVirtualDisk(context.TODO(), diskPath, dc) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
_, err = task.WaitForResult(context.TODO(), nil) | ||
if err != nil { | ||
log.Printf("[INFO] Failed to delete disk: %v", err) | ||
return err | ||
} | ||
|
||
log.Printf("[INFO] Deleted disk: %v", diskPath) | ||
d.SetId("") | ||
return nil | ||
} | ||
|
||
// createHardDisk creates a new Hard Disk. | ||
func createHardDisk(client *govmomi.Client, size int, diskPath string, diskType string, adapterType string, dc string) error { | ||
virtualDiskManager := object.NewVirtualDiskManager(client.Client) | ||
spec := &types.FileBackedVirtualDiskSpec{ | ||
VirtualDiskSpec: types.VirtualDiskSpec{ | ||
AdapterType: adapterType, | ||
DiskType: diskType, | ||
}, | ||
CapacityKb: int64(1024 * 1024 * size), | ||
} | ||
datacenter, err := getDatacenter(client, dc) | ||
if err != nil { | ||
return err | ||
} | ||
log.Printf("[DEBUG] Disk spec: %v", spec) | ||
|
||
task, err := virtualDiskManager.CreateVirtualDisk(context.TODO(), diskPath, datacenter, spec) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
_, err = task.WaitForResult(context.TODO(), nil) | ||
if err != nil { | ||
log.Printf("[INFO] Failed to create disk: %v", err) | ||
return err | ||
} | ||
log.Printf("[INFO] Created disk.") | ||
|
||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
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. Does the TF framework support negative test cases? 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. Looking at other tests and at helper/resource/testing.go library, it looks like negative tests aren't supported. 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. 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. Not sure I follow what you mean by negative testing? You can make whatever assertions you want for acceptance tests - I can't think of an example offhand but the function signature for the checkFuncs should be reasonably self-explanatory. |
||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"os" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform/helper/resource" | ||
"github.com/hashicorp/terraform/terraform" | ||
"github.com/vmware/govmomi" | ||
"github.com/vmware/govmomi/find" | ||
"golang.org/x/net/context" | ||
) | ||
|
||
func TestAccVSphereVirtualDisk_basic(t *testing.T) { | ||
var datacenterOpt string | ||
var datastoreOpt string | ||
var initTypeOpt string | ||
var adapterTypeOpt string | ||
|
||
if v := os.Getenv("VSPHERE_DATACENTER"); v != "" { | ||
datacenterOpt = v | ||
} | ||
if v := os.Getenv("VSPHERE_DATASTORE"); v != "" { | ||
datastoreOpt = v | ||
} | ||
if v := os.Getenv("VSPHERE_INIT_TYPE"); v != "" { | ||
initTypeOpt += fmt.Sprintf(" type = \"%s\"\n", v) | ||
} | ||
if v := os.Getenv("VSPHERE_ADAPTER_TYPE"); v != "" { | ||
adapterTypeOpt += fmt.Sprintf(" adapter_type = \"%s\"\n", v) | ||
} | ||
|
||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
CheckDestroy: testAccCheckVSphereVirtualDiskDestroy, | ||
Steps: []resource.TestStep{ | ||
resource.TestStep{ | ||
Config: fmt.Sprintf( | ||
testAccCheckVSphereVirtuaDiskConfig_basic, | ||
initTypeOpt, | ||
adapterTypeOpt, | ||
datacenterOpt, | ||
datastoreOpt, | ||
), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccVSphereVirtualDiskExists("vsphere_virtual_disk.foo"), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccVSphereVirtualDiskExists(name string) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
rs, ok := s.RootModule().Resources[name] | ||
if !ok { | ||
return fmt.Errorf("Not found: %s", name) | ||
} | ||
if rs.Primary.ID == "" { | ||
return fmt.Errorf("No ID is set") | ||
} | ||
|
||
client := testAccProvider.Meta().(*govmomi.Client) | ||
finder := find.NewFinder(client.Client, true) | ||
|
||
dc, err := finder.Datacenter(context.TODO(), rs.Primary.Attributes["datacenter"]) | ||
if err != nil { | ||
return fmt.Errorf("error %s", err) | ||
} | ||
finder = finder.SetDatacenter(dc) | ||
|
||
ds, err := finder.Datastore(context.TODO(), rs.Primary.Attributes["datastore"]) | ||
if err != nil { | ||
return fmt.Errorf("error %s", err) | ||
} | ||
|
||
_, err = ds.Stat(context.TODO(), rs.Primary.Attributes["vmdk_path"]) | ||
if err != nil { | ||
return fmt.Errorf("error %s", err) | ||
} | ||
|
||
return nil | ||
} | ||
} | ||
|
||
func testAccCheckVSphereVirtualDiskDestroy(s *terraform.State) error { | ||
log.Printf("[FINDME] test Destroy") | ||
client := testAccProvider.Meta().(*govmomi.Client) | ||
finder := find.NewFinder(client.Client, true) | ||
|
||
for _, rs := range s.RootModule().Resources { | ||
if rs.Type != "vsphere_virtual_disk" { | ||
continue | ||
} | ||
|
||
dc, err := finder.Datacenter(context.TODO(), rs.Primary.Attributes["datacenter"]) | ||
if err != nil { | ||
return fmt.Errorf("error %s", err) | ||
} | ||
|
||
finder = finder.SetDatacenter(dc) | ||
|
||
ds, err := finder.Datastore(context.TODO(), rs.Primary.Attributes["datastore"]) | ||
if err != nil { | ||
return fmt.Errorf("error %s", err) | ||
} | ||
|
||
_, err = ds.Stat(context.TODO(), rs.Primary.Attributes["vmdk_path"]) | ||
if err == nil { | ||
return fmt.Errorf("error %s", err) | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
const testAccCheckVSphereVirtuaDiskConfig_basic = ` | ||
resource "vsphere_virtual_disk" "foo" { | ||
size = 1 | ||
vmdk_path = "tfTestDisk.vmdk" | ||
%s | ||
%s | ||
datacenter = "%s" | ||
datastore = "%s" | ||
} | ||
` |
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.
Method comments ... Pretty please. What does this do ..