-
Notifications
You must be signed in to change notification settings - Fork 486
Add VLAN ID support to vsphere_network data source #2675
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
Changes from 4 commits
889ec5e
4f97630
e69c200
d6d5196
91e4ec5
71e6f56
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,14 +5,18 @@ | |
| package vsphere | ||
|
|
||
| import ( | ||
| "context" | ||
| "errors" | ||
| "fmt" | ||
| "time" | ||
|
|
||
| "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
| "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" | ||
| "github.com/hashicorp/terraform-plugin-testing/helper/resource" | ||
| "github.com/vmware/govmomi/find" | ||
| "github.com/vmware/govmomi/object" | ||
| "github.com/vmware/govmomi/vim25/mo" | ||
| "github.com/vmware/govmomi/vim25/types" | ||
| "github.com/vmware/terraform-provider-vsphere/vsphere/internal/helper/network" | ||
| ) | ||
|
|
||
|
|
@@ -28,9 +32,16 @@ func dataSourceVSphereNetwork() *schema.Resource { | |
|
|
||
| Schema: map[string]*schema.Schema{ | ||
| "name": { | ||
| Type: schema.TypeString, | ||
| Description: "The name or path of the network.", | ||
| Required: true, | ||
| Type: schema.TypeString, | ||
| Description: "The name or path of the network.", | ||
| Optional: true, | ||
| ExactlyOneOf: []string{"name", "vlan_id"}, | ||
| }, | ||
| "vlan_id": { | ||
| Type: schema.TypeInt, | ||
| Optional: true, | ||
| Description: "The vlan id of the network.", | ||
|
akli-ime marked this conversation as resolved.
Outdated
akli-ime marked this conversation as resolved.
Outdated
|
||
| ExactlyOneOf: []string{"name", "vlan_id"}, | ||
| }, | ||
| "datacenter_id": { | ||
| Type: schema.TypeString, | ||
|
|
@@ -88,9 +99,23 @@ func dataSourceVSphereNetwork() *schema.Resource { | |
| } | ||
| } | ||
|
|
||
| type distributedPortGroupStructure struct { | ||
| VLANID int | ||
|
akli-ime marked this conversation as resolved.
|
||
| } | ||
|
|
||
| func expandDistributedPortGroupVlan(d *schema.ResourceData) *distributedPortGroupStructure { | ||
| if v, ok := d.GetOk("vlan_id"); ok { | ||
| return &distributedPortGroupStructure{ | ||
| VLANID: v.(int), | ||
| } | ||
| } | ||
| return nil | ||
| } | ||
|
akli-ime marked this conversation as resolved.
|
||
|
|
||
| func dataSourceVSphereNetworkRead(d *schema.ResourceData, meta interface{}) error { | ||
| client := meta.(*Client).vimClient | ||
|
|
||
| vlan := expandDistributedPortGroupVlan(d) | ||
| name := d.Get("name").(string) | ||
| dvSwitchUUID := d.Get("distributed_virtual_switch_uuid").(string) | ||
| vpcID := d.Get("vpc_id").(string) | ||
|
|
@@ -145,6 +170,59 @@ func dataSourceVSphereNetworkRead(d *schema.ResourceData, meta interface{}) erro | |
| } | ||
| return net, waitForNetworkCompleted, nil | ||
| } | ||
| // Handle VLAN-based lookup (Distributed Virtual Port Groups only) | ||
| if vlan != nil { | ||
| ctx := context.Background() | ||
|
akli-ime marked this conversation as resolved.
|
||
| finder := find.NewFinder(vimClient, false) | ||
| if dc != nil { | ||
| finder.SetDatacenter(dc) | ||
| } | ||
|
|
||
| nets, err := finder.NetworkList(ctx, "*") | ||
| if err != nil { | ||
| return struct{}{}, waitForNetworkError, err | ||
| } | ||
|
|
||
| var matches []object.NetworkReference | ||
|
|
||
| for _, n := range nets { | ||
| dvpg, ok := n.(*object.DistributedVirtualPortgroup) | ||
| if !ok { | ||
| continue | ||
| } | ||
|
Comment on lines
+189
to
+192
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Standard networks can be assigned with a VLAN ID too
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The whole point of this PR is to allow lookup by VLAN ID without requiring a name. Names may not always be known or consistent — for example, a portgroup might have one name in vSphere and a completely different one in Infoblox (IP address management, IPAM). Using only the VLAN ID provides a single identifier that can be used to bind resources across both technologies, so making the name mandatory would defeat the purpose. This provides an alternative for identifying distributed portgroups by VLAN.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My bad, I'm assuming that your infrastructure has a one-to-one mapping of distributed portgroups and VLAN IDs. Let me give some examples how network names are unique
Every host has a "VM Network" created by default. All of these actually point to the same managed object.
These are externally created by NSX. The name is unique in both systems and is synchronized between NSX-T and vCenter.
Every distributed portgroup is a managed object and is stored in the same network folder as its distributed switch. vCenter does not allow more than 1 distributed portgroup to exist under the same name even across different datacenters. What it does allow is an arbitrary number of distributed portgroups to be assigned to the same VLAN
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From our perspective, it is important to give users the flexibility to decide how they want to identify and select their target resources when using the data source. At the moment, the provider already offers mechanisms to help narrow down the selection, including support for wildcard (*) name patterns. When multiple networks match, a clear error message is returned to prompt the user to refine their criteria. Here is a concrete example I just tested with Terraform, using a wildcard name pattern: Returned error: This approach helps avoid ambiguity and encourages users to be explicit about their intent, which is especially important in environments where multiple networks may share similar characteristics. In this context, leaving the responsibility of precise resource identification to the user provides greater flexibility and aligns well with the variety of infrastructure designs found in real-world deployments. |
||
|
|
||
| var pg mo.DistributedVirtualPortgroup | ||
| if err := dvpg.Properties(ctx, dvpg.Reference(), []string{"config.defaultPortConfig"}, &pg); err != nil { | ||
| return struct{}{}, waitForNetworkError, err | ||
| } | ||
|
akli-ime marked this conversation as resolved.
|
||
|
|
||
| cfg, ok := pg.Config.DefaultPortConfig.(*types.VMwareDVSPortSetting) | ||
| if !ok || cfg.Vlan == nil { | ||
| continue | ||
| } | ||
|
|
||
| vlanSpec, ok := cfg.Vlan.(*types.VmwareDistributedVirtualSwitchVlanIdSpec) | ||
| if !ok { | ||
| continue | ||
| } | ||
|
|
||
| if int(vlanSpec.VlanId) == vlan.VLANID { | ||
| matches = append(matches, dvpg) | ||
| } | ||
| } | ||
|
|
||
| if len(matches) == 0 { | ||
| return struct{}{}, waitForNetworkPending, nil | ||
| } | ||
|
|
||
| if len(matches) > 1 { | ||
| return struct{}{}, waitForNetworkError, | ||
| fmt.Errorf("multiple distributed port groups found with vlan_id %d", vlan.VLANID) | ||
| } | ||
|
|
||
| return matches[0], waitForNetworkCompleted, nil | ||
| } | ||
|
akli-ime marked this conversation as resolved.
|
||
|
|
||
| // Handle standard switch port group | ||
| net, err = network.FromName(vimClient, name, dc, filters) // Pass the *vim25.Client | ||
| if err != nil { | ||
|
|
@@ -183,7 +261,11 @@ func dataSourceVSphereNetworkRead(d *schema.ResourceData, meta interface{}) erro | |
| } | ||
|
|
||
| if state == waitForNetworkPending { | ||
| err = fmt.Errorf("network %s not found", name) | ||
| if vlan != nil { | ||
| err = fmt.Errorf("network with vlan_id %d not found", vlan.VLANID) | ||
| } else { | ||
| err = fmt.Errorf("network %s not found", name) | ||
| } | ||
| } | ||
|
|
||
| if err != nil { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.