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

New resource: vsphere_host_virtual_switch #138

Merged
merged 6 commits into from
Aug 31, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
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,

Choose a reason for hiding this comment

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

would it make sense for this to be defaulted to a sensible value?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hey @tombuildsstuff - these are defaulted in resource_host_virtual_switch.go. They need to be non-defaulted here so that the schema can be shared with vsphere_host_port_group for inheritance purposes.

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.",

Choose a reason for hiding this comment

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

would it make sense for this to be defaulted to a sensible value?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As above

},

// 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.",
},

Choose a reason for hiding this comment

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

minor I think this can be removed, since it's inferred? 🤔

}
}

// 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])

Choose a reason for hiding this comment

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

can we make this more readable via:

failback := v.(bool)
obj.RollingOrder = &failback

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

Choose a reason for hiding this comment

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

can we make this more readable via:

notifySwitches := v.(bool)
obj.NotifySwitches = &notifySwitches

}
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])

Choose a reason for hiding this comment

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

(same here)


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)

Choose a reason for hiding this comment

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

minor can we make this more readable by splitting this out?

}
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])

Choose a reason for hiding this comment

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

(same here)

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

Choose a reason for hiding this comment

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

(same here)

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

Choose a reason for hiding this comment

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

(same here)

}
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 {

Choose a reason for hiding this comment

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

should this be in a child-object?

Copy link
Contributor Author

@vancluever vancluever Aug 24, 2017

Choose a reason for hiding this comment

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

Hey @tombuildsstuff, I have been trying to avoid unnecessary nesting whenever possible. I have lost too many dev hours on trying to work around the issues of dealing with nested config stuff in TF - the inheritance model of this resource in conjunction with vsphere_host_port_group also makes things even more complicated. So going forward I am only reserving using lists and sets for things that need it because they need to deal with multiple values and that's it. A virtual switch or port group can only have one policy, of which traffic shaping options are a part of, so this does not need to be nested here.

FWIW the entire resource was originally a nested structure - but certain issues dealing with inheritance and more importantly default values for entire nested structures ultimately led me to throwing out that approach and just going with a (mostly) shared flat schema.

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