Skip to content

Commit

Permalink
New resource: vsphere_host_virtual_switch
Browse files Browse the repository at this point in the history
This commit adds a new resource, vsphere_host_virtual_switch, which can
be used to manage a virtual switch on an ESXi host.
  • Loading branch information
vancluever committed Aug 31, 2017
1 parent 181b638 commit edb0836
Show file tree
Hide file tree
Showing 12 changed files with 1,204 additions and 6 deletions.
3 changes: 3 additions & 0 deletions tf-vsphere-devrc.mk.example
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,8 @@ export VSPHERE_INIT_TYPE ?= thin # vDisk type
export VSPHERE_ADAPTER_TYPE ?= lsiLogic # Virtual disk adapter type
export VSPHERE_LICENSE ?= key # License resource test key
export VSPHERE_DC_FOLDER ?= dc-folder # DC resource test folder
export VSPHERE_ESXI_HOST ?= esxi1 # ESXi host to work with
export VSPHERE_HOST_NIC0 ?= vmnic0 # NIC0 for host net tests
export VSPHERE_HOST_NIC1 ?= vmnic1 # NIC1 for host net tests

# vi: filetype=make
38 changes: 38 additions & 0 deletions vsphere/helper_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,48 @@
package vsphere

import (
"fmt"
"os"
"testing"
"time"

"github.com/hashicorp/terraform/terraform"
"github.com/vmware/govmomi"
)

// testCheckVariables bundles common variables needed by various test checkers.
type testCheckVariables struct {
// A client for various operations.
client *govmomi.Client

// The subject resource's ID.
resourceID string

// The ESXi host that a various API call is directed at.
esxiHost string

// The datacenter that a various API call is directed at.
datacenter string

// A timeout to pass to various context creation calls.
timeout time.Duration
}

func testClientVariablesForResource(s *terraform.State, addr string) (testCheckVariables, error) {
rs, ok := s.RootModule().Resources[addr]
if !ok {
return testCheckVariables{}, fmt.Errorf("%s not found in state", addr)
}

return testCheckVariables{
client: testAccProvider.Meta().(*govmomi.Client),
resourceID: rs.Primary.ID,
esxiHost: os.Getenv("VSPHERE_ESXI_HOST"),
datacenter: os.Getenv("VSPHERE_DATACENTER"),
timeout: time.Minute * 5,
}, nil
}

// testAccSkipIfNotEsxi skips a test if VSPHERE_TEST_ESXI is not set.
func testAccSkipIfNotEsxi(t *testing.T) {
if os.Getenv("VSPHERE_TEST_ESXI") == "" {
Expand Down
286 changes: 286 additions & 0 deletions vsphere/host_network_policy_structure.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,286 @@
package vsphere

import (
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
"github.com/vmware/govmomi/vim25/types"
)

const (
hostNetworkPolicyNicTeamingPolicyModeLoadbalanceIP = "loadbalance_ip"
hostNetworkPolicyNicTeamingPolicyModeLoadbalanceSrcMac = "loadbalance_srcmac"
hostNetworkPolicyNicTeamingPolicyModeLoadbalanceSrcID = "loadbalance_srcid"
hostNetworkPolicyNicTeamingPolicyModeFailoverExplicit = "failover_explicit"
)

var hostNetworkPolicyNicTeamingPolicyAllowedValues = []string{
hostNetworkPolicyNicTeamingPolicyModeLoadbalanceIP,
hostNetworkPolicyNicTeamingPolicyModeLoadbalanceSrcMac,
hostNetworkPolicyNicTeamingPolicyModeLoadbalanceSrcID,
hostNetworkPolicyNicTeamingPolicyModeFailoverExplicit,
}

// schemaHostNetworkPolicy returns schema items for resources that need to work
// with a HostNetworkPolicy, such as virtual switches and port groups.
func schemaHostNetworkPolicy() map[string]*schema.Schema {
return map[string]*schema.Schema{
// HostNicTeamingPolicy/HostNicFailureCriteria
"check_beacon": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Description: "Enable beacon probing. Requires that the vSwitch has been configured to use a beacon. If disabled, link status is used only.",
},

// HostNicTeamingPolicy/HostNicOrderPolicy
"active_nics": &schema.Schema{
Type: schema.TypeList,
Description: "List of active network adapters used for load balancing.",
Elem: &schema.Schema{Type: schema.TypeString},
},
"standby_nics": &schema.Schema{
Type: schema.TypeList,
Description: "List of standby network adapters used for failover.",
Elem: &schema.Schema{Type: schema.TypeString},
},

// HostNicTeamingPolicy
"teaming_policy": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Description: "The network adapter teaming policy. Can be one of loadbalance_ip, loadbalance_srcmac, loadbalance_srcid, or failover_explicit.",
ValidateFunc: validation.StringInSlice(hostNetworkPolicyNicTeamingPolicyAllowedValues, false),
},
"notify_switches": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Description: "If true, the teaming policy will notify the broadcast network of a NIC failover, triggering cache updates.",
},
"failback": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Description: "If true, the teaming policy will re-activate failed interfaces higher in precedence when they come back up.",
},

// HostNetworkSecurityPolicy
"allow_promiscuous": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Description: "Enable promiscuous mode on the network. This flag indicates whether or not all traffic is seen on a given port.",
},
"allow_forged_transmits": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Description: "Controls whether or not the virtual network adapter is allowed to send network traffic with a different MAC address than that of its own.",
},
"allow_mac_changes": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Description: "Controls whether or not the Media Access Control (MAC) address can be changed.",
},

// HostNetworkTrafficShapingPolicy
"shaping_average_bandwidth": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Description: "The average bandwidth in bits per second if shaping is enabled on the port.",
},
"shaping_burst_size": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Description: "The maximum burst size allowed in bytes if shaping is enabled on the port.",
},
"shaping_enabled": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Description: "True if the traffic shaper is enabled on the port.",
},
"shaping_peak_bandwidth": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Description: "The peak bandwidth during bursts in bits per second if traffic shaping is enabled on the port.",
},
}
}

// expandHostNicFailureCriteria reads certain ResourceData keys and returns a
// HostNicFailureCriteria.
func expandHostNicFailureCriteria(d *schema.ResourceData) *types.HostNicFailureCriteria {
obj := &types.HostNicFailureCriteria{}

if v, ok := d.GetOkExists("check_beacon"); ok {
obj.CheckBeacon = &([]bool{v.(bool)}[0])
}

// These fields are deprecated and are set only to make things work. They are
// not exposed to Terraform.
obj.CheckSpeed = "minimum"
obj.Speed = 10
obj.CheckDuplex = &([]bool{false}[0])
obj.FullDuplex = &([]bool{false}[0])
obj.CheckErrorPercent = &([]bool{false}[0])
obj.Percentage = 0

return obj
}

// flattenHostNicFailureCriteria reads various fields from a
// HostNicFailureCriteria into the passed in ResourceData.
func flattenHostNicFailureCriteria(d *schema.ResourceData, obj *types.HostNicFailureCriteria) error {
if obj.CheckBeacon != nil {
d.Set("check_beacon", obj.CheckBeacon)
}
return nil
}

// expandHostNicOrderPolicy reads certain ResourceData keys and returns a
// HostNicOrderPolicy.
func expandHostNicOrderPolicy(d *schema.ResourceData) *types.HostNicOrderPolicy {
obj := &types.HostNicOrderPolicy{}
activeNics, activeOk := d.GetOkExists("active_nics")
standbyNics, standbyOk := d.GetOkExists("standby_nics")
if !activeOk && !standbyOk {
return nil
}
obj.ActiveNic = sliceInterfacesToStrings(activeNics.([]interface{}))
obj.StandbyNic = sliceInterfacesToStrings(standbyNics.([]interface{}))
return obj
}

// flattenHostNicOrderPolicy reads various fields from a HostNicOrderPolicy
// into the passed in ResourceData.
func flattenHostNicOrderPolicy(d *schema.ResourceData, obj *types.HostNicOrderPolicy) error {
if obj == nil {
return nil
}
if err := d.Set("active_nics", sliceStringsToInterfaces(obj.ActiveNic)); err != nil {
return err
}
if err := d.Set("standby_nics", sliceStringsToInterfaces(obj.StandbyNic)); err != nil {
return err
}
return nil
}

// expandHostNicTeamingPolicy reads certain ResourceData keys and returns a
// HostNicTeamingPolicy.
func expandHostNicTeamingPolicy(d *schema.ResourceData) *types.HostNicTeamingPolicy {
obj := &types.HostNicTeamingPolicy{
Policy: d.Get("teaming_policy").(string),
}
if v, ok := d.GetOkExists("failback"); ok {
obj.RollingOrder = &([]bool{!v.(bool)}[0])
}
if v, ok := d.GetOkExists("notify_switches"); ok {
obj.NotifySwitches = &([]bool{v.(bool)}[0])
}
obj.FailureCriteria = expandHostNicFailureCriteria(d)
obj.NicOrder = expandHostNicOrderPolicy(d)

// These fields are deprecated and are set only to make things work. They are
// not exposed to Terraform.
obj.ReversePolicy = &([]bool{true}[0])

return obj
}

// flattenHostNicTeamingPolicy reads various fields from a HostNicTeamingPolicy
// into the passed in ResourceData.
func flattenHostNicTeamingPolicy(d *schema.ResourceData, obj *types.HostNicTeamingPolicy) error {
if obj.RollingOrder != nil {
d.Set("failback", !*obj.RollingOrder)
}
if obj.NotifySwitches != nil {
d.Set("notify_switches", obj.NotifySwitches)
}
d.Set("teaming_policy", obj.Policy)
if err := flattenHostNicFailureCriteria(d, obj.FailureCriteria); err != nil {
return err
}
if err := flattenHostNicOrderPolicy(d, obj.NicOrder); err != nil {
return err
}
return nil
}

// expandHostNetworkSecurityPolicy reads certain ResourceData keys and returns
// a HostNetworkSecurityPolicy.
func expandHostNetworkSecurityPolicy(d *schema.ResourceData) *types.HostNetworkSecurityPolicy {
obj := &types.HostNetworkSecurityPolicy{}
if v, ok := d.GetOkExists("allow_promiscuous"); ok {
obj.AllowPromiscuous = &([]bool{v.(bool)}[0])
}
if v, ok := d.GetOkExists("allow_forged_transmits"); ok {
obj.ForgedTransmits = &([]bool{v.(bool)}[0])
}
if v, ok := d.GetOkExists("allow_mac_changes"); ok {
obj.MacChanges = &([]bool{v.(bool)}[0])
}
return obj
}

// flattenHostNetworkSecurityPolicy reads various fields from a
// HostNetworkSecurityPolicy into the passed in ResourceData.
func flattenHostNetworkSecurityPolicy(d *schema.ResourceData, obj *types.HostNetworkSecurityPolicy) error {
if obj.AllowPromiscuous != nil {
d.Set("allow_promiscuous", *obj.AllowPromiscuous)
}
if obj.ForgedTransmits != nil {
d.Set("allow_forged_transmits", *obj.ForgedTransmits)
}
if obj.MacChanges != nil {
d.Set("allow_mac_changes", *obj.MacChanges)
}
return nil
}

// expandHostNetworkTrafficShapingPolicy reads certain ResourceData keys and
// returns a HostNetworkTrafficShapingPolicy.
func expandHostNetworkTrafficShapingPolicy(d *schema.ResourceData) *types.HostNetworkTrafficShapingPolicy {
obj := &types.HostNetworkTrafficShapingPolicy{
AverageBandwidth: int64(d.Get("shaping_average_bandwidth").(int)),
BurstSize: int64(d.Get("shaping_burst_size").(int)),
PeakBandwidth: int64(d.Get("shaping_peak_bandwidth").(int)),
}
if v, ok := d.GetOkExists("shaping_enabled"); ok {
obj.Enabled = &([]bool{v.(bool)}[0])
}
return obj
}

// flattenHostNetworkTrafficShapingPolicy reads various fields from a
// HostNetworkTrafficShapingPolicy into the passed in ResourceData.
func flattenHostNetworkTrafficShapingPolicy(d *schema.ResourceData, obj *types.HostNetworkTrafficShapingPolicy) error {
if obj.Enabled != nil {
d.Set("shaping_enabled", *obj.Enabled)
}
d.Set("shaping_average_bandwidth", obj.AverageBandwidth)
d.Set("shaping_burst_size", obj.BurstSize)
d.Set("shaping_peak_bandwidth", obj.PeakBandwidth)
return nil
}

// expandHostNetworkPolicy reads certain ResourceData keys and returns a
// HostNetworkPolicy.
func expandHostNetworkPolicy(d *schema.ResourceData) *types.HostNetworkPolicy {
obj := &types.HostNetworkPolicy{
Security: expandHostNetworkSecurityPolicy(d),
NicTeaming: expandHostNicTeamingPolicy(d),
ShapingPolicy: expandHostNetworkTrafficShapingPolicy(d),
}
return obj
}

// flattenHostNetworkPolicy reads various fields from a HostNetworkPolicy into
// the passed in ResourceData.
func flattenHostNetworkPolicy(d *schema.ResourceData, obj *types.HostNetworkPolicy) error {
if err := flattenHostNetworkSecurityPolicy(d, obj.Security); err != nil {
return err
}
if err := flattenHostNicTeamingPolicy(d, obj.NicTeaming); err != nil {
return err
}
if err := flattenHostNetworkTrafficShapingPolicy(d, obj.ShapingPolicy); err != nil {
return err
}
return nil
}
49 changes: 49 additions & 0 deletions vsphere/host_network_system_helper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package vsphere

import (
"context"
"fmt"

"github.com/vmware/govmomi"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)

// hostNetworkSystemFromHostSystem locates a HostNetworkSystem from a specified
// HostSystem.
func hostNetworkSystemFromHostSystem(hs *object.HostSystem) (*object.HostNetworkSystem, error) {
ctx, cancel := context.WithTimeout(context.Background(), defaultAPITimeout)
defer cancel()
return hs.ConfigManager().NetworkSystem(ctx)
}

// hostNetworkSystemFromHostSystemID locates a HostNetworkSystem from a
// specified HostSystem managed object ID.
func hostNetworkSystemFromHostSystemID(client *govmomi.Client, hsID string) (*object.HostNetworkSystem, error) {
hs, err := hostSystemFromID(client, hsID)
if err != nil {
return nil, err
}
return hostNetworkSystemFromHostSystem(hs)
}

// hostVSwitchFromName locates a virtual switch on the supplied HostSystem by
// name.
func hostVSwitchFromName(client *govmomi.Client, ns *object.HostNetworkSystem, name string) (*types.HostVirtualSwitch, error) {
var mns mo.HostNetworkSystem
pc := client.PropertyCollector()
ctx, cancel := context.WithTimeout(context.Background(), defaultAPITimeout)
defer cancel()
if err := pc.RetrieveOne(ctx, ns.Reference(), []string{"networkInfo.vswitch"}, &mns); err != nil {
return nil, fmt.Errorf("error fetching host network properties: %s", err)
}

for _, sw := range mns.NetworkInfo.Vswitch {
if sw.Name == name {
return &sw, nil
}
}

return nil, fmt.Errorf("could not find virtual switch %s", name)
}
Loading

0 comments on commit edb0836

Please sign in to comment.