forked from hashicorp/terraform-provider-vsphere
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request hashicorp#142 from terraform-providers/f-vmfs-data…
…store-resource New resource: vsphere_vmfs_datastore
- Loading branch information
Showing
13 changed files
with
1,664 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package vsphere | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/vmware/govmomi" | ||
"github.com/vmware/govmomi/find" | ||
"github.com/vmware/govmomi/object" | ||
"github.com/vmware/govmomi/vim25/mo" | ||
"github.com/vmware/govmomi/vim25/types" | ||
) | ||
|
||
// datastoreFromID locates a Datastore by its managed object reference ID. | ||
func datastoreFromID(client *govmomi.Client, id string) (*object.Datastore, error) { | ||
finder := find.NewFinder(client.Client, false) | ||
|
||
ref := types.ManagedObjectReference{ | ||
Type: "Datastore", | ||
Value: id, | ||
} | ||
|
||
ctx, cancel := context.WithTimeout(context.Background(), defaultAPITimeout) | ||
defer cancel() | ||
ds, err := finder.ObjectReference(ctx, ref) | ||
if err != nil { | ||
return nil, err | ||
} | ||
// Should be safe to return here. If our reference returned here and is not a | ||
// datastore, then we have bigger problems and to be honest we should be | ||
// panicking anyway. | ||
return ds.(*object.Datastore), nil | ||
} | ||
|
||
// datastoreProperties is a convenience method that wraps fetching the | ||
// Datastore MO from its higher-level object. | ||
func datastoreProperties(ds *object.Datastore) (*mo.Datastore, error) { | ||
ctx, cancel := context.WithTimeout(context.Background(), defaultAPITimeout) | ||
defer cancel() | ||
var props mo.Datastore | ||
if err := ds.Properties(ctx, ds.Reference(), nil, &props); err != nil { | ||
return nil, err | ||
} | ||
return &props, nil | ||
} | ||
|
||
// moveDatastoreToFolder is a complex method that moves a datastore to a given | ||
// relative datastore folder path. "Relative" here means relative to a | ||
// datacenter, which is discovered from the current datastore path. | ||
func moveDatastoreToFolder(client *govmomi.Client, ds *object.Datastore, relative string) error { | ||
folder, err := datastoreFolderFromObject(client, ds, relative) | ||
if err != nil { | ||
return err | ||
} | ||
return moveObjectToFolder(ds.Reference(), folder) | ||
} | ||
|
||
// moveDatastoreToFolderRelativeHostSystemID is a complex method that moves a | ||
// datastore to a given datastore path, similar to moveDatastoreToFolder, | ||
// except the path is relative to a HostSystem supplied by ID instead of the | ||
// datastore. | ||
func moveDatastoreToFolderRelativeHostSystemID(client *govmomi.Client, ds *object.Datastore, hsID, relative string) error { | ||
hs, err := hostSystemFromID(client, hsID) | ||
if err != nil { | ||
return err | ||
} | ||
folder, err := datastoreFolderFromObject(client, hs, relative) | ||
if err != nil { | ||
return err | ||
} | ||
return moveObjectToFolder(ds.Reference(), folder) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package vsphere | ||
|
||
import ( | ||
"github.com/hashicorp/terraform/helper/schema" | ||
"github.com/vmware/govmomi/vim25/types" | ||
) | ||
|
||
// schemaDatastoreSummary returns schema items for resources that | ||
// need to work with a DatastoreSummary. | ||
func schemaDatastoreSummary() map[string]*schema.Schema { | ||
return map[string]*schema.Schema{ | ||
// Note that the following fields are not represented in the schema here: | ||
// * Name (more than likely the ID attribute and will be represented in | ||
// resource schema) | ||
// * Type (redundant attribute as the datastore type will be represented by | ||
// the resource) | ||
"accessible": &schema.Schema{ | ||
Type: schema.TypeBool, | ||
Description: "The connectivity status of the datastore. If this is false, some other computed attributes may be out of date.", | ||
Computed: true, | ||
}, | ||
"capacity": &schema.Schema{ | ||
Type: schema.TypeInt, | ||
Description: "Maximum capacity of the datastore, in MB.", | ||
Computed: true, | ||
}, | ||
"free_space": &schema.Schema{ | ||
Type: schema.TypeInt, | ||
Description: "Available space of this datastore, in MB.", | ||
Computed: true, | ||
}, | ||
"maintenance_mode": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Description: "The current maintenance mode state of the datastore.", | ||
Computed: true, | ||
}, | ||
"multiple_host_access": &schema.Schema{ | ||
Type: schema.TypeBool, | ||
Description: "If true, more than one host in the datacenter has been configured with access to the datastore.", | ||
Computed: true, | ||
}, | ||
"uncommitted_space": &schema.Schema{ | ||
Type: schema.TypeInt, | ||
Description: "Total additional storage space, in MB, potentially used by all virtual machines on this datastore.", | ||
Computed: true, | ||
}, | ||
"url": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Description: "The unique locator for the datastore.", | ||
Computed: true, | ||
}, | ||
} | ||
} | ||
|
||
// flattenDatastoreSummary reads various fields from a DatastoreSummary into | ||
// the passed in ResourceData. | ||
func flattenDatastoreSummary(d *schema.ResourceData, obj *types.DatastoreSummary) error { | ||
d.Set("accessible", obj.Accessible) | ||
d.Set("capacity", byteToMB(obj.Capacity)) | ||
d.Set("free_space", byteToMB(obj.FreeSpace)) | ||
d.Set("maintenance_mode", obj.MaintenanceMode) | ||
d.Set("multiple_host_access", obj.MultipleHostAccess) | ||
d.Set("uncommitted_space", byteToMB(obj.Uncommitted)) | ||
d.Set("url", obj.Url) | ||
|
||
// Set the name attribute off of the name here - since we do not track this | ||
// here we check for errors | ||
if err := d.Set("name", obj.Name); err != nil { | ||
return err | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
package vsphere | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"path" | ||
"reflect" | ||
"strings" | ||
|
||
"github.com/vmware/govmomi" | ||
"github.com/vmware/govmomi/find" | ||
"github.com/vmware/govmomi/object" | ||
"github.com/vmware/govmomi/vim25/mo" | ||
"github.com/vmware/govmomi/vim25/types" | ||
) | ||
|
||
// rootPathParticle is the section of a vSphere inventory path that denotes a | ||
// specific kind of inventory item. | ||
type rootPathParticle string | ||
|
||
// String implements Stringer for rootPathParticle. | ||
func (p rootPathParticle) String() string { | ||
return string(p) | ||
} | ||
|
||
// Delimeter returns the path delimiter for the particle, which is basically | ||
// just a particle with a leading slash. | ||
func (p rootPathParticle) Delimeter() string { | ||
return string("/" + p) | ||
} | ||
|
||
// SplitDatacenter is a convenience method that splits out the datacenter path | ||
// from the supplied path for the particle. | ||
func (p rootPathParticle) SplitDatacenter(inventoryPath string) (string, error) { | ||
s := strings.SplitN(inventoryPath, p.Delimeter(), 2) | ||
if len(s) != 2 { | ||
return inventoryPath, fmt.Errorf("could not split path %q on %q", inventoryPath, p.Delimeter()) | ||
} | ||
return s[0], nil | ||
} | ||
|
||
// SplitRelativeFolder is a convenience method that splits out the relative | ||
// folder from the supplied path for the particle. | ||
func (p rootPathParticle) SplitRelativeFolder(inventoryPath string) (string, error) { | ||
s := strings.SplitN(inventoryPath, p.Delimeter(), 2) | ||
if len(s) != 2 { | ||
return inventoryPath, fmt.Errorf("could not split path %q on %q", inventoryPath, p.Delimeter()) | ||
} | ||
return path.Dir(s[1]), nil | ||
} | ||
|
||
// NewRootFromPath takes the datacenter path for a specific entity, and then | ||
// appends the new particle supplied. | ||
func (p rootPathParticle) NewRootFromPath(inventoryPath string, newParticle rootPathParticle) (string, error) { | ||
dcPath, err := p.SplitDatacenter(inventoryPath) | ||
if err != nil { | ||
return inventoryPath, err | ||
} | ||
return fmt.Sprintf("%s/%s", dcPath, newParticle), nil | ||
} | ||
|
||
// PathFromNewRoot takes the datacenter path for a specific entity, and then | ||
// appends the new particle supplied with the new relative path. | ||
// | ||
// As an example, consider a supplied host path "/dc1/host/cluster1/esxi1", and | ||
// a supplied datastore folder relative path of "/foo/bar". This function will | ||
// split off the datacenter section of the path (/dc1) and combine it with the | ||
// datastore folder with the proper delimiter. The resulting path will be | ||
// "/dc1/datastore/foo/bar". | ||
func (p rootPathParticle) PathFromNewRoot(inventoryPath string, newParticle rootPathParticle, relative string) (string, error) { | ||
rootPath, err := p.NewRootFromPath(inventoryPath, newParticle) | ||
if err != nil { | ||
return inventoryPath, err | ||
} | ||
return path.Clean(fmt.Sprintf("%s/%s", rootPath, relative)), nil | ||
} | ||
|
||
const ( | ||
rootPathParticleVM = rootPathParticle("vm") | ||
rootPathParticleNetwork = rootPathParticle("network") | ||
rootPathParticleHost = rootPathParticle("host") | ||
rootPathParticleDatastore = rootPathParticle("datastore") | ||
) | ||
|
||
// datacenterPathFromHostSystemID returns the datacenter section of a | ||
// HostSystem's inventory path. | ||
func datacenterPathFromHostSystemID(client *govmomi.Client, hsID string) (string, error) { | ||
hs, err := hostSystemFromID(client, hsID) | ||
if err != nil { | ||
return "", err | ||
} | ||
return rootPathParticleHost.SplitDatacenter(hs.InventoryPath) | ||
} | ||
|
||
// datastoreRootPathFromHostSystemID returns the root datastore folder path | ||
// for a specific host system ID. | ||
func datastoreRootPathFromHostSystemID(client *govmomi.Client, hsID string) (string, error) { | ||
hs, err := hostSystemFromID(client, hsID) | ||
if err != nil { | ||
return "", err | ||
} | ||
return rootPathParticleHost.NewRootFromPath(hs.InventoryPath, rootPathParticleDatastore) | ||
} | ||
|
||
// folderFromObject returns an *object.Folder from a given object of specific | ||
// types, and relative path of a type defined in folderType. If no such folder | ||
// is found, an appropriate error will be returned. | ||
// | ||
// The list of supported object types will grow as the provider supports more | ||
// resources. | ||
func folderFromObject(client *govmomi.Client, obj interface{}, folderType rootPathParticle, relative string) (*object.Folder, error) { | ||
if err := validateVirtualCenter(client); err != nil { | ||
return nil, err | ||
} | ||
var p string | ||
var err error | ||
switch o := obj.(type) { | ||
case (*object.Datastore): | ||
p, err = rootPathParticleDatastore.PathFromNewRoot(o.InventoryPath, folderType, relative) | ||
case (*object.HostSystem): | ||
p, err = rootPathParticleHost.PathFromNewRoot(o.InventoryPath, folderType, relative) | ||
default: | ||
return nil, fmt.Errorf("unsupported object type %T", o) | ||
} | ||
if err != nil { | ||
return nil, err | ||
} | ||
// Set up a finder. Don't set datacenter here as we are looking for full | ||
// path, should not be necessary. | ||
finder := find.NewFinder(client.Client, false) | ||
ctx, cancel := context.WithTimeout(context.Background(), defaultAPITimeout) | ||
defer cancel() | ||
folder, err := finder.Folder(ctx, p) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return folder, nil | ||
} | ||
|
||
// datastoreFolderFromObject returns an *object.Folder from a given object, | ||
// and relative datastore folder path. If no such folder is found, of if it is | ||
// not a datastore folder, an appropriate error will be returned. | ||
func datastoreFolderFromObject(client *govmomi.Client, obj interface{}, relative string) (*object.Folder, error) { | ||
folder, err := folderFromObject(client, obj, rootPathParticleDatastore, relative) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return validateDatastoreFolder(folder) | ||
} | ||
|
||
// validateDatastoreFolder checks to make sure the folder is a datastore | ||
// folder, and returns it if it is not, or an error if it isn't. | ||
func validateDatastoreFolder(folder *object.Folder) (*object.Folder, error) { | ||
var props mo.Folder | ||
ctx, cancel := context.WithTimeout(context.Background(), defaultAPITimeout) | ||
defer cancel() | ||
if err := folder.Properties(ctx, folder.Reference(), nil, &props); err != nil { | ||
return nil, err | ||
} | ||
if !reflect.DeepEqual(props.ChildType, []string{"Folder", "Datastore", "StoragePod"}) { | ||
return nil, fmt.Errorf("%q is not a datastore folder", folder.InventoryPath) | ||
} | ||
return folder, nil | ||
} | ||
|
||
// pathIsEmpty checks a folder path to see if it's "empty" (ie: would resolve | ||
// to the root inventory path for a given type in a datacenter - "" or "/"). | ||
func pathIsEmpty(path string) bool { | ||
return path == "" || path == "/" | ||
} | ||
|
||
// normalizeFolderPath is a SchemaStateFunc that normalizes a folder path. | ||
func normalizeFolderPath(v interface{}) string { | ||
p := v.(string) | ||
if pathIsEmpty(p) { | ||
return "" | ||
} | ||
return strings.TrimPrefix(path.Clean(p), "/") | ||
} | ||
|
||
// moveObjectToFolder moves a object by reference into a folder. | ||
func moveObjectToFolder(ref types.ManagedObjectReference, folder *object.Folder) error { | ||
ctx, cancel := context.WithTimeout(context.Background(), defaultAPITimeout) | ||
defer cancel() | ||
task, err := folder.MoveInto(ctx, []types.ManagedObjectReference{ref}) | ||
if err != nil { | ||
return err | ||
} | ||
tctx, tcancel := context.WithTimeout(context.Background(), defaultAPITimeout) | ||
defer tcancel() | ||
return task.Wait(tctx) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.