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

feat: vmware_distributed_virtual_switch_pvlan_mapping resource #2291

Merged
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
31 changes: 31 additions & 0 deletions vsphere/distributed_virtual_switch_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,34 @@ func enableDVSNetworkResourceManagement(client *govmomi.Client, dvs *object.Vmwa

return nil
}

func updateDVSPvlanMappings(dvs *object.VmwareDistributedVirtualSwitch, pvlanConfig []types.VMwareDVSPvlanConfigSpec) error {
// Load current properties, required to get the 'config version' to provide back when updating
props, err := dvsProperties(dvs)
if err != nil {
return fmt.Errorf("cannot read properties of distributed_virtual_switch: %s", err)
}
dvsConfig := props.Config.(*types.VMwareDVSConfigInfo)

updateSpec := types.VMwareDVSConfigSpec{
DVSConfigSpec: types.DVSConfigSpec{
ConfigVersion: dvsConfig.ConfigVersion,
},
PvlanConfigSpec: pvlanConfig,
}

// Start ReconfigureDvs_Task
ctx, cancel := context.WithTimeout(context.Background(), defaultAPITimeout)
defer cancel()
task, err := dvs.Reconfigure(ctx, &updateSpec)
if err != nil {
return fmt.Errorf("error reconfiguring DVS: %s", err)
}

// Wait for ReconfigureDvs_Task to finish
tctx, tcancel := context.WithTimeout(context.Background(), defaultAPITimeout)
defer tcancel()
task.Wait(tctx)

return nil
}
2 changes: 1 addition & 1 deletion vsphere/distributed_virtual_switch_structure.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ func schemaVMwareDVSConfigSpec() map[string]*schema.Schema {
"ignore_other_pvlan_mappings": {
Type: schema.TypeBool,
Optional: true,
Description: "Whether to ignore existing PVLAN mappings not managed by this resource. Defaults to false.",
Description: "Whether to ignore existing PVLAN mappings not managed by this resource.",
Default: false,
},

Expand Down
87 changes: 44 additions & 43 deletions vsphere/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,49 +102,50 @@ func Provider() *schema.Provider {
},

ResourcesMap: map[string]*schema.Resource{
"vsphere_compute_cluster": resourceVSphereComputeCluster(),
"vsphere_compute_cluster_host_group": resourceVSphereComputeClusterHostGroup(),
"vsphere_compute_cluster_vm_affinity_rule": resourceVSphereComputeClusterVMAffinityRule(),
"vsphere_compute_cluster_vm_anti_affinity_rule": resourceVSphereComputeClusterVMAntiAffinityRule(),
"vsphere_compute_cluster_vm_dependency_rule": resourceVSphereComputeClusterVMDependencyRule(),
"vsphere_compute_cluster_vm_group": resourceVSphereComputeClusterVMGroup(),
"vsphere_compute_cluster_vm_host_rule": resourceVSphereComputeClusterVMHostRule(),
"vsphere_content_library": resourceVSphereContentLibrary(),
"vsphere_content_library_item": resourceVSphereContentLibraryItem(),
"vsphere_custom_attribute": resourceVSphereCustomAttribute(),
"vsphere_datacenter": resourceVSphereDatacenter(),
"vsphere_datastore_cluster": resourceVSphereDatastoreCluster(),
"vsphere_datastore_cluster_vm_anti_affinity_rule": resourceVSphereDatastoreClusterVMAntiAffinityRule(),
"vsphere_distributed_port_group": resourceVSphereDistributedPortGroup(),
"vsphere_distributed_virtual_switch": resourceVSphereDistributedVirtualSwitch(),
"vsphere_drs_vm_override": resourceVSphereDRSVMOverride(),
"vsphere_dpm_host_override": resourceVSphereDPMHostOverride(),
"vsphere_file": resourceVSphereFile(),
"vsphere_folder": resourceVSphereFolder(),
"vsphere_ha_vm_override": resourceVSphereHAVMOverride(),
"vsphere_host_port_group": resourceVSphereHostPortGroup(),
"vsphere_host_virtual_switch": resourceVSphereHostVirtualSwitch(),
"vsphere_license": resourceVSphereLicense(),
"vsphere_offline_software_depot": resourceVsphereOfflineSoftwareDepot(),
"vsphere_resource_pool": resourceVSphereResourcePool(),
"vsphere_tag": resourceVSphereTag(),
"vsphere_tag_category": resourceVSphereTagCategory(),
"vsphere_virtual_disk": resourceVSphereVirtualDisk(),
"vsphere_virtual_machine": resourceVSphereVirtualMachine(),
"vsphere_virtual_machine_class": resourceVsphereVmClass(),
"vsphere_virtual_machine_snapshot": resourceVSphereVirtualMachineSnapshot(),
"vsphere_nas_datastore": resourceVSphereNasDatastore(),
"vsphere_storage_drs_vm_override": resourceVSphereStorageDrsVMOverride(),
"vsphere_supervisor": resourceVsphereSupervisor(),
"vsphere_vapp_container": resourceVSphereVAppContainer(),
"vsphere_vapp_entity": resourceVSphereVAppEntity(),
"vsphere_vmfs_datastore": resourceVSphereVmfsDatastore(),
"vsphere_host": resourceVsphereHost(),
"vsphere_vnic": resourceVsphereNic(),
"vsphere_vm_storage_policy": resourceVMStoragePolicy(),
"vsphere_role": resourceVsphereRole(),
"vsphere_entity_permissions": resourceVsphereEntityPermissions(),
"vsphere_guest_os_customization": resourceVSphereGuestOsCustomization(),
"vsphere_compute_cluster": resourceVSphereComputeCluster(),
"vsphere_compute_cluster_host_group": resourceVSphereComputeClusterHostGroup(),
"vsphere_compute_cluster_vm_affinity_rule": resourceVSphereComputeClusterVMAffinityRule(),
"vsphere_compute_cluster_vm_anti_affinity_rule": resourceVSphereComputeClusterVMAntiAffinityRule(),
"vsphere_compute_cluster_vm_dependency_rule": resourceVSphereComputeClusterVMDependencyRule(),
"vsphere_compute_cluster_vm_group": resourceVSphereComputeClusterVMGroup(),
"vsphere_compute_cluster_vm_host_rule": resourceVSphereComputeClusterVMHostRule(),
"vsphere_content_library": resourceVSphereContentLibrary(),
"vsphere_content_library_item": resourceVSphereContentLibraryItem(),
"vsphere_custom_attribute": resourceVSphereCustomAttribute(),
"vsphere_datacenter": resourceVSphereDatacenter(),
"vsphere_datastore_cluster": resourceVSphereDatastoreCluster(),
"vsphere_datastore_cluster_vm_anti_affinity_rule": resourceVSphereDatastoreClusterVMAntiAffinityRule(),
"vsphere_distributed_port_group": resourceVSphereDistributedPortGroup(),
"vsphere_distributed_virtual_switch": resourceVSphereDistributedVirtualSwitch(),
"vsphere_distributed_virtual_switch_pvlan_mapping": resourceVSphereDistributedVirtualSwitchPvlanMapping(),
"vsphere_drs_vm_override": resourceVSphereDRSVMOverride(),
"vsphere_dpm_host_override": resourceVSphereDPMHostOverride(),
"vsphere_file": resourceVSphereFile(),
"vsphere_folder": resourceVSphereFolder(),
"vsphere_ha_vm_override": resourceVSphereHAVMOverride(),
"vsphere_host_port_group": resourceVSphereHostPortGroup(),
"vsphere_host_virtual_switch": resourceVSphereHostVirtualSwitch(),
"vsphere_license": resourceVSphereLicense(),
"vsphere_offline_software_depot": resourceVsphereOfflineSoftwareDepot(),
"vsphere_resource_pool": resourceVSphereResourcePool(),
"vsphere_tag": resourceVSphereTag(),
"vsphere_tag_category": resourceVSphereTagCategory(),
"vsphere_virtual_disk": resourceVSphereVirtualDisk(),
"vsphere_virtual_machine": resourceVSphereVirtualMachine(),
"vsphere_virtual_machine_class": resourceVsphereVmClass(),
"vsphere_virtual_machine_snapshot": resourceVSphereVirtualMachineSnapshot(),
"vsphere_nas_datastore": resourceVSphereNasDatastore(),
"vsphere_storage_drs_vm_override": resourceVSphereStorageDrsVMOverride(),
"vsphere_supervisor": resourceVsphereSupervisor(),
"vsphere_vapp_container": resourceVSphereVAppContainer(),
"vsphere_vapp_entity": resourceVSphereVAppEntity(),
"vsphere_vmfs_datastore": resourceVSphereVmfsDatastore(),
"vsphere_host": resourceVsphereHost(),
"vsphere_vnic": resourceVsphereNic(),
"vsphere_vm_storage_policy": resourceVMStoragePolicy(),
"vsphere_role": resourceVsphereRole(),
"vsphere_entity_permissions": resourceVsphereEntityPermissions(),
"vsphere_guest_os_customization": resourceVSphereGuestOsCustomization(),
},

DataSourcesMap: map[string]*schema.Resource{
Expand Down
5 changes: 5 additions & 0 deletions vsphere/resource_vsphere_distributed_virtual_switch.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ func resourceVSphereDistributedVirtualSwitchRead(d *schema.ResourceData, meta in
if err := viapi.ValidateVirtualCenter(client); err != nil {
return err
}

id := d.Id()
dvs, err := dvsFromUUID(client, id)
if err != nil {
Expand Down Expand Up @@ -195,6 +196,8 @@ func resourceVSphereDistributedVirtualSwitchUpdate(d *schema.ResourceData, meta
return err
}

vsphereDistributedVirtualSwitchModificationMutex.Lock()

id := d.Id()
dvs, err := dvsFromUUID(client, id)
if err != nil {
Expand Down Expand Up @@ -256,6 +259,8 @@ func resourceVSphereDistributedVirtualSwitchUpdate(d *schema.ResourceData, meta
}
}

vsphereDistributedVirtualSwitchModificationMutex.Unlock()

return resourceVSphereDistributedVirtualSwitchRead(d, meta)
}

Expand Down
142 changes: 142 additions & 0 deletions vsphere/resource_vsphere_distributed_virtual_switch_pvlan_mapping.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package vsphere

import (
"fmt"
"sync"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/hashicorp/terraform-provider-vsphere/vsphere/internal/helper/viapi"
"github.com/vmware/govmomi/vim25/types"
)

var vsphereDistributedVirtualSwitchModificationMutex sync.Mutex

func resourceVSphereDistributedVirtualSwitchPvlanMapping() *schema.Resource {
s := map[string]*schema.Schema{
"distributed_virtual_switch_id": {
Type: schema.TypeString,
Description: "The ID of the distributed virtual switch to attach this mapping to.",
Required: true,
ForceNew: true,
},
"primary_vlan_id": {
Type: schema.TypeInt,
Required: true,
Description: "The primary VLAN ID. The VLAN IDs of 0 and 4095 are reserved and cannot be used in this property.",
ValidateFunc: validation.IntBetween(1, 4094),
ForceNew: true,
},
"secondary_vlan_id": {
Type: schema.TypeInt,
Required: true,
Description: "The secondary VLAN ID. The VLAN IDs of 0 and 4095 are reserved and cannot be used in this property.",
ValidateFunc: validation.IntBetween(1, 4094),
ForceNew: true,
},
"pvlan_type": {
Type: schema.TypeString,
Required: true,
Description: "The private VLAN type. Valid values are promiscuous, community and isolated.",
ValidateFunc: validation.StringInSlice(privateVLANTypeAllowedValues, false),
ForceNew: true,
},
}

return &schema.Resource{
Create: resourceVSphereDistributedVirtualSwitchPvlanMappingCreate,
Read: resourceVSphereDistributedVirtualSwitchPvlanMappingRead,
Delete: resourceVSphereDistributedVirtualSwitchPvlanMappingDelete,
Schema: s,
}
}

func resourceVSphereDistributedVirtualSwitchPvlanMappingOperation(operation types.ConfigSpecOperation, d *schema.ResourceData, meta interface{}) error {
client := meta.(*Client).vimClient
if err := viapi.ValidateVirtualCenter(client); err != nil {
return err
}

// vSphere only allows one modification operation at a time, so lock locally
// to avoid errors if multiple pvlan mappings are created at once.
vsphereDistributedVirtualSwitchModificationMutex.Lock()

// Fetch the target dvswitch
dvs, err := dvsFromUUID(client, d.Get("distributed_virtual_switch_id").(string))
if err != nil {
return fmt.Errorf("cannot locate distributed_virtual_switch: %s", err)
}

// Perform operation
entry := types.VMwareDVSPvlanMapEntry{
PrimaryVlanId: int32(d.Get("primary_vlan_id").(int)),
SecondaryVlanId: int32(d.Get("secondary_vlan_id").(int)),
PvlanType: d.Get("pvlan_type").(string),
}

pvlanConfig := []types.VMwareDVSPvlanConfigSpec{
{
Operation: string(operation),
PvlanEntry: entry,
},
}

err = updateDVSPvlanMappings(dvs, pvlanConfig)
if err != nil {
return fmt.Errorf("cannot reconfigure distributed virtual switch: %s", err)
}

vsphereDistributedVirtualSwitchModificationMutex.Unlock()
return nil
}

func resourceVSphereDistributedVirtualSwitchPvlanMappingCreate(d *schema.ResourceData, meta interface{}) error {
err := resourceVSphereDistributedVirtualSwitchPvlanMappingOperation(types.ConfigSpecOperationAdd, d, meta)
if err != nil {
return err
}

// Try to read the mapping back, which will also generate the ID.
return resourceVSphereDistributedVirtualSwitchPvlanMappingRead(d, meta)
}

func resourceVSphereDistributedVirtualSwitchPvlanMappingDelete(d *schema.ResourceData, meta interface{}) error {
return resourceVSphereDistributedVirtualSwitchPvlanMappingOperation(types.ConfigSpecOperationRemove, d, meta)
}

func resourceVSphereDistributedVirtualSwitchPvlanMappingRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Client).vimClient
if err := viapi.ValidateVirtualCenter(client); err != nil {
return err
}

// Ensure the DVSwitch still exists
dvs, err := dvsFromUUID(client, d.Get("distributed_virtual_switch_id").(string))
if err != nil {
return fmt.Errorf("cannot locate distributed_virtual_switch: %s", err)
}

// Get its properties
props, err := dvsProperties(dvs)
if err != nil {
return fmt.Errorf("cannot read properties of distributed_virtual_switch: %s", err)
}
d.Set("distributed_virtual_switch_id", props.Uuid)

// Loop through the existing mappings on the switch to try and find one matching our spec
for _, mapping := range props.Config.(*types.VMwareDVSConfigInfo).PvlanConfig {
// Check if the existing mapping matches the one specified by the resource
if mapping.PrimaryVlanId == int32(d.Get("primary_vlan_id").(int)) && mapping.SecondaryVlanId == int32(d.Get("secondary_vlan_id").(int)) && mapping.PvlanType == d.Get("pvlan_type").(string) {
d.SetId(fmt.Sprintf("dvswitch-%s-mapping-%d-%d-%s", props.Config.(*types.VMwareDVSConfigInfo).Uuid, mapping.PrimaryVlanId, mapping.SecondaryVlanId, mapping.PvlanType))
return nil
}
}

// If we don't find a mapping on the switch matching the current spec, then tell Terraform
// that the resource no longer exists
d.SetId("")
return nil
}
Loading