github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/vsphere/resource_vsphere_virtual_disk.go (about)

     1  package vsphere
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  
     7  	"errors"
     8  	"github.com/hashicorp/terraform/helper/schema"
     9  	"github.com/vmware/govmomi"
    10  	"github.com/vmware/govmomi/find"
    11  	"github.com/vmware/govmomi/object"
    12  	"github.com/vmware/govmomi/vim25/types"
    13  	"golang.org/x/net/context"
    14  	"path"
    15  )
    16  
    17  type virtualDisk struct {
    18  	size        int
    19  	vmdkPath    string
    20  	initType    string
    21  	adapterType string
    22  	datacenter  string
    23  	datastore   string
    24  }
    25  
    26  // Define VirtualDisk args
    27  func resourceVSphereVirtualDisk() *schema.Resource {
    28  	return &schema.Resource{
    29  		Create: resourceVSphereVirtualDiskCreate,
    30  		Read:   resourceVSphereVirtualDiskRead,
    31  		Delete: resourceVSphereVirtualDiskDelete,
    32  
    33  		Schema: map[string]*schema.Schema{
    34  			// Size in GB
    35  			"size": &schema.Schema{
    36  				Type:     schema.TypeInt,
    37  				Required: true,
    38  				ForceNew: true, //TODO Can this be optional (resize)?
    39  			},
    40  
    41  			"vmdk_path": &schema.Schema{
    42  				Type:     schema.TypeString,
    43  				Required: true,
    44  				ForceNew: true, //TODO Can this be optional (move)?
    45  			},
    46  
    47  			"type": &schema.Schema{
    48  				Type:     schema.TypeString,
    49  				Optional: true,
    50  				ForceNew: true,
    51  				Default:  "eagerZeroedThick",
    52  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    53  					value := v.(string)
    54  					if value != "thin" && value != "eagerZeroedThick" && value != "lazy" {
    55  						errors = append(errors, fmt.Errorf(
    56  							"only 'thin', 'eagerZeroedThick', and 'lazy' are supported values for 'type'"))
    57  					}
    58  					return
    59  				},
    60  			},
    61  
    62  			"adapter_type": &schema.Schema{
    63  				Type:     schema.TypeString,
    64  				Optional: true,
    65  				ForceNew: true,
    66  				Default:  "ide",
    67  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    68  					value := v.(string)
    69  					if value != "ide" && value != "busLogic" && value != "lsiLogic" {
    70  						errors = append(errors, fmt.Errorf(
    71  							"only 'ide', 'busLogic', and 'lsiLogic' are supported values for 'adapter_type'"))
    72  					}
    73  					return
    74  				},
    75  			},
    76  
    77  			"datacenter": &schema.Schema{
    78  				Type:     schema.TypeString,
    79  				Optional: true,
    80  				ForceNew: true,
    81  			},
    82  
    83  			"datastore": &schema.Schema{
    84  				Type:     schema.TypeString,
    85  				Optional: true,
    86  				ForceNew: true,
    87  			},
    88  		},
    89  	}
    90  }
    91  
    92  func resourceVSphereVirtualDiskCreate(d *schema.ResourceData, meta interface{}) error {
    93  	log.Printf("[INFO] Creating Virtual Disk")
    94  	client := meta.(*govmomi.Client)
    95  
    96  	vDisk := virtualDisk{
    97  		size: d.Get("size").(int),
    98  	}
    99  
   100  	if v, ok := d.GetOk("vmdk_path"); ok {
   101  		vDisk.vmdkPath = v.(string)
   102  	}
   103  
   104  	if v, ok := d.GetOk("type"); ok {
   105  		vDisk.initType = v.(string)
   106  	}
   107  
   108  	if v, ok := d.GetOk("adapter_type"); ok {
   109  		vDisk.adapterType = v.(string)
   110  	}
   111  
   112  	if v, ok := d.GetOk("datacenter"); ok {
   113  		vDisk.datacenter = v.(string)
   114  	}
   115  
   116  	if v, ok := d.GetOk("datastore"); ok {
   117  		vDisk.datastore = v.(string)
   118  	}
   119  
   120  	finder := find.NewFinder(client.Client, true)
   121  
   122  	dc, err := getDatacenter(client, d.Get("datacenter").(string))
   123  	if err != nil {
   124  		return fmt.Errorf("Error finding Datacenter: %s: %s", vDisk.datacenter, err)
   125  	}
   126  	finder = finder.SetDatacenter(dc)
   127  
   128  	ds, err := getDatastore(finder, vDisk.datastore)
   129  	if err != nil {
   130  		return fmt.Errorf("Error finding Datastore: %s: %s", vDisk.datastore, err)
   131  	}
   132  
   133  	err = createHardDisk(client, vDisk.size, ds.Path(vDisk.vmdkPath), vDisk.initType, vDisk.adapterType, vDisk.datacenter)
   134  	if err != nil {
   135  		return err
   136  	}
   137  
   138  	d.SetId(ds.Path(vDisk.vmdkPath))
   139  	log.Printf("[DEBUG] Virtual Disk id: %v", ds.Path(vDisk.vmdkPath))
   140  
   141  	return resourceVSphereVirtualDiskRead(d, meta)
   142  }
   143  
   144  func resourceVSphereVirtualDiskRead(d *schema.ResourceData, meta interface{}) error {
   145  	log.Printf("[DEBUG] Reading virtual disk.")
   146  	client := meta.(*govmomi.Client)
   147  
   148  	vDisk := virtualDisk{
   149  		size: d.Get("size").(int),
   150  	}
   151  
   152  	if v, ok := d.GetOk("vmdk_path"); ok {
   153  		vDisk.vmdkPath = v.(string)
   154  	}
   155  
   156  	if v, ok := d.GetOk("type"); ok {
   157  		vDisk.initType = v.(string)
   158  	}
   159  
   160  	if v, ok := d.GetOk("adapter_type"); ok {
   161  		vDisk.adapterType = v.(string)
   162  	}
   163  
   164  	if v, ok := d.GetOk("datacenter"); ok {
   165  		vDisk.datacenter = v.(string)
   166  	}
   167  
   168  	if v, ok := d.GetOk("datastore"); ok {
   169  		vDisk.datastore = v.(string)
   170  	}
   171  
   172  	dc, err := getDatacenter(client, d.Get("datacenter").(string))
   173  	if err != nil {
   174  		return err
   175  	}
   176  
   177  	finder := find.NewFinder(client.Client, true)
   178  	finder = finder.SetDatacenter(dc)
   179  
   180  	ds, err := finder.Datastore(context.TODO(), d.Get("datastore").(string))
   181  	if err != nil {
   182  		return err
   183  	}
   184  
   185  	ctx := context.TODO()
   186  	b, err := ds.Browser(ctx)
   187  	if err != nil {
   188  		return err
   189  	}
   190  
   191  	// `Datastore.Stat` does not allow to query `VmDiskFileQuery`. Instead, we
   192  	// search the datastore manually.
   193  	spec := types.HostDatastoreBrowserSearchSpec{
   194  		Query: []types.BaseFileQuery{&types.VmDiskFileQuery{Details: &types.VmDiskFileQueryFlags{
   195  			CapacityKb: true,
   196  			DiskType:   true,
   197  		}}},
   198  		Details: &types.FileQueryFlags{
   199  			FileSize:     true,
   200  			FileType:     true,
   201  			Modification: true,
   202  			FileOwner:    types.NewBool(true),
   203  		},
   204  		MatchPattern: []string{path.Base(vDisk.vmdkPath)},
   205  	}
   206  
   207  	dsPath := ds.Path(path.Dir(vDisk.vmdkPath))
   208  	task, err := b.SearchDatastore(context.TODO(), dsPath, &spec)
   209  
   210  	if err != nil {
   211  		log.Printf("[DEBUG] resourceVSphereVirtualDiskRead - could not search datastore for: %v", vDisk.vmdkPath)
   212  		return err
   213  	}
   214  
   215  	info, err := task.WaitForResult(context.TODO(), nil)
   216  	if err != nil {
   217  		if info == nil || info.Error != nil {
   218  			_, ok := info.Error.Fault.(*types.FileNotFound)
   219  			if ok {
   220  				log.Printf("[DEBUG] resourceVSphereVirtualDiskRead - could not find: %v", vDisk.vmdkPath)
   221  				d.SetId("")
   222  				return nil
   223  			}
   224  		}
   225  
   226  		log.Printf("[DEBUG] resourceVSphereVirtualDiskRead - could not search datastore for: %v", vDisk.vmdkPath)
   227  		return err
   228  	}
   229  
   230  	res := info.Result.(types.HostDatastoreBrowserSearchResults)
   231  	log.Printf("[DEBUG] num results: %d", len(res.File))
   232  	if len(res.File) == 0 {
   233  		d.SetId("")
   234  		log.Printf("[DEBUG] resourceVSphereVirtualDiskRead - could not find: %v", vDisk.vmdkPath)
   235  		return nil
   236  	}
   237  
   238  	if len(res.File) != 1 {
   239  		return errors.New("Datastore search did not return exactly one result")
   240  	}
   241  
   242  	fileInfo := res.File[0]
   243  	log.Printf("[DEBUG] resourceVSphereVirtualDiskRead - fileinfo: %#v", fileInfo)
   244  	size := fileInfo.(*types.VmDiskFileInfo).CapacityKb / 1024 / 1024
   245  
   246  	d.SetId(vDisk.vmdkPath)
   247  
   248  	d.Set("size", size)
   249  	d.Set("vmdk_path", vDisk.vmdkPath)
   250  	d.Set("datacenter", d.Get("datacenter"))
   251  	d.Set("datastore", d.Get("datastore"))
   252  	// Todo collect and write type info
   253  
   254  	return nil
   255  
   256  }
   257  
   258  func resourceVSphereVirtualDiskDelete(d *schema.ResourceData, meta interface{}) error {
   259  	client := meta.(*govmomi.Client)
   260  
   261  	vDisk := virtualDisk{}
   262  
   263  	if v, ok := d.GetOk("vmdk_path"); ok {
   264  		vDisk.vmdkPath = v.(string)
   265  	}
   266  	if v, ok := d.GetOk("datastore"); ok {
   267  		vDisk.datastore = v.(string)
   268  	}
   269  
   270  	dc, err := getDatacenter(client, d.Get("datacenter").(string))
   271  	if err != nil {
   272  		return err
   273  	}
   274  
   275  	finder := find.NewFinder(client.Client, true)
   276  	finder = finder.SetDatacenter(dc)
   277  
   278  	ds, err := getDatastore(finder, vDisk.datastore)
   279  	if err != nil {
   280  		return err
   281  	}
   282  
   283  	diskPath := ds.Path(vDisk.vmdkPath)
   284  
   285  	virtualDiskManager := object.NewVirtualDiskManager(client.Client)
   286  
   287  	task, err := virtualDiskManager.DeleteVirtualDisk(context.TODO(), diskPath, dc)
   288  	if err != nil {
   289  		return err
   290  	}
   291  
   292  	_, err = task.WaitForResult(context.TODO(), nil)
   293  	if err != nil {
   294  		log.Printf("[INFO] Failed to delete disk:  %v", err)
   295  		return err
   296  	}
   297  
   298  	log.Printf("[INFO] Deleted disk: %v", diskPath)
   299  	d.SetId("")
   300  	return nil
   301  }
   302  
   303  // createHardDisk creates a new Hard Disk.
   304  func createHardDisk(client *govmomi.Client, size int, diskPath string, diskType string, adapterType string, dc string) error {
   305  	var vDiskType string
   306  	switch diskType {
   307  	case "thin":
   308  		vDiskType = "thin"
   309  	case "eagerZeroedThick":
   310  		vDiskType = "eagerZeroedThick"
   311  	case "lazy":
   312  		vDiskType = "preallocated"
   313  	}
   314  
   315  	virtualDiskManager := object.NewVirtualDiskManager(client.Client)
   316  	spec := &types.FileBackedVirtualDiskSpec{
   317  		VirtualDiskSpec: types.VirtualDiskSpec{
   318  			AdapterType: adapterType,
   319  			DiskType:    vDiskType,
   320  		},
   321  		CapacityKb: int64(1024 * 1024 * size),
   322  	}
   323  	datacenter, err := getDatacenter(client, dc)
   324  	if err != nil {
   325  		return err
   326  	}
   327  	log.Printf("[DEBUG] Disk spec: %v", spec)
   328  
   329  	task, err := virtualDiskManager.CreateVirtualDisk(context.TODO(), diskPath, datacenter, spec)
   330  	if err != nil {
   331  		return err
   332  	}
   333  
   334  	_, err = task.WaitForResult(context.TODO(), nil)
   335  	if err != nil {
   336  		log.Printf("[INFO] Failed to create disk:  %v", err)
   337  		return err
   338  	}
   339  	log.Printf("[INFO] Created disk.")
   340  
   341  	return nil
   342  }