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: add vmk services support #1855

Merged
merged 2 commits into from
Apr 26, 2023
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
115 changes: 114 additions & 1 deletion vsphere/resource_vsphere_vnic.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,27 @@ import (
"strings"

"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/hostsystem"
"github.com/hashicorp/terraform-provider-vsphere/vsphere/internal/helper/structure"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)

const (
vnicServiceTypeVsan = "vsan"
vnicServiceTypeVmotion = "vmotion"
vnicServiceTypeManagement = "management"
)

var vnicServiceTypeAllowedValues = []string{
vnicServiceTypeVsan,
vnicServiceTypeVmotion,
vnicServiceTypeManagement,
}

func resourceVsphereNic() *schema.Resource {
return &schema.Resource{
Create: resourceVsphereNicCreate,
Expand Down Expand Up @@ -118,6 +132,35 @@ func resourceVsphereNicRead(d *schema.ResourceData, meta interface{}) error {
}
}
}

// get enabled services
hostSystem, err := hostsystem.FromID(client, hostID)
if err != nil {
return err
}

hostVnicMgr, err := hostSystem.ConfigManager().VirtualNicManager(ctx)
if err != nil {
return nil
}

hostVnicMgrInfo, err := hostVnicMgr.Info(ctx)
if err != nil {
return nil
}

var services []string
for _, netConfig := range hostVnicMgrInfo.NetConfig {
for _, vnic := range netConfig.SelectedVnic {
if isNicIdContained := strings.Contains(vnic, nicID); isNicIdContained {
services = append(services, netConfig.NicType)
}
}
}
if err := d.Set("services", schema.NewSet(schema.HashString, structure.SliceStringsToInterfaces(services))); err != nil {
return err
}

return nil
}

Expand All @@ -136,7 +179,7 @@ func resourceVsphereNicCreate(d *schema.ResourceData, meta interface{}) error {
func resourceVsphereNicUpdate(d *schema.ResourceData, meta interface{}) error {
for _, k := range []string{
"portgroup", "distributed_switch_port", "distributed_port_group",
"mac", "mtu", "ipv4", "ipv6", "netstack"} {
"mac", "mtu", "ipv4", "ipv6", "netstack", "services"} {
if d.HasChange(k) {
_, err := updateVNic(d, meta)
if err != nil {
Expand Down Expand Up @@ -271,11 +314,25 @@ func BaseVMKernelSchema() map[string]*schema.Schema {
Default: "defaultTcpipStack",
ForceNew: true,
},
"services": {
Type: schema.TypeSet,
Optional: true,
Description: "Enabled services setting for this interface. Current possible values are 'vmotion', 'management' and 'vsan'",
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validation.StringInSlice(vnicServiceTypeAllowedValues, false),
},
},
}
return sch
}

func updateVNic(d *schema.ResourceData, meta interface{}) (string, error) {
err := precheckEnableServices(d)
if err != nil {
return "", err
}

client := meta.(*Client).vimClient
hostID, nicID := splitHostIDNicID(d)
ctx := context.TODO()
Expand All @@ -295,10 +352,60 @@ func updateVNic(d *schema.ResourceData, meta interface{}) (string, error) {
return "", err
}

err = updateVnicService(d, hostID, nicID, meta)
if err != nil {
return "", err
}

return nicID, nil
}

func updateVnicService(d *schema.ResourceData, hostID string, nicID string, meta interface{}) error {
serviceOld, serviceNew := d.GetChange("services")
deleteList := serviceOld.(*schema.Set).List()
addList := serviceNew.(*schema.Set).List()

client := meta.(*Client).vimClient
ctx := context.TODO()
hostSystem, err := hostsystem.FromID(client, hostID)
if err != nil {
return err
}
method, err := hostSystem.ConfigManager().VirtualNicManager(ctx)
if err != nil {
return nil
}

for _, value := range deleteList {
err = method.DeselectVnic(ctx, value.(string), nicID)
if err != nil {
return err
}
}

for _, value := range addList {
err = method.SelectVnic(ctx, value.(string), nicID)
if err != nil {
return err
}
}

return nil
}

func precheckEnableServices(d *schema.ResourceData) error {
if d.Get("netstack").(string) != "defaultTcpipStack" && len(d.Get("services").(*schema.Set).List()) != 0 {
return fmt.Errorf("services can only be configured when netstack is set to defaultTcpipStack")
}
return nil
}

func createVNic(d *schema.ResourceData, meta interface{}) (string, error) {
err := precheckEnableServices(d)
if err != nil {
return "", err
}

client := meta.(*Client).vimClient
ctx := context.TODO()

Expand All @@ -319,6 +426,12 @@ func createVNic(d *schema.ResourceData, meta interface{}) (string, error) {
return "", err
}
d.SetId(fmt.Sprintf("%s_%s", hostID, nicID))

err = updateVnicService(d, hostID, nicID, meta)
if err != nil {
return "", err
}

return nicID, nil
}

Expand Down
114 changes: 114 additions & 0 deletions vsphere/resource_vsphere_vnic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"errors"
"fmt"
"os"
"regexp"
"strconv"
"strings"
"testing"
Expand All @@ -18,6 +19,8 @@ import (
"github.com/vmware/govmomi"
)

// TODO: move away from tests being composed in this manner

type genTfConfig func(string) string

func generateSteps(cfgFunc genTfConfig, netstack string) []resource.TestStep {
Expand Down Expand Up @@ -117,6 +120,117 @@ func TestAccResourceVSphereVNic_hvs_vmotion(t *testing.T) {
})
}

func TestAccResourceVSphereVNic_services_nonDefaultNetstack(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() {
RunSweepers()
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccVSphereVNicDestroy,
Steps: []resource.TestStep{
{
Config: testaccvspherevnicconfigHvs(
combineSnippets(
ipv4Snippet("192.0.2.10|255.255.255.0|192.0.2.1"),
"",
netstackSnippet("vmotion"),
"",
`services = ["vsan"]`,
),
),
ExpectError: regexp.MustCompile("services can only be configured when netstack is set to defaultTcpipStack"),
},
},
})
}

func TestAccResourceVSphereVNic_services_invalid(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() {
RunSweepers()
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccVSphereVNicDestroy,
Steps: []resource.TestStep{
{
Config: testaccvspherevnicconfigHvs(
combineSnippets(
ipv4Snippet("192.0.2.10|255.255.255.0|192.0.2.1"),
"",
netstackSnippet("defaultTcpipStack"),
"",
`services = ["invalid"]`,
),
),
ExpectError: regexp.MustCompile("Error"),
PlanOnly: true,
},
},
})
}

func TestAccResourceVSphereVNic_services_valid(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() {
RunSweepers()
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccVSphereVNicDestroy,
Steps: []resource.TestStep{
{
Config: testaccvspherevnicconfigHvs(
combineSnippets(
ipv4Snippet("192.0.2.10|255.255.255.0|192.0.2.1"),
"",
netstackSnippet("defaultTcpipStack"),
"",
`services = ["vsan"]`,
),
),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("vsphere_vnic.v1", "services.#", "1"),
resource.TestCheckTypeSetElemAttr("vsphere_vnic.v1", "services.*", "vsan"),
),
},
{
Config: testaccvspherevnicconfigHvs(
combineSnippets(
ipv4Snippet("192.0.2.10|255.255.255.0|192.0.2.1"),
"",
netstackSnippet("defaultTcpipStack"),
"",
`services = ["vmotion"]`,
),
),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("vsphere_vnic.v1", "services.#", "1"),
resource.TestCheckTypeSetElemAttr("vsphere_vnic.v1", "services.*", "vmotion"),
),
},
{
Config: testaccvspherevnicconfigHvs(
combineSnippets(
ipv4Snippet("192.0.2.10|255.255.255.0|192.0.2.1"),
"",
netstackSnippet("defaultTcpipStack"),
"",
`services = ["vmotion", "management", "vsan"]`,
),
),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("vsphere_vnic.v1", "services.#", "3"),
resource.TestCheckTypeSetElemAttr("vsphere_vnic.v1", "services.*", "vmotion"),
resource.TestCheckTypeSetElemAttr("vsphere_vnic.v1", "services.*", "management"),
resource.TestCheckTypeSetElemAttr("vsphere_vnic.v1", "services.*", "vsan"),
),
},
},
})
}

func testAccVsphereVNicNetworkSettings(name, ipv4State, ipv6State, netstack string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[name]
Expand Down
2 changes: 2 additions & 0 deletions website/docs/r/vnic.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ resource "vsphere_vnic" "v1" {
ipv4 {
dhcp = true
}
enabled_services = ["vsan", "management"]
}
```

Expand All @@ -98,6 +99,7 @@ resource "vsphere_vnic" "v1" {
* `mac` - (Optional) MAC address of the interface.
* `mtu` - (Optional) MTU of the interface.
* `netstack` - (Optional) TCP/IP stack setting for this interface. Possible values are 'defaultTcpipStack', 'vmotion', 'vSphereProvisioning'. Changing this will force the creation of a new interface since it's not possible to change the stack once it gets created. (Default: `defaultTcpipStack`)
* `services` - (Optional) Enabled services setting for this interface. Current possible values are 'vmotion', 'management', and 'vsan'.


### ipv4 options
Expand Down