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

provider/vsphere: IPv6 support. #6457

Merged
merged 2 commits into from
May 3, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
91 changes: 65 additions & 26 deletions builtin/providers/vsphere/resource_vsphere_virtual_machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@ type networkInterface struct {
label string
ipv4Address string
ipv4PrefixLength int
ipv4Gateway string
ipv6Address string
ipv6PrefixLength int
ipv6Gateway string
adapterType string // TODO: Make "adapter_type" argument
}

Expand Down Expand Up @@ -77,7 +79,6 @@ type virtualMachine struct {
networkInterfaces []networkInterface
hardDisks []hardDisk
cdroms []cdrom
gateway string
domain string
timeZone string
dnsSuffixes []string
Expand Down Expand Up @@ -163,9 +164,10 @@ func resourceVSphereVirtualMachine() *schema.Resource {
ForceNew: true,
},
"gateway": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Deprecated: "Please use network_interface.ipv4_gateway",
},

"domain": &schema.Schema{
Expand Down Expand Up @@ -285,16 +287,27 @@ func resourceVSphereVirtualMachine() *schema.Resource {
Computed: true,
},

// TODO: Imprement ipv6 parameters to be optional
"ipv4_gateway": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},

"ipv6_address": &schema.Schema{
Type: schema.TypeString,
Computed: true,
Optional: true,
ForceNew: true,
},

"ipv6_prefix_length": &schema.Schema{
Type: schema.TypeInt,
Computed: true,
Optional: true,
ForceNew: true,
},

"ipv6_gateway": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},

Expand Down Expand Up @@ -515,10 +528,6 @@ func resourceVSphereVirtualMachineCreate(d *schema.ResourceData, meta interface{
vm.resourcePool = v.(string)
}

if v, ok := d.GetOk("gateway"); ok {
vm.gateway = v.(string)
}

if v, ok := d.GetOk("domain"); ok {
vm.domain = v.(string)
}
Expand Down Expand Up @@ -570,6 +579,9 @@ func resourceVSphereVirtualMachineCreate(d *schema.ResourceData, meta interface{
if v, ok := network["ip_address"].(string); ok && v != "" {
networks[i].ipv4Address = v
}
if v, ok := d.GetOk("gateway"); ok {
networks[i].ipv4Gateway = v.(string)
}
if v, ok := network["subnet_mask"].(string); ok && v != "" {
ip := net.ParseIP(v).To4()
if ip != nil {
Expand All @@ -586,6 +598,18 @@ func resourceVSphereVirtualMachineCreate(d *schema.ResourceData, meta interface{
if v, ok := network["ipv4_prefix_length"].(int); ok && v != 0 {
networks[i].ipv4PrefixLength = v
}
if v, ok := network["ipv4_gateway"].(string); ok && v != "" {
networks[i].ipv4Gateway = v
}
if v, ok := network["ipv6_address"].(string); ok && v != "" {
networks[i].ipv6Address = v
}
if v, ok := network["ipv6_prefix_length"].(int); ok && v != 0 {
networks[i].ipv6PrefixLength = v
}
if v, ok := network["ipv6_gateway"].(string); ok && v != "" {
networks[i].ipv6Gateway = v
}
}
vm.networkInterfaces = networks
log.Printf("[DEBUG] network_interface init: %v", networks)
Expand Down Expand Up @@ -1473,33 +1497,48 @@ func (vm *virtualMachine) deployVirtualMachine(c *govmomi.Client) error {
}
networkDevices = append(networkDevices, nd)

// TODO: IPv6 support
var ipSetting types.CustomizationIPSettings
if network.ipv4Address == "" {
ipSetting = types.CustomizationIPSettings{
Ip: &types.CustomizationDhcpIpGenerator{},
}
ipSetting.Ip = &types.CustomizationDhcpIpGenerator{}
} else {
if network.ipv4PrefixLength == 0 {
return fmt.Errorf("Error: ipv4_prefix_length argument is empty.")
}
m := net.CIDRMask(network.ipv4PrefixLength, 32)
sm := net.IPv4(m[0], m[1], m[2], m[3])
subnetMask := sm.String()
log.Printf("[DEBUG] gateway: %v", vm.gateway)
log.Printf("[DEBUG] ipv4 address: %v", network.ipv4Address)
log.Printf("[DEBUG] ipv4 prefix length: %v", network.ipv4PrefixLength)
log.Printf("[DEBUG] ipv4 subnet mask: %v", subnetMask)
ipSetting = types.CustomizationIPSettings{
Gateway: []string{
vm.gateway,
},
Ip: &types.CustomizationFixedIp{
IpAddress: network.ipv4Address,
log.Printf("[DEBUG] ipv4 gateway: %v\n", network.ipv4Gateway)
log.Printf("[DEBUG] ipv4 address: %v\n", network.ipv4Address)
log.Printf("[DEBUG] ipv4 prefix length: %v\n", network.ipv4PrefixLength)
log.Printf("[DEBUG] ipv4 subnet mask: %v\n", subnetMask)
ipSetting.Gateway = []string{
network.ipv4Gateway,
}
ipSetting.Ip = &types.CustomizationFixedIp{
IpAddress: network.ipv4Address,
}
ipSetting.SubnetMask = subnetMask
}

ipv6Spec := &types.CustomizationIPSettingsIpV6AddressSpec{}
if network.ipv6Address == "" {
ipv6Spec.Ip = []types.BaseCustomizationIpV6Generator{
&types.CustomizationDhcpIpV6Generator{},
}
} else {
log.Printf("[DEBUG] ipv6 gateway: %v\n", network.ipv6Gateway)
log.Printf("[DEBUG] ipv6 address: %v\n", network.ipv6Address)
log.Printf("[DEBUG] ipv6 prefix length: %v\n", network.ipv6PrefixLength)

ipv6Spec.Ip = []types.BaseCustomizationIpV6Generator{
&types.CustomizationFixedIpV6{
IpAddress: network.ipv6Address,
SubnetMask: network.ipv6PrefixLength,
},
SubnetMask: subnetMask,
}
ipv6Spec.Gateway = []string{network.ipv6Gateway}
}
ipSetting.IpV6Spec = ipv6Spec

// network config
config := types.CustomizationAdapterMapping{
Expand Down
110 changes: 104 additions & 6 deletions builtin/providers/vsphere/resource_vsphere_virtual_machine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ func TestAccVSphereVirtualMachine_basic(t *testing.T) {
datastoreOpt = fmt.Sprintf(" datastore = \"%s\"\n", v)
}
template := os.Getenv("VSPHERE_TEMPLATE")
gateway := os.Getenv("VSPHERE_NETWORK_GATEWAY")
gateway := os.Getenv("VSPHERE_IPV4_GATEWAY")
label := os.Getenv("VSPHERE_NETWORK_LABEL")
ip_address := os.Getenv("VSPHERE_NETWORK_IP_ADDRESS")
ip_address := os.Getenv("VSPHERE_IPV4_ADDRESS")

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Expand Down Expand Up @@ -95,9 +95,9 @@ func TestAccVSphereVirtualMachine_diskInitType(t *testing.T) {
datastoreOpt = fmt.Sprintf(" datastore = \"%s\"\n", v)
}
template := os.Getenv("VSPHERE_TEMPLATE")
gateway := os.Getenv("VSPHERE_NETWORK_GATEWAY")
gateway := os.Getenv("VSPHERE_IPV4_GATEWAY")
label := os.Getenv("VSPHERE_NETWORK_LABEL")
ip_address := os.Getenv("VSPHERE_NETWORK_IP_ADDRESS")
ip_address := os.Getenv("VSPHERE_IPV4_ADDRESS")

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Expand Down Expand Up @@ -457,9 +457,9 @@ func TestAccVSphereVirtualMachine_createWithCdrom(t *testing.T) {

func TestAccVSphereVirtualMachine_createWithExistingVmdk(t *testing.T) {
vmdk_path := os.Getenv("VSPHERE_VMDK_PATH")
gateway := os.Getenv("VSPHERE_NETWORK_GATEWAY")
gateway := os.Getenv("VSPHERE_IPV4_GATEWAY")
label := os.Getenv("VSPHERE_NETWORK_LABEL")
ip_address := os.Getenv("VSPHERE_NETWORK_IP_ADDRESS")
ip_address := os.Getenv("VSPHERE_IPV4_ADDRESS")

var vm virtualMachine
var locationOpt string
Expand Down Expand Up @@ -679,6 +679,77 @@ func TestAccVSphereVirtualMachine_updateVcpu(t *testing.T) {
})
}

func TestAccVSphereVirtualMachine_ipv4Andipv6(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")
ipv4Address := os.Getenv("VSPHERE_IPV4_ADDRESS")
ipv4Gateway := os.Getenv("VSPHERE_IPV4_GATEWAY")
ipv6Address := os.Getenv("VSPHERE_IPV6_ADDRESS")
ipv6Gateway := os.Getenv("VSPHERE_IPV6_GATEWAY")

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckVSphereVirtualMachineDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: fmt.Sprintf(
testAccCheckVSphereVirtualMachineConfig_ipv4Andipv6,
locationOpt,
label,
ipv4Address,
ipv4Gateway,
ipv6Address,
ipv6Gateway,
datastoreOpt,
template,
),
Check: resource.ComposeTestCheckFunc(
testAccCheckVSphereVirtualMachineExists("vsphere_virtual_machine.ipv4ipv6", &vm),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.ipv4ipv6", "name", "terraform-test-ipv4-ipv6"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.ipv4ipv6", "vcpu", "2"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.ipv4ipv6", "memory", "4096"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.ipv4ipv6", "disk.#", "2"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.ipv4ipv6", "disk.0.template", template),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.ipv4ipv6", "network_interface.#", "1"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.ipv4ipv6", "network_interface.0.label", label),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.ipv4ipv6", "network_interface.0.ipv4_address", ipv4Address),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.ipv4ipv6", "network_interface.0.ipv4_gateway", ipv4Gateway),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.ipv4ipv6", "network_interface.0.ipv6_address", ipv6Address),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.ipv4ipv6", "network_interface.0.ipv6_gateway", ipv6Gateway),
),
},
},
})
}

func testAccCheckVSphereVirtualMachineDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*govmomi.Client)
finder := find.NewFinder(client.Client, true)
Expand Down Expand Up @@ -1079,3 +1150,30 @@ resource "vsphere_virtual_machine" "bar" {
}
}
`

const testAccCheckVSphereVirtualMachineConfig_ipv4Andipv6 = `
resource "vsphere_virtual_machine" "ipv4ipv6" {
name = "terraform-test-ipv4-ipv6"
%s
vcpu = 2
memory = 4096
network_interface {
label = "%s"
ipv4_address = "%s"
ipv4_prefix_length = 24
ipv4_gateway = "%s"
ipv6_address = "%s"
ipv6_prefix_length = 64
ipv6_gateway = "%s"
}
disk {
%s
template = "%s"
iops = 500
}
disk {
size = 1
iops = 500
}
}
`
6 changes: 4 additions & 2 deletions website/source/docs/providers/vsphere/index.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,10 @@ configuration fields to be set using the documented environment variables.
In addition, the following environment variables are used in tests, and must be
set to valid values for your VMware vSphere environment:

* VSPHERE\_NETWORK\_GATEWAY
* VSPHERE\_NETWORK\_IP\_ADDRESS
* VSPHERE\_IPV4\_GATEWAY
* VSPHERE\_IPV4\_ADDRESS
* VSPHERE\_IPV6\_GATEWAY
* VSPHERE\_IPV6\_ADDRESS
* VSPHERE\_NETWORK\_LABEL
* VSPHERE\_NETWORK\_LABEL\_DHCP
* VSPHERE\_TEMPLATE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ The following arguments are supported:
* `datacenter` - (Optional) The name of a Datacenter in which to launch the virtual machine
* `cluster` - (Optional) Name of a Cluster in which to launch the virtual machine
* `resource_pool` (Optional) The name of a Resource Pool in which to launch the virtual machine. Requires full path (see cluster example).
* `gateway` - (Optional) Gateway IP address to use for all network interfaces
* `gateway` - __Deprecated, please use `network_interface.ipv4_gateway` instead__.
* `domain` - (Optional) A FQDN for the virtual machine; defaults to "vsphere.local"
* `time_zone` - (Optional) The [Linux](https://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/timezone.html) or [Windows](https://msdn.microsoft.com/en-us/library/ms912391.aspx) time zone to set on the virtual machine. Defaults to "Etc/UTC"
* `dns_suffixes` - (Optional) List of name resolution suffixes for the virtual network adapter
Expand All @@ -85,14 +85,18 @@ The following arguments are supported:
The `network_interface` block supports:

* `label` - (Required) Label to assign to this network interface
* `ipv4_address` - (Optional) Static IP to assign to this network interface. Interface will use DHCP if this is left blank. Currently only IPv4 IP addresses are supported.
* `ipv4_prefix_length` - (Optional) prefix length to use when statically assigning an IP.
* `ipv4_address` - (Optional) Static IPv4 to assign to this network interface. Interface will use DHCP if this is left blank.
* `ipv4_prefix_length` - (Optional) prefix length to use when statically assigning an IPv4 address.
* `ipv4_gateway` - (Optional) IPv4 gateway IP address to use.
* `ipv6_address` - (Optional) Static IPv6 to assign to this network interface. Interface will use DHCPv6 if this is left blank.
* `ipv6_prefix_length` - (Optional) prefix length to use when statically assigning an IPv6.
* `ipv6_gateway` - (Optional) IPv6 gateway IP address to use.

The following arguments are maintained for backwards compatibility and may be
removed in a future version:

* `ip_address` - __Deprecated, please use `ipv4_address` instead_.
* `subnet_mask` - __Deprecated, please use `ipv4_prefix_length` instead_.
* `ip_address` - __Deprecated, please use `ipv4_address` instead__.
* `subnet_mask` - __Deprecated, please use `ipv4_prefix_length` instead__.

The `windows_opt_config` block supports:

Expand All @@ -112,7 +116,7 @@ The `disk` block supports:
* `size` - (Required if template and bootable_vmdks_path not provided) Size of this disk (in GB).
* `iops` - (Optional) Number of virtual iops to allocate for this disk.
* `type` - (Optional) 'eager_zeroed' (the default), or 'thin' are supported options.
* `vmdk` - (Required if template and size not provided) Path to a vmdk in a vSphere datastore.
* `vmdk` - (Required if template and size not provided) Path to a vmdk in a vSphere datastore.
* `bootable` - (Optional) Set to 'true' if a vmdk was given and it should attempt to boot after creation.

<a id="cdrom"></a>
Expand Down