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

     1  package vsphere
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strings"
     7  
     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/soap"
    13  	"golang.org/x/net/context"
    14  )
    15  
    16  type file struct {
    17  	sourceDatacenter  string
    18  	datacenter        string
    19  	sourceDatastore   string
    20  	datastore         string
    21  	sourceFile        string
    22  	destinationFile   string
    23  	createDirectories bool
    24  	copyFile          bool
    25  }
    26  
    27  func resourceVSphereFile() *schema.Resource {
    28  	return &schema.Resource{
    29  		Create: resourceVSphereFileCreate,
    30  		Read:   resourceVSphereFileRead,
    31  		Update: resourceVSphereFileUpdate,
    32  		Delete: resourceVSphereFileDelete,
    33  
    34  		Schema: map[string]*schema.Schema{
    35  			"datacenter": {
    36  				Type:     schema.TypeString,
    37  				Optional: true,
    38  			},
    39  
    40  			"source_datacenter": {
    41  				Type:     schema.TypeString,
    42  				Optional: true,
    43  				ForceNew: true,
    44  			},
    45  
    46  			"datastore": {
    47  				Type:     schema.TypeString,
    48  				Required: true,
    49  			},
    50  
    51  			"source_datastore": {
    52  				Type:     schema.TypeString,
    53  				Optional: true,
    54  				ForceNew: true,
    55  			},
    56  
    57  			"source_file": {
    58  				Type:     schema.TypeString,
    59  				Required: true,
    60  				ForceNew: true,
    61  			},
    62  
    63  			"destination_file": {
    64  				Type:     schema.TypeString,
    65  				Required: true,
    66  			},
    67  
    68  			"create_directories": {
    69  				Type:     schema.TypeBool,
    70  				Optional: true,
    71  			},
    72  		},
    73  	}
    74  }
    75  
    76  func resourceVSphereFileCreate(d *schema.ResourceData, meta interface{}) error {
    77  
    78  	log.Printf("[DEBUG] creating file: %#v", d)
    79  	client := meta.(*govmomi.Client)
    80  
    81  	f := file{}
    82  
    83  	if v, ok := d.GetOk("source_datacenter"); ok {
    84  		f.sourceDatacenter = v.(string)
    85  		f.copyFile = true
    86  	}
    87  
    88  	if v, ok := d.GetOk("datacenter"); ok {
    89  		f.datacenter = v.(string)
    90  	}
    91  
    92  	if v, ok := d.GetOk("source_datastore"); ok {
    93  		f.sourceDatastore = v.(string)
    94  		f.copyFile = true
    95  	}
    96  
    97  	if v, ok := d.GetOk("datastore"); ok {
    98  		f.datastore = v.(string)
    99  	} else {
   100  		return fmt.Errorf("datastore argument is required")
   101  	}
   102  
   103  	if v, ok := d.GetOk("source_file"); ok {
   104  		f.sourceFile = v.(string)
   105  	} else {
   106  		return fmt.Errorf("source_file argument is required")
   107  	}
   108  
   109  	if v, ok := d.GetOk("destination_file"); ok {
   110  		f.destinationFile = v.(string)
   111  	} else {
   112  		return fmt.Errorf("destination_file argument is required")
   113  	}
   114  
   115  	if v, ok := d.GetOk("create_directories"); ok {
   116  		f.createDirectories = v.(bool)
   117  	}
   118  
   119  	err := createFile(client, &f)
   120  	if err != nil {
   121  		return err
   122  	}
   123  
   124  	d.SetId(fmt.Sprintf("[%v] %v/%v", f.datastore, f.datacenter, f.destinationFile))
   125  	log.Printf("[INFO] Created file: %s", f.destinationFile)
   126  
   127  	return resourceVSphereFileRead(d, meta)
   128  }
   129  
   130  func createFile(client *govmomi.Client, f *file) error {
   131  
   132  	finder := find.NewFinder(client.Client, true)
   133  
   134  	dc, err := finder.Datacenter(context.TODO(), f.datacenter)
   135  	if err != nil {
   136  		return fmt.Errorf("error %s", err)
   137  	}
   138  	finder = finder.SetDatacenter(dc)
   139  
   140  	ds, err := getDatastore(finder, f.datastore)
   141  	if err != nil {
   142  		return fmt.Errorf("error %s", err)
   143  	}
   144  
   145  	if f.copyFile {
   146  		// Copying file from withing vSphere
   147  		source_dc, err := finder.Datacenter(context.TODO(), f.sourceDatacenter)
   148  		if err != nil {
   149  			return fmt.Errorf("error %s", err)
   150  		}
   151  		finder = finder.SetDatacenter(dc)
   152  
   153  		source_ds, err := getDatastore(finder, f.sourceDatastore)
   154  		if err != nil {
   155  			return fmt.Errorf("error %s", err)
   156  		}
   157  
   158  		fm := object.NewFileManager(client.Client)
   159  		if f.createDirectories {
   160  			directoryPathIndex := strings.LastIndex(f.destinationFile, "/")
   161  			path := f.destinationFile[0:directoryPathIndex]
   162  			err = fm.MakeDirectory(context.TODO(), ds.Path(path), dc, true)
   163  			if err != nil {
   164  				return fmt.Errorf("error %s", err)
   165  			}
   166  		}
   167  		task, err := fm.CopyDatastoreFile(context.TODO(), source_ds.Path(f.sourceFile), source_dc, ds.Path(f.destinationFile), dc, true)
   168  
   169  		if err != nil {
   170  			return fmt.Errorf("error %s", err)
   171  		}
   172  
   173  		_, err = task.WaitForResult(context.TODO(), nil)
   174  		if err != nil {
   175  			return fmt.Errorf("error %s", err)
   176  		}
   177  
   178  	} else {
   179  		// Uploading file to vSphere
   180  		dsurl, err := ds.URL(context.TODO(), dc, f.destinationFile)
   181  		if err != nil {
   182  			return fmt.Errorf("error %s", err)
   183  		}
   184  
   185  		p := soap.DefaultUpload
   186  		err = client.Client.UploadFile(f.sourceFile, dsurl, &p)
   187  		if err != nil {
   188  			return fmt.Errorf("error %s", err)
   189  		}
   190  	}
   191  
   192  	return nil
   193  }
   194  
   195  func resourceVSphereFileRead(d *schema.ResourceData, meta interface{}) error {
   196  
   197  	log.Printf("[DEBUG] reading file: %#v", d)
   198  	f := file{}
   199  
   200  	if v, ok := d.GetOk("source_datacenter"); ok {
   201  		f.sourceDatacenter = v.(string)
   202  	}
   203  
   204  	if v, ok := d.GetOk("datacenter"); ok {
   205  		f.datacenter = v.(string)
   206  	}
   207  
   208  	if v, ok := d.GetOk("source_datastore"); ok {
   209  		f.sourceDatastore = v.(string)
   210  	}
   211  
   212  	if v, ok := d.GetOk("datastore"); ok {
   213  		f.datastore = v.(string)
   214  	} else {
   215  		return fmt.Errorf("datastore argument is required")
   216  	}
   217  
   218  	if v, ok := d.GetOk("source_file"); ok {
   219  		f.sourceFile = v.(string)
   220  	} else {
   221  		return fmt.Errorf("source_file argument is required")
   222  	}
   223  
   224  	if v, ok := d.GetOk("destination_file"); ok {
   225  		f.destinationFile = v.(string)
   226  	} else {
   227  		return fmt.Errorf("destination_file argument is required")
   228  	}
   229  
   230  	client := meta.(*govmomi.Client)
   231  	finder := find.NewFinder(client.Client, true)
   232  
   233  	dc, err := finder.Datacenter(context.TODO(), f.datacenter)
   234  	if err != nil {
   235  		return fmt.Errorf("error %s", err)
   236  	}
   237  	finder = finder.SetDatacenter(dc)
   238  
   239  	ds, err := getDatastore(finder, f.datastore)
   240  	if err != nil {
   241  		return fmt.Errorf("error %s", err)
   242  	}
   243  
   244  	_, err = ds.Stat(context.TODO(), f.destinationFile)
   245  	if err != nil {
   246  		log.Printf("[DEBUG] resourceVSphereFileRead - stat failed on: %v", f.destinationFile)
   247  		d.SetId("")
   248  
   249  		_, ok := err.(object.DatastoreNoSuchFileError)
   250  		if !ok {
   251  			return err
   252  		}
   253  	}
   254  
   255  	return nil
   256  }
   257  
   258  func resourceVSphereFileUpdate(d *schema.ResourceData, meta interface{}) error {
   259  
   260  	log.Printf("[DEBUG] updating file: %#v", d)
   261  
   262  	if d.HasChange("destination_file") || d.HasChange("datacenter") || d.HasChange("datastore") {
   263  		// File needs to be moved, get old and new destination changes
   264  		var oldDataceneter, newDatacenter, oldDatastore, newDatastore, oldDestinationFile, newDestinationFile string
   265  		if d.HasChange("datacenter") {
   266  			tmpOldDataceneter, tmpNewDatacenter := d.GetChange("datacenter")
   267  			oldDataceneter = tmpOldDataceneter.(string)
   268  			newDatacenter = tmpNewDatacenter.(string)
   269  		} else {
   270  			if v, ok := d.GetOk("datacenter"); ok {
   271  				oldDataceneter = v.(string)
   272  				newDatacenter = oldDataceneter
   273  			}
   274  		}
   275  		if d.HasChange("datastore") {
   276  			tmpOldDatastore, tmpNewDatastore := d.GetChange("datastore")
   277  			oldDatastore = tmpOldDatastore.(string)
   278  			newDatastore = tmpNewDatastore.(string)
   279  		} else {
   280  			oldDatastore = d.Get("datastore").(string)
   281  			newDatastore = oldDatastore
   282  		}
   283  		if d.HasChange("destination_file") {
   284  			tmpOldDestinationFile, tmpNewDestinationFile := d.GetChange("destination_file")
   285  			oldDestinationFile = tmpOldDestinationFile.(string)
   286  			newDestinationFile = tmpNewDestinationFile.(string)
   287  		} else {
   288  			oldDestinationFile = d.Get("destination_file").(string)
   289  			newDestinationFile = oldDestinationFile
   290  		}
   291  
   292  		// Get old and new dataceter and datastore
   293  		client := meta.(*govmomi.Client)
   294  		dcOld, err := getDatacenter(client, oldDataceneter)
   295  		if err != nil {
   296  			return err
   297  		}
   298  		dcNew, err := getDatacenter(client, newDatacenter)
   299  		if err != nil {
   300  			return err
   301  		}
   302  		finder := find.NewFinder(client.Client, true)
   303  		finder = finder.SetDatacenter(dcOld)
   304  		dsOld, err := getDatastore(finder, oldDatastore)
   305  		if err != nil {
   306  			return fmt.Errorf("error %s", err)
   307  		}
   308  		finder = finder.SetDatacenter(dcNew)
   309  		dsNew, err := getDatastore(finder, newDatastore)
   310  		if err != nil {
   311  			return fmt.Errorf("error %s", err)
   312  		}
   313  
   314  		// Move file between old/new dataceter, datastore and path (destination_file)
   315  		fm := object.NewFileManager(client.Client)
   316  		task, err := fm.MoveDatastoreFile(context.TODO(), dsOld.Path(oldDestinationFile), dcOld, dsNew.Path(newDestinationFile), dcNew, true)
   317  		if err != nil {
   318  			return err
   319  		}
   320  		_, err = task.WaitForResult(context.TODO(), nil)
   321  		if err != nil {
   322  			return err
   323  		}
   324  	}
   325  
   326  	return nil
   327  }
   328  
   329  func resourceVSphereFileDelete(d *schema.ResourceData, meta interface{}) error {
   330  
   331  	log.Printf("[DEBUG] deleting file: %#v", d)
   332  	f := file{}
   333  
   334  	if v, ok := d.GetOk("datacenter"); ok {
   335  		f.datacenter = v.(string)
   336  	}
   337  
   338  	if v, ok := d.GetOk("datastore"); ok {
   339  		f.datastore = v.(string)
   340  	} else {
   341  		return fmt.Errorf("datastore argument is required")
   342  	}
   343  
   344  	if v, ok := d.GetOk("source_file"); ok {
   345  		f.sourceFile = v.(string)
   346  	} else {
   347  		return fmt.Errorf("source_file argument is required")
   348  	}
   349  
   350  	if v, ok := d.GetOk("destination_file"); ok {
   351  		f.destinationFile = v.(string)
   352  	} else {
   353  		return fmt.Errorf("destination_file argument is required")
   354  	}
   355  
   356  	client := meta.(*govmomi.Client)
   357  
   358  	err := deleteFile(client, &f)
   359  	if err != nil {
   360  		return err
   361  	}
   362  
   363  	d.SetId("")
   364  	return nil
   365  }
   366  
   367  func deleteFile(client *govmomi.Client, f *file) error {
   368  
   369  	dc, err := getDatacenter(client, f.datacenter)
   370  	if err != nil {
   371  		return err
   372  	}
   373  
   374  	finder := find.NewFinder(client.Client, true)
   375  	finder = finder.SetDatacenter(dc)
   376  
   377  	ds, err := getDatastore(finder, f.datastore)
   378  	if err != nil {
   379  		return fmt.Errorf("error %s", err)
   380  	}
   381  
   382  	fm := object.NewFileManager(client.Client)
   383  	task, err := fm.DeleteDatastoreFile(context.TODO(), ds.Path(f.destinationFile), dc)
   384  	if err != nil {
   385  		return err
   386  	}
   387  
   388  	_, err = task.WaitForResult(context.TODO(), nil)
   389  	if err != nil {
   390  		return err
   391  	}
   392  	return nil
   393  }
   394  
   395  // getDatastore gets datastore object
   396  func getDatastore(f *find.Finder, ds string) (*object.Datastore, error) {
   397  
   398  	if ds != "" {
   399  		dso, err := f.Datastore(context.TODO(), ds)
   400  		return dso, err
   401  	} else {
   402  		dso, err := f.DefaultDatastore(context.TODO())
   403  		return dso, err
   404  	}
   405  }