Merge pull request #3867 from chrislovecnm/vsphere-custom-vm-params

vSphere custom vm params
This commit is contained in:
Paul Hinze 2015-12-02 14:00:30 -06:00
commit 6c76984419
3 changed files with 246 additions and 17 deletions

View File

@ -41,21 +41,22 @@ type hardDisk struct {
} }
type virtualMachine struct { type virtualMachine struct {
name string name string
datacenter string datacenter string
cluster string cluster string
resourcePool string resourcePool string
datastore string datastore string
vcpu int vcpu int
memoryMb int64 memoryMb int64
template string template string
networkInterfaces []networkInterface networkInterfaces []networkInterface
hardDisks []hardDisk hardDisks []hardDisk
gateway string gateway string
domain string domain string
timeZone string timeZone string
dnsSuffixes []string dnsSuffixes []string
dnsServers []string dnsServers []string
customConfigurations map[string](types.AnyType)
} }
func resourceVSphereVirtualMachine() *schema.Resource { func resourceVSphereVirtualMachine() *schema.Resource {
@ -135,6 +136,12 @@ func resourceVSphereVirtualMachine() *schema.Resource {
ForceNew: true, ForceNew: true,
}, },
"custom_configuration_parameters": &schema.Schema{
Type: schema.TypeMap,
Optional: true,
ForceNew: true,
},
"network_interface": &schema.Schema{ "network_interface": &schema.Schema{
Type: schema.TypeList, Type: schema.TypeList,
Required: true, Required: true,
@ -261,6 +268,17 @@ func resourceVSphereVirtualMachineCreate(d *schema.ResourceData, meta interface{
vm.dnsServers = DefaultDNSServers vm.dnsServers = DefaultDNSServers
} }
if vL, ok := d.GetOk("custom_configuration_parameters"); ok {
if custom_configs, ok := vL.(map[string]interface{}); ok {
custom := make(map[string]types.AnyType)
for k,v := range custom_configs {
custom[k] = v
}
vm.customConfigurations = custom
log.Printf("[DEBUG] custom_configuration_parameters init: %v", vm.customConfigurations)
}
}
if vL, ok := d.GetOk("network_interface"); ok { if vL, ok := d.GetOk("network_interface"); ok {
networks := make([]networkInterface, len(vL.([]interface{}))) networks := make([]networkInterface, len(vL.([]interface{})))
for i, v := range vL.([]interface{}) { for i, v := range vL.([]interface{}) {
@ -802,6 +820,24 @@ func (vm *virtualMachine) createVirtualMachine(c *govmomi.Client) error {
} }
log.Printf("[DEBUG] virtual machine config spec: %v", configSpec) log.Printf("[DEBUG] virtual machine config spec: %v", configSpec)
// make ExtraConfig
log.Printf("[DEBUG] virtual machine Extra Config spec start")
if len(vm.customConfigurations) > 0 {
var ov []types.BaseOptionValue
for k, v := range vm.customConfigurations {
key := k
value := v
o := types.OptionValue{
Key: key,
Value: &value,
}
log.Printf("[DEBUG] virtual machine Extra Config spec: %s,%s", k,v)
ov = append(ov, &o)
}
configSpec.ExtraConfig = ov
log.Printf("[DEBUG] virtual machine Extra Config spec: %v", configSpec.ExtraConfig)
}
var datastore *object.Datastore var datastore *object.Datastore
if vm.datastore == "" { if vm.datastore == "" {
datastore, err = finder.DefaultDatastore(context.TODO()) datastore, err = finder.DefaultDatastore(context.TODO())
@ -1003,7 +1039,25 @@ func (vm *virtualMachine) deployVirtualMachine(c *govmomi.Client) error {
} }
log.Printf("[DEBUG] virtual machine config spec: %v", configSpec) log.Printf("[DEBUG] virtual machine config spec: %v", configSpec)
// build CustomizationSpec log.Printf("[DEBUG] starting extra custom config spec: %v", vm.customConfigurations)
// make ExtraConfig
if len(vm.customConfigurations) > 0 {
var ov []types.BaseOptionValue
for k, v := range vm.customConfigurations {
key := k
value := v
o := types.OptionValue{
Key: key,
Value: &value,
}
ov = append(ov, &o)
}
configSpec.ExtraConfig = ov
log.Printf("[DEBUG] virtual machine Extra Config spec: %v", configSpec.ExtraConfig)
}
// create CustomizationSpec
customSpec := types.CustomizationSpec{ customSpec := types.CustomizationSpec{
Identity: &types.CustomizationLinuxPrep{ Identity: &types.CustomizationLinuxPrep{
HostName: &types.CustomizationFixedName{ HostName: &types.CustomizationFixedName{
@ -1095,5 +1149,6 @@ func (vm *virtualMachine) deployVirtualMachine(c *govmomi.Client) error {
return err return err
} }
} }
log.Printf("[DEBUG] virtual machine config spec: %v", configSpec)
return nil return nil
} }

View File

@ -10,6 +10,9 @@ import (
"github.com/vmware/govmomi" "github.com/vmware/govmomi"
"github.com/vmware/govmomi/find" "github.com/vmware/govmomi/find"
"github.com/vmware/govmomi/object" "github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/property"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
"golang.org/x/net/context" "golang.org/x/net/context"
) )
@ -127,6 +130,67 @@ func TestAccVSphereVirtualMachine_dhcp(t *testing.T) {
}) })
} }
func TestAccVSphereVirtualMachine_custom_configs(t *testing.T) {
var vm virtualMachine
var locationOpt string
var datastoreOpt string
if v := os.Getenv("VSPHERE_DATACENTER"); v != "" {
locationOpt += fmt.Sprintf(" datacenter = \"%s\"\n", v)
}
if v := os.Getenv("VSPHERE_CLUSTER"); v != "" {
locationOpt += fmt.Sprintf(" cluster = \"%s\"\n", v)
}
if v := os.Getenv("VSPHERE_RESOURCE_POOL"); v != "" {
locationOpt += fmt.Sprintf(" resource_pool = \"%s\"\n", v)
}
if v := os.Getenv("VSPHERE_DATASTORE"); v != "" {
datastoreOpt = fmt.Sprintf(" datastore = \"%s\"\n", v)
}
template := os.Getenv("VSPHERE_TEMPLATE")
label := os.Getenv("VSPHERE_NETWORK_LABEL_DHCP")
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckVSphereVirtualMachineDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: fmt.Sprintf(
testAccCheckVSphereVirtualMachineConfig_custom_configs,
locationOpt,
label,
datastoreOpt,
template,
),
Check: resource.ComposeTestCheckFunc(
testAccCheckVSphereVirtualMachineExistsHasCustomConfig("vsphere_virtual_machine.car", &vm),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.car", "name", "terraform-test-custom"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.car", "vcpu", "2"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.car", "memory", "4096"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.car", "disk.#", "1"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.car", "disk.0.template", template),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.car", "network_interface.#", "1"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.car", "custom_configuration_parameters.foo", "bar"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.car", "custom_configuration_parameters.car", "ferrari"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.car", "custom_configuration_parameters.num", "42"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.car", "network_interface.0.label", label),
),
},
},
})
}
func testAccCheckVSphereVirtualMachineDestroy(s *terraform.State) error { func testAccCheckVSphereVirtualMachineDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*govmomi.Client) client := testAccProvider.Meta().(*govmomi.Client)
finder := find.NewFinder(client.Client, true) finder := find.NewFinder(client.Client, true)
@ -155,8 +219,96 @@ func testAccCheckVSphereVirtualMachineDestroy(s *terraform.State) error {
return nil return nil
} }
func testAccCheckVSphereVirtualMachineExistsHasCustomConfig(n string, vm *virtualMachine) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}
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)
}
dcFolders, err := dc.Folders(context.TODO())
if err != nil {
return fmt.Errorf("error %s", err)
}
_, err = object.NewSearchIndex(client.Client).FindChild(context.TODO(), dcFolders.VmFolder, rs.Primary.Attributes["name"])
if err != nil {
return fmt.Errorf("error %s", err)
}
finder = finder.SetDatacenter(dc)
instance, err := finder.VirtualMachine(context.TODO(), rs.Primary.Attributes["name"])
if err != nil {
return fmt.Errorf("error %s", err)
}
var mvm mo.VirtualMachine
collector := property.DefaultCollector(client.Client)
if err := collector.RetrieveOne(context.TODO(), instance.Reference(), []string{"config.extraConfig"}, &mvm); err != nil {
return fmt.Errorf("error %s", err)
}
var configMap = make(map[string]types.AnyType)
if mvm.Config != nil && mvm.Config.ExtraConfig != nil && len(mvm.Config.ExtraConfig) > 0 {
for _, v := range mvm.Config.ExtraConfig {
value := v.GetOptionValue()
configMap[value.Key] = value.Value
}
} else {
return fmt.Errorf("error no ExtraConfig")
}
if configMap["foo"] == nil {
return fmt.Errorf("error no ExtraConfig for 'foo'")
}
if configMap["foo"] != "bar" {
return fmt.Errorf("error ExtraConfig 'foo' != bar")
}
if configMap["car"] == nil {
return fmt.Errorf("error no ExtraConfig for 'car'")
}
if configMap["car"] != "ferrari" {
return fmt.Errorf("error ExtraConfig 'car' != ferrari")
}
if configMap["num"] == nil {
return fmt.Errorf("error no ExtraConfig for 'num'")
}
// todo this should be an int, getting back a string
if configMap["num"] != "42" {
return fmt.Errorf("error ExtraConfig 'num' != 42")
}
*vm = virtualMachine{
name: rs.Primary.ID,
}
return nil
}
}
func testAccCheckVSphereVirtualMachineExists(n string, vm *virtualMachine) resource.TestCheckFunc { func testAccCheckVSphereVirtualMachineExists(n string, vm *virtualMachine) resource.TestCheckFunc {
return func(s *terraform.State) error { return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n] rs, ok := s.RootModule().Resources[n]
if !ok { if !ok {
return fmt.Errorf("Not found: %s", n) return fmt.Errorf("Not found: %s", n)
@ -186,6 +338,7 @@ func testAccCheckVSphereVirtualMachineExists(n string, vm *virtualMachine) resou
} }
return nil return nil
} }
} }
@ -212,7 +365,6 @@ resource "vsphere_virtual_machine" "foo" {
} }
} }
` `
const testAccCheckVSphereVirtualMachineConfig_dhcp = ` const testAccCheckVSphereVirtualMachineConfig_dhcp = `
resource "vsphere_virtual_machine" "bar" { resource "vsphere_virtual_machine" "bar" {
name = "terraform-test" name = "terraform-test"
@ -228,3 +380,24 @@ resource "vsphere_virtual_machine" "bar" {
} }
} }
` `
const testAccCheckVSphereVirtualMachineConfig_custom_configs = `
resource "vsphere_virtual_machine" "car" {
name = "terraform-test-custom"
%s
vcpu = 2
memory = 4096
network_interface {
label = "%s"
}
custom_configuration_parameters {
"foo" = "bar"
"car" = "ferrari"
"num" = 42
}
disk {
%s
template = "%s"
}
}
`

View File

@ -48,6 +48,7 @@ The following arguments are supported:
* `network_interface` - (Required) Configures virtual network interfaces; see [Network Interfaces](#network-interfaces) below for details. * `network_interface` - (Required) Configures virtual network interfaces; see [Network Interfaces](#network-interfaces) below for details.
* `disk` - (Required) Configures virtual disks; see [Disks](#disks) below for details * `disk` - (Required) Configures virtual disks; see [Disks](#disks) below for details
* `boot_delay` - (Optional) Time in seconds to wait for machine network to be ready. * `boot_delay` - (Optional) Time in seconds to wait for machine network to be ready.
* `custom_configuration_parameters` - (Optional) Map of values that is set as virtual machine custom configurations.
<a id="network-interfaces"></a> <a id="network-interfaces"></a>
## Network Interfaces ## Network Interfaces