Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vSphere custom vm params #3867

Merged
merged 29 commits into from
Dec 2, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
725a735
adding capability to set custom configuration value in virtual machines
chrislovecnm Nov 9, 2015
b5ca146
fixing if and AnyTypes
chrislovecnm Nov 9, 2015
01adcb1
adding new functional test
chrislovecnm Nov 9, 2015
a5050fe
working on read and more testing
chrislovecnm Nov 9, 2015
3f37884
adding capability to set custom configuration value in virtual machines
chrislovecnm Nov 9, 2015
6e19c3f
fixing if and AnyTypes
chrislovecnm Nov 9, 2015
106b126
adding new functional test
chrislovecnm Nov 9, 2015
0bf8ffd
working on read and more testing
chrislovecnm Nov 9, 2015
671f685
Merge branch 'vsphere-custom-vm-params' of https://github.com/chrislo…
chrislovecnm Nov 11, 2015
54b103b
testing finished
chrislovecnm Nov 11, 2015
09ce6b4
updating documentation
chrislovecnm Nov 11, 2015
e899a29
adding capability to set custom configuration value in virtual machines
chrislovecnm Nov 9, 2015
bc36ba7
fixing if and AnyTypes
chrislovecnm Nov 9, 2015
728b2be
adding new functional test
chrislovecnm Nov 9, 2015
0f46b3a
working on read and more testing
chrislovecnm Nov 9, 2015
b47d1cd
adding capability to set custom configuration value in virtual machines
chrislovecnm Nov 9, 2015
cae7fd8
fixing if and AnyTypes
chrislovecnm Nov 9, 2015
8c47441
adding new functional test
chrislovecnm Nov 9, 2015
6615285
working on read and more testing
chrislovecnm Nov 9, 2015
ce6f0ae
testing finished
chrislovecnm Nov 11, 2015
6d13b92
updating documentation
chrislovecnm Nov 11, 2015
50d36f1
merging
chrislovecnm Nov 13, 2015
45fe850
trying to remove changes
chrislovecnm Nov 17, 2015
a5690b7
removing debug print statements
chrislovecnm Nov 17, 2015
98167ce
merging upstream master
Nov 18, 2015
a35b65e
working on better test
chrislovecnm Nov 25, 2015
1d1de99
adding better acceptance test to check custom config
chrislovecnm Nov 25, 2015
d70cdde
merging in upstream, because rebase was insane
chrislovecnm Nov 25, 2015
de2c76a
polish
chrislovecnm Nov 25, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 71 additions & 16 deletions builtin/providers/vsphere/resource_vsphere_virtual_machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,21 +41,22 @@ type hardDisk struct {
}

type virtualMachine struct {
name string
datacenter string
cluster string
resourcePool string
datastore string
vcpu int
memoryMb int64
template string
networkInterfaces []networkInterface
hardDisks []hardDisk
gateway string
domain string
timeZone string
dnsSuffixes []string
dnsServers []string
name string
datacenter string
cluster string
resourcePool string
datastore string
vcpu int
memoryMb int64
template string
networkInterfaces []networkInterface
hardDisks []hardDisk
gateway string
domain string
timeZone string
dnsSuffixes []string
dnsServers []string
customConfigurations map[string](types.AnyType)
}

func resourceVSphereVirtualMachine() *schema.Resource {
Expand Down Expand Up @@ -135,6 +136,12 @@ func resourceVSphereVirtualMachine() *schema.Resource {
ForceNew: true,
},

"custom_configuration_parameters": &schema.Schema{
Type: schema.TypeMap,
Optional: true,
ForceNew: true,
},

"network_interface": &schema.Schema{
Type: schema.TypeList,
Required: true,
Expand Down Expand Up @@ -261,6 +268,17 @@ func resourceVSphereVirtualMachineCreate(d *schema.ResourceData, meta interface{
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 {
networks := make([]networkInterface, len(vL.([]interface{})))
for i, v := range vL.([]interface{}) {
Expand Down Expand Up @@ -802,6 +820,24 @@ func (vm *virtualMachine) createVirtualMachine(c *govmomi.Client) error {
}
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
if vm.datastore == "" {
datastore, err = finder.DefaultDatastore(context.TODO())
Expand Down Expand Up @@ -1003,7 +1039,25 @@ func (vm *virtualMachine) deployVirtualMachine(c *govmomi.Client) error {
}
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{
Identity: &types.CustomizationLinuxPrep{
HostName: &types.CustomizationFixedName{
Expand Down Expand Up @@ -1095,5 +1149,6 @@ func (vm *virtualMachine) deployVirtualMachine(c *govmomi.Client) error {
return err
}
}
log.Printf("[DEBUG] virtual machine config spec: %v", configSpec)
return nil
}
175 changes: 174 additions & 1 deletion builtin/providers/vsphere/resource_vsphere_virtual_machine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import (
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/find"
"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"
)

Expand Down Expand Up @@ -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"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These helpers are only checking the state file, so right now no verification is being done on the custom parameters.

In other words, if you remove all your code but the addition of the field to the schema, this test will still pass!

I'd recommend writing a CheckFunc that accepts &vm and inspects it to prove the custom attributes get set.

Something along these lines:

func testAccCheckVSphereVirtualMachineCustomConfigs(vm *virtualMachine) resource.TestCheckFunc {
  return func(s *terraform.State) error {
    // use the vmware client to ensure that the passed in vm has the expected custom config
    // return an error if not
  }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is exactly what I was asking about in another ticket 😄 Will do!!

resource.TestCheckResourceAttr(
"vsphere_virtual_machine.car", "network_interface.0.label", label),
),
},
},
})
}

func testAccCheckVSphereVirtualMachineDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*govmomi.Client)
finder := find.NewFinder(client.Client, true)
Expand Down Expand Up @@ -155,8 +219,96 @@ func testAccCheckVSphereVirtualMachineDestroy(s *terraform.State) error {
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 {
return func(s *terraform.State) error {

rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
Expand Down Expand Up @@ -186,6 +338,7 @@ func testAccCheckVSphereVirtualMachineExists(n string, vm *virtualMachine) resou
}

return nil

}
}

Expand All @@ -212,7 +365,6 @@ resource "vsphere_virtual_machine" "foo" {
}
}
`

const testAccCheckVSphereVirtualMachineConfig_dhcp = `
resource "vsphere_virtual_machine" "bar" {
name = "terraform-test"
Expand All @@ -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"
}
}
`
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ The following arguments are supported:
* `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
* `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>
## Network Interfaces
Expand Down