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

Add Resource NSX-T Route Advertisement #858

Merged
merged 55 commits into from
Jun 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
ce70803
Update go.mod
May 26, 2022
03015fd
Add new resource vcd_nsxt_route_advertisement
May 26, 2022
908e1e8
Add skeleton for route advertisement resource
May 26, 2022
caa32b4
Add route advertisement test file
May 26, 2022
c0e2c9a
CRUD methods implemented
May 26, 2022
f42d9b3
Fix issue with leftover code
May 26, 2022
b28e01c
Improve testing
May 26, 2022
ee34def
Improve testing
May 26, 2022
85f1961
Fix typo
May 26, 2022
06241cc
Fix bugs
May 26, 2022
5f197b2
Fix issue with tests
May 26, 2022
68b3079
Docs fix
May 27, 2022
28e6ed8
Add docs
May 27, 2022
c6a45d0
Add import
May 27, 2022
9767559
Add test for import
May 27, 2022
7334f23
Improved docs
May 27, 2022
a048777
Fix issues docs
May 30, 2022
5e0a163
Add tags to test file
May 30, 2022
5202946
Self-review
May 30, 2022
d807599
Add changelog
May 30, 2022
3fa271f
Fix conditional
May 31, 2022
9961ef2
Remove vdc resource argument and fix some nits
Jun 2, 2022
eb647dd
Add test for route advertisement set to false
Jun 2, 2022
20afdd0
Fix some nits in docs
Jun 2, 2022
1c5654f
Add checkNSXTEdgeGatewayDedicated to error if NSX-T edge gateway is n…
Jun 2, 2022
caa9e72
Self review
Jun 2, 2022
a74a8bb
Add `VCDClient.GetOrgNameFromResource` and `VCDClient.GetOrgName`
Jun 2, 2022
01fa1d2
Improve org name retrieval
Jun 2, 2022
3d256cb
Add test precondition to make it work
Jun 3, 2022
266ac4f
Add fields VdcGroup and VdcGroupEdgeGateway to TestConfig struct
Jun 8, 2022
9867a5b
Add test case for vdc group
Jun 8, 2022
d427329
Add lock for vdc group as well as edge gateway
Jun 9, 2022
1fab22f
Add test for vdc group scenario
Jun 9, 2022
15d3be1
Merge branch 'main' into resource-nsxt-route-advertisement
Jun 9, 2022
4e2a0f7
Add route_advertisement datasource
Jun 13, 2022
251c599
Add vcd_nsxt_route_advertisement datasource
Jun 13, 2022
8f2bdad
Update changelog with datasource
Jun 13, 2022
0d2954f
Add route advertisement data source docs
Jun 13, 2022
e53f20a
Fix nits
Jun 13, 2022
97bbb06
Fix nits
Jun 13, 2022
afaca5a
Remove redundant example
Jun 13, 2022
c7a5ceb
Fix nit in docs
Jun 13, 2022
e0fbae8
Fix docs nits
Jun 14, 2022
c1690c2
Add test for route advertisement datasource
Jun 14, 2022
6fcd2be
Update go.mod
Jun 14, 2022
55f7594
Change old methods that needed contect for new ones that comes with i…
Jun 14, 2022
d6431ed
Skip binary test execution for test TestAccVcdNsxtRouteAdvertisement
Jun 14, 2022
f246660
Update go.mod
Jun 14, 2022
1e4b28b
Update method names
Jun 14, 2022
91b9ae0
Merge branch 'main' into resource-nsxt-route-advertisement
Jun 15, 2022
8e35e4c
Adapt test for CDS
Jun 15, 2022
f72a988
Improve docs
Jun 15, 2022
3d24473
Update go.mod
Jun 15, 2022
1fec4ec
Merge branch 'main' into resource-nsxt-route-advertisement
Jun 15, 2022
be149e0
Update go.mod
Jun 15, 2022
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
2 changes: 2 additions & 0 deletions .changes/v3.7.0/858-features.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
* Add resource `vcd_nsxt_route_advertisement` that allows NSX-T Edge Gateway to advertise subnets to Tier-0 Gateway [GH-858]
* Add datasource `vcd_nsxt_route_advertisement` that reads the NSX-T Edge Gateway routes that are being advertised [GH-858]
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ require (
github.com/hashicorp/go-version v1.5.0
github.com/hashicorp/terraform-plugin-sdk/v2 v2.17.0
github.com/kr/pretty v0.2.1
github.com/vmware/go-vcloud-director/v2 v2.16.0-alpha.7
github.com/vmware/go-vcloud-director/v2 v2.16.0-alpha.9
)
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -224,8 +224,8 @@ github.com/vmihailenco/msgpack/v4 v4.3.12 h1:07s4sz9IReOgdikxLTKNbBdqDMLsjPKXwvC
github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
github.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY=
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
github.com/vmware/go-vcloud-director/v2 v2.16.0-alpha.7 h1:sLzaQfSbcYq5Sm4VrrDZ+mMxFdCBGL3Nij1gtrlRcRg=
github.com/vmware/go-vcloud-director/v2 v2.16.0-alpha.7/go.mod h1:2BS1yw61VN34WI0/nUYoInFvBc3Zcuf84d4ESiAAl68=
github.com/vmware/go-vcloud-director/v2 v2.16.0-alpha.9 h1:i8OqjVqUoYuPcu/TByXxkxGg02xsX7X7lJlcjrl9y9o=
github.com/vmware/go-vcloud-director/v2 v2.16.0-alpha.9/go.mod h1:2BS1yw61VN34WI0/nUYoInFvBc3Zcuf84d4ESiAAl68=
github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI=
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
Expand Down
19 changes: 19 additions & 0 deletions vcd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,25 @@ func (cli *VCDClient) GetNsxtEdgeGatewayFromResourceById(d *schema.ResourceData,
return egw, nil
}

// GetOrgNameFromResource returns the Org name if set at resource level. If not, tries to get it from provider level.
// It errors if none is provided.
func (cli *VCDClient) GetOrgNameFromResource(d *schema.ResourceData) (string, error) {
orgName := d.Get("org").(string)
return cli.GetOrgName(orgName)
}

// GetOrgName returns the parameter orgName if provided. If not tried to get it from provider.
func (cli *VCDClient) GetOrgName(orgName string) (string, error) {
if orgName == "" {
orgName = cli.Org
}
if orgName == "" {
return "", fmt.Errorf("empty Org name provided")
}

return orgName, nil
}

func ProviderAuthenticate(client *govcd.VCDClient, user, password, token, org, apiToken string) error {
var err error
if apiToken != "" {
Expand Down
16 changes: 9 additions & 7 deletions vcd/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,13 +130,15 @@ type TestConfig struct {
} `json:"peer"`
} `json:"networking"`
Nsxt struct {
Manager string `json:"manager"`
Tier0router string `json:"tier0router"`
Tier0routerVrf string `json:"tier0routervrf"`
Vdc string `json:"vdc"`
ExternalNetwork string `json:"externalNetwork"`
EdgeGateway string `json:"edgeGateway"`
NsxtImportSegment string `json:"nsxtImportSegment"`
Manager string `json:"manager"`
Tier0router string `json:"tier0router"`
Tier0routerVrf string `json:"tier0routervrf"`
Vdc string `json:"vdc"`
ExternalNetwork string `json:"externalNetwork"`
EdgeGateway string `json:"edgeGateway"`
VdcGroup string `json:"vdcGroup"`
VdcGroupEdgeGateway string `json:"vdcGroupEdgeGateway"`
NsxtImportSegment string `json:"nsxtImportSegment"`

NsxtAlbControllerUrl string `json:"nsxtAlbControllerUrl"`
NsxtAlbControllerUser string `json:"nsxtAlbControllerUser"`
Expand Down
73 changes: 73 additions & 0 deletions vcd/datasource_vcd_nsxt_route_advertisement.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package vcd

import (
"context"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func datasourceVcdNsxtRouteAdvertisement() *schema.Resource {
return &schema.Resource{
ReadContext: datasourceVcdNsxtRouteAdvertisementRead,

Schema: map[string]*schema.Schema{
"org": {
Type: schema.TypeString,
Optional: true,
Description: "The name of organization to use, optional if defined at provider " +
"level. Useful when connected as sysadmin working across different organizations",
},
"edge_gateway_id": {
Type: schema.TypeString,
Required: true,
Description: "NSX-T Edge Gateway ID in which route advertisement is located",
},
"enabled": {
Type: schema.TypeBool,
Computed: true,
Description: "Defines if route advertisement is active",
},
"subnets": {
Type: schema.TypeSet,
Computed: true,
Description: "Set of subnets that will be advertised to Tier-0 gateway. Empty means none",
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
},
}
}

func datasourceVcdNsxtRouteAdvertisementRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
vcdClient := meta.(*VCDClient)

edgeGatewayID := d.Get("edge_gateway_id").(string)

orgName, err := vcdClient.GetOrgNameFromResource(d)
if err != nil {
return diag.Errorf("error when getting Org name - %s", err)
}

nsxtEdge, err := vcdClient.GetNsxtEdgeGatewayById(orgName, edgeGatewayID)
if err != nil {
return diag.Errorf("error retrieving NSX-T Edge Gateway: %s", err)
}

routeAdvertisement, err := nsxtEdge.GetNsxtRouteAdvertisement()
if err != nil {
return diag.Errorf("error while retrieving route advertisement - %s", err)
}

dSet(d, "enabled", routeAdvertisement.Enable)

subnetSet := convertStringsToTypeSet(routeAdvertisement.Subnets)
err = d.Set("subnets", subnetSet)
if err != nil {
return diag.Errorf("error while setting subnets argument: %s", err)
}

d.SetId(edgeGatewayID)

return nil
}
2 changes: 2 additions & 0 deletions vcd/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ var globalDataSourceMap = map[string]*schema.Resource{
"vcd_vdc_group": datasourceVdcGroup(), // 3.5
"vcd_nsxt_distributed_firewall": datasourceVcdNsxtDistributedFirewall(), // 3.6
"vcd_nsxt_network_context_profile": datasourceVcdNsxtNetworkContextProfile(), // 3.6
"vcd_nsxt_route_advertisement": datasourceVcdNsxtRouteAdvertisement(), // 3.7

}

Expand Down Expand Up @@ -163,6 +164,7 @@ var globalResourceMap = map[string]*schema.Resource{
"vcd_vdc_group": resourceVdcGroup(), // 3.5
"vcd_nsxt_distributed_firewall": resourceVcdNsxtDistributedFirewall(), // 3.6
"vcd_security_tag": resourceVcdSecurityTag(), // 3.7
"vcd_nsxt_route_advertisement": resourceVcdNsxtRouteAdvertisement(), // 3.7
"vcd_org_vdc_access_control": resourceVcdOrgVdcAccessControl(), // 3.7
}

Expand Down
216 changes: 216 additions & 0 deletions vcd/resource_vcd_nsxt_route_advertisement.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
package vcd

import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/vmware/go-vcloud-director/v2/govcd"
"log"
"strings"
)

func resourceVcdNsxtRouteAdvertisement() *schema.Resource {
return &schema.Resource{
CreateContext: resourceVcdNsxtRouteAdvertisementCreateUpdate,
ReadContext: resourceVcdNsxtRouteAdvertisementRead,
UpdateContext: resourceVcdNsxtRouteAdvertisementCreateUpdate,
DeleteContext: resourceVcdNsxtRouteAdvertisementDelete,
Importer: &schema.ResourceImporter{
StateContext: resourceVcdNsxtRouteAdvertisementImport,
},
Schema: map[string]*schema.Schema{
"org": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Description: "The name of organization to use, optional if defined at provider " +
"level. Useful when connected as sysadmin working across different organizations",
},
"edge_gateway_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "NSX-T Edge Gateway ID in which route advertisement is located",
},
"enabled": {
Type: schema.TypeBool,
Optional: true,
Default: true,
Description: "Defines if route advertisement is active",
},
"subnets": {
Type: schema.TypeSet,
Optional: true,
Description: "Set of subnets that will be advertised to Tier-0 gateway. Empty means none",
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
},
}
}

func resourceVcdNsxtRouteAdvertisementCreateUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
vcdClient := meta.(*VCDClient)

// Handling locks on a route advertisement is conditional. There are two scenarios:
// * When the parent Edge Gateway is in a VDC - a lock on parent Edge Gateway must be acquired
// * When the parent Edge Gateway is in a VDC Group - a lock on parent VDC Group must be acquired
// To find out parent lock object, Edge Gateway must be looked up and its OwnerRef must be checked
// Note. It is not safe to do multiple locks in the same resource as it can result in a deadlock
parentEdgeGatewayOwnerId, _, err := getParentEdgeGatewayOwnerId(vcdClient, d)
if err != nil {
return diag.Errorf("[routed advertisement create/update] error finding parent Edge Gateway: %s", err)
}

if govcd.OwnerIsVdcGroup(parentEdgeGatewayOwnerId) {
vcdClient.lockById(parentEdgeGatewayOwnerId)
defer vcdClient.unlockById(parentEdgeGatewayOwnerId)
} else {
vcdClient.lockParentEdgeGtw(d)
defer vcdClient.unLockParentEdgeGtw(d)
}

var subnets []string
enableRouteAdvertisement := d.Get("enabled").(bool)
subnetsFromSchema, ok := d.GetOk("subnets")

if ok {
subnets = convertSchemaSetToSliceOfStrings(subnetsFromSchema.(*schema.Set))
}

if !enableRouteAdvertisement && len(subnets) > 0 {
return diag.Errorf("if enable is set to false, no subnets must be passed")
}

_, edgeGateway, err := getParentEdgeGatewayOwnerIdAndNsxtEdgeGateway(vcdClient, d, "route advertisement")
if err != nil {
return diag.FromErr(err)
}

err = checkNSXTEdgeGatewayDedicated(edgeGateway)
if err != nil {
return diag.Errorf("error when configuring route advertisement on NSX-T Edge Gateway - %s", err)
}

_, err = edgeGateway.UpdateNsxtRouteAdvertisement(enableRouteAdvertisement, subnets)
if err != nil {
return diag.Errorf("error when creating/updating route advertisement - %s", err)
}

d.SetId(edgeGateway.EdgeGateway.ID)

return resourceVcdNsxtRouteAdvertisementRead(ctx, d, meta)
}

func resourceVcdNsxtRouteAdvertisementRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
vcdClient := meta.(*VCDClient)

orgName, err := vcdClient.GetOrgNameFromResource(d)
if err != nil {
return diag.Errorf("error when getting Org name - %s", err)
}

nsxtEdge, err := vcdClient.GetNsxtEdgeGatewayById(orgName, d.Id())
if err != nil {
if govcd.ContainsNotFound(err) {
d.SetId("")
return nil
}
return diag.Errorf("error retrieving NSX-T Edge Gateway: %s", err)
}

routeAdvertisement, err := nsxtEdge.GetNsxtRouteAdvertisement()
if err != nil {
return diag.Errorf("error while retrieving route advertisement - %s", err)
}

dSet(d, "enabled", routeAdvertisement.Enable)

subnetSet := convertStringsToTypeSet(routeAdvertisement.Subnets)
err = d.Set("subnets", subnetSet)
if err != nil {
return diag.Errorf("error while setting subnets argument: %s", err)
}

return nil
}

func resourceVcdNsxtRouteAdvertisementDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
vcdClient := meta.(*VCDClient)

// Handling locks on a route advertisement is conditional. There are two scenarios:
// * When the parent Edge Gateway is in a VDC - a lock on parent Edge Gateway must be acquired
// * When the parent Edge Gateway is in a VDC Group - a lock on parent VDC Group must be acquired
// To find out parent lock object, Edge Gateway must be looked up and its OwnerRef must be checked
// Note. It is not safe to do multiple locks in the same resource as it can result in a deadlock
parentEdgeGatewayOwnerId, _, err := getParentEdgeGatewayOwnerId(vcdClient, d)
if err != nil {
return diag.Errorf("[route advertisement delete] error finding parent Edge Gateway: %s", err)
}

if govcd.OwnerIsVdcGroup(parentEdgeGatewayOwnerId) {
vcdClient.lockById(parentEdgeGatewayOwnerId)
defer vcdClient.unlockById(parentEdgeGatewayOwnerId)
} else {
vcdClient.lockParentEdgeGtw(d)
defer vcdClient.unLockParentEdgeGtw(d)
}

orgName, err := vcdClient.GetOrgNameFromResource(d)
if err != nil {
return diag.Errorf("error when getting Org name - %s", err)
}

nsxtEdge, err := vcdClient.GetNsxtEdgeGatewayById(orgName, d.Id())
if err != nil {
return diag.Errorf("error retrieving NSX-T Edge Gateway: %s", err)
}

err = nsxtEdge.DeleteNsxtRouteAdvertisement()
if err != nil {
return diag.Errorf("error while deleting route advertisement - %s", err)
}

d.SetId("")
return nil
}

func resourceVcdNsxtRouteAdvertisementImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
log.Printf("[TRACE] NSX-T Edge Gateway Route Advertisement import initiated")

resourceURI := strings.Split(d.Id(), ImportSeparator)
if len(resourceURI) != 3 {
return nil, fmt.Errorf("resource name must be specified as org-name.vdc-or-vdc-group-name.nsxt-edge-gw-name")
}
orgName, vdcOrVdcGroupName, edgeName := resourceURI[0], resourceURI[1], resourceURI[2]

vcdClient := meta.(*VCDClient)
vdcOrVdcGroup, err := lookupVdcOrVdcGroup(vcdClient, orgName, vdcOrVdcGroupName)
if err != nil {
return nil, err
}

edge, err := vdcOrVdcGroup.GetNsxtEdgeGatewayByName(edgeName)
if err != nil {
return nil, fmt.Errorf("could not retrieve NSX-T edge gateway with ID '%s': %s", d.Id(), err)
}

dSet(d, "org", orgName)

dSet(d, "edge_gateway_id", edge.EdgeGateway.ID)
d.SetId(edge.EdgeGateway.ID)

return []*schema.ResourceData{d}, nil
}

// checkNSXTEdgeGatewayDedicated is a simple helper function that checks if "Using Dedicated Provider Router" option is enabled
// on NSX-T Edge Gateway so that route advertisement can be configured. If not it returns an error.
func checkNSXTEdgeGatewayDedicated(nsxtEdgeGw *govcd.NsxtEdgeGateway) error {
if !nsxtEdgeGw.EdgeGateway.EdgeGatewayUplinks[0].Dedicated {
return fmt.Errorf("NSX-T Edge Gateway is not using a dedicated provider router. Please enable this feature before configuring route advertisement")
}

return nil
}
Loading