Skip to content

Commit

Permalink
Content library vm-template, disk type, and vApp props (#1198)
Browse files Browse the repository at this point in the history
  • Loading branch information
bill-rich authored Sep 2, 2020
1 parent 19c0d81 commit 58d4485
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 77 deletions.
153 changes: 111 additions & 42 deletions vsphere/internal/helper/virtualmachine/virtual_machine_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ import (
"context"
"errors"
"fmt"
"github.com/vmware/govmomi/vapi/library"
"github.com/vmware/govmomi/vapi/rest"
"github.com/vmware/govmomi/vapi/vcenter"
"log"
"net"
"strconv"
"strings"
"time"

"github.com/vmware/govmomi/vapi/library"
"github.com/vmware/govmomi/vapi/vcenter"

"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/terraform-provider-vsphere/vsphere/internal/helper/folder"
"github.com/hashicorp/terraform-provider-vsphere/vsphere/internal/helper/provider"
"github.com/hashicorp/terraform-provider-vsphere/vsphere/internal/helper/structure"
Expand Down Expand Up @@ -46,6 +47,24 @@ type UUIDNotFoundError struct {
s string
}

// VCenterDeploy containss everything required to create a VM from a content

This comment has been minimized.

Copy link
@travis-bear

travis-bear Sep 24, 2020

containss typo

// library item, and reduces the number of arguments passed between functions.
type VCenterDeploy struct {
VCenterManager *vcenter.Manager

VMName string
Annotation string
FolderID string
DatastoreID string
ResourcePoolID string
HostSystemID string
StoragePolicyID string
DiskType string
NetworkMap []vcenter.NetworkMapping
VAppProperties []vcenter.Property
LibraryItem *library.Item
}

// Error implements error for UUIDNotFoundError.
func (e *UUIDNotFoundError) Error() string {
return e.s
Expand Down Expand Up @@ -526,59 +545,109 @@ func Clone(c *govmomi.Client, src *object.VirtualMachine, f *object.Folder, name
return FromMOID(c, result.Result.(types.ManagedObjectReference).Value)
}

func DeployDest(name string, annotation string, rp *object.ResourcePool, host *object.HostSystem, folder *object.Folder, ds viapi.ManagedObject, spId string, nm []vcenter.NetworkMapping) *vcenter.Deploy {
rpId := ""
hostId := ""
folderId := ""
// Deploy clones a virtual machine from a content library item.
func Deploy(deployData *VCenterDeploy) (*types.ManagedObjectReference, error) {
log.Printf("[DEBUG] virtualmachine.Deploy: Deploying VM from Content Library item.")
// Get OVF mappings for NICs

if rp != nil {
rpId = rp.Reference().Value
switch deployData.LibraryItem.Type {
case library.ItemTypeOVF:
return deployData.deployOvf()
case library.ItemTypeVMTX:
return deployData.deployVmtx()
default:
return nil, fmt.Errorf("unsupported library item type: %s", deployData.LibraryItem.Type)
}
if host != nil {
hostId = host.Reference().Value
}

func (deployData *VCenterDeploy) deployVmtx() (*types.ManagedObjectReference, error) {
storage := &vcenter.DiskStorage{
Datastore: deployData.DatastoreID,
StoragePolicy: &vcenter.StoragePolicy{
Policy: deployData.StoragePolicyID,
Type: "USE_SOURCE_POLICY",
},
}
if folder != nil {
folderId = folder.Reference().Value
if deployData.StoragePolicyID != "" {
storage.StoragePolicy.Type = "USE_SPECIFIED_POLICY"
}

deploy := vcenter.DeployTemplate{
Name: deployData.VMName,
Description: deployData.Annotation,
DiskStorage: storage,
VMHomeStorage: storage,
Placement: &vcenter.Placement{
ResourcePool: deployData.ResourcePoolID,
Host: deployData.HostSystemID,
Folder: deployData.FolderID,
},
}
ctx := context.TODO()
return deployData.VCenterManager.DeployTemplateLibraryItem(ctx, deployData.LibraryItem.ID, deploy)
}

d := vcenter.Deploy{
func (deployData *VCenterDeploy) deployOvf() (*types.ManagedObjectReference, error) {
deploy := vcenter.Deploy{
DeploymentSpec: vcenter.DeploymentSpec{
Name: name,
Annotation: annotation,
AcceptAllEULA: true,
NetworkMappings: nm,
StorageMappings: nil,
StorageProvisioning: "",
StorageProfileID: spId,
Locale: "",
Flags: nil,
AdditionalParams: nil,
DefaultDatastoreID: ds.Reference().Value,
Name: deployData.VMName,
DefaultDatastoreID: deployData.DatastoreID,
AcceptAllEULA: true,
Annotation: deployData.Annotation,
AdditionalParams: []vcenter.AdditionalParams{
{
Class: vcenter.ClassDeploymentOptionParams,
Type: vcenter.TypeDeploymentOptionParams,
},
{
Class: vcenter.ClassPropertyParams,
Type: vcenter.TypePropertyParams,
Properties: deployData.VAppProperties,
},
},
NetworkMappings: deployData.NetworkMap,
StorageProvisioning: deployData.DiskType,
StorageProfileID: deployData.StoragePolicyID,
},
Target: vcenter.Target{
ResourcePoolID: rpId,
HostID: hostId,
FolderID: folderId,
ResourcePoolID: deployData.ResourcePoolID,
HostID: deployData.HostSystemID,
FolderID: deployData.FolderID,
},
}
return &d
ctx := context.TODO()
return deployData.VCenterManager.DeployLibraryItem(ctx, deployData.LibraryItem.ID, deploy)
}

func Deploy(c *rest.Client, item *library.Item, deploy *vcenter.Deploy, timeout int) (*types.ManagedObjectReference, error) {
log.Printf("[DEBUG] virtualmachine.Deploy: Deploying VM from Content Library item %s", item.Name)
m := vcenter.NewManager(c)
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*time.Duration(timeout))
defer cancel()

mo, err := m.DeployLibraryItem(ctx, item.ID, *deploy)
if err != nil {
if ctx.Err() == context.DeadlineExceeded {
err = errors.New("timeout waiting for deploy to complete")
// VAppProperties converts the vApp properties from the configuration and
// converts them into a slice of vcenter.Properties to be used while deploying
// content library items as VMs.
func VAppProperties(propertyMap map[string]interface{}) []vcenter.Property {
properties := []vcenter.Property{}
for key, value := range propertyMap {
property := vcenter.Property{
ID: key,
Label: "",
Value: value.(string),
}
return nil, err
properties = append(properties, property)
}
return properties
}

// DiskType converts standard disk type labels into labels used in content
// library deployment specs.
func DiskType(d *schema.ResourceData) string {
thin := d.Get("disk.0.thin_provisioned").(bool)
eagerlyScrub := d.Get("disk.0.eagerly_scrub").(bool)
switch {
case thin:
return "thin"
case eagerlyScrub:
return "eagerZeroedThick"
default:
return "thick"
}
log.Printf("[DEBUG] virtualmachine.Deploy: Successfully deployed VM from Content Library item %s", item.Name)
return mo, nil
}

// Customize wraps the customization of a virtual machine and the subsequent
Expand Down
69 changes: 34 additions & 35 deletions vsphere/resource_vsphere_virtual_machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import (
"context"
"errors"
"fmt"
"github.com/hashicorp/terraform-provider-vsphere/vsphere/internal/helper/contentlibrary"
"github.com/hashicorp/terraform-provider-vsphere/vsphere/internal/helper/ovfdeploy"
"log"
"net"
"os"
"strings"
"time"

"github.com/hashicorp/terraform-provider-vsphere/vsphere/internal/helper/contentlibrary"
"github.com/hashicorp/terraform-provider-vsphere/vsphere/internal/helper/ovfdeploy"

"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
"github.com/hashicorp/terraform-provider-vsphere/vsphere/internal/helper/customattribute"
Expand All @@ -30,6 +31,7 @@ import (
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/ovf"
"github.com/vmware/govmomi/vapi/vcenter"
"github.com/vmware/govmomi/vim25/types"
)

Expand Down Expand Up @@ -1483,6 +1485,31 @@ func resourceVsphereMachineDeployOvfAndOva(d *schema.ResourceData, meta interfac
return vm, nil
}

func createVCenterDeploy(d *schema.ResourceData, meta interface{}) (*virtualmachine.VCenterDeploy, error) {
restClient := meta.(*VSphereClient).restClient
vCenterManager := vcenter.NewManager(restClient)

item, err := contentlibrary.ItemFromID(restClient, d.Get("clone.0.template_uuid").(string))
if err != nil {
return nil, err
}

return &virtualmachine.VCenterDeploy{
VMName: d.Get("name").(string),
Annotation: d.Get("annotation").(string),
FolderID: d.Get("folder_id").(string),
DatastoreID: d.Get("datastore_id").(string),
NetworkMap: contentlibrary.MapNetworkDevices(d),
ResourcePoolID: d.Get("resource_pool_id").(string),
HostSystemID: d.Get("host_system_id").(string),
StoragePolicyID: d.Get("storage_policy_id").(string),
VAppProperties: virtualmachine.VAppProperties(d.Get("vapp.0.properties").(map[string]interface{})),
DiskType: virtualmachine.DiskType(d),
VCenterManager: vCenterManager,
LibraryItem: item,
}, nil
}

// resourceVSphereVirtualMachineCreateClone contains the clone VM deploy
// path. The VM is returned.
func resourceVSphereVirtualMachineCreateClone(d *schema.ResourceData, meta interface{}) (*object.VirtualMachine, error) {
Expand All @@ -1507,41 +1534,13 @@ func resourceVSphereVirtualMachineCreateClone(d *schema.ResourceData, meta inter
name := d.Get("name").(string)
timeout := d.Get("clone.0.timeout").(int)
var vm *object.VirtualMachine
if contentlibrary.IsContentLibraryItem(meta.(*VSphereClient).restClient, d.Get("clone.0.template_uuid").(string)) {
// Clone source is an item from a Content Library. Prepare required resources.

// First, if a host is set, check that it is valid.
host := &object.HostSystem{}
if hid := d.Get("host_system_id").(string); hid != "" {
host, err = hostsystem.FromID(client, hid)
if err != nil {
return nil, err
}
}

// Get OVF mappings for NICs
nm := contentlibrary.MapNetworkDevices(d)

// Validate the specified datastore
dsID := d.Get("datastore_id")
ds, err := datastore.FromID(client, dsID.(string))
if err != nil {
return nil, err
}
switch contentlibrary.IsContentLibraryItem(meta.(*VSphereClient).restClient, d.Get("clone.0.template_uuid").(string)) {
case true:
deploySpec, err := createVCenterDeploy(d, meta)
if err != nil {
return nil, err
}

spId := d.Get("storage_policy_id").(string)

dd := virtualmachine.DeployDest(name, d.Get("annotation").(string), pool, host, fo, ds, spId, nm)

rclient := meta.(*VSphereClient).restClient
item, err := contentlibrary.ItemFromID(rclient, d.Get("clone.0.template_uuid").(string))
if err != nil {
return nil, err
}
vmoid, err := virtualmachine.Deploy(rclient, item, dd, 30)
vmoid, err := virtualmachine.Deploy(deploySpec)
if err != nil {
return nil, err
}
Expand All @@ -1552,7 +1551,7 @@ func resourceVSphereVirtualMachineCreateClone(d *schema.ResourceData, meta inter
// There is not currently a way to pull config values from Content Library items. If we do not send the values,
// the defaults from the template will be used.
d.Set("guest_id", "")
} else {
case false:
// Expand the clone spec. We get the source VM here too.
cloneSpec, srcVM, err := vmworkflow.ExpandVirtualMachineCloneSpec(d, client)
if err != nil {
Expand Down

0 comments on commit 58d4485

Please sign in to comment.