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 Nsxt route advertisement methods #478

Merged
merged 23 commits into from
Jun 15, 2022
Merged
Show file tree
Hide file tree
Changes from 11 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
1 change: 1 addition & 0 deletions .changes/v2.16.0/478-features.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Add NSX-T Edge Gateway methods `NsxtEdgeGateway.GetNsxtRouteAdvertisement`, `NsxtEdgeGateway.UpdateNsxtRouteAdvertisement` and `NsxtEdgeGateway.DeleteNsxtRouteAdvertisement` that allows to manage NSX-T route advertisement [GH-478]
112 changes: 112 additions & 0 deletions govcd/nsxt_route_advertisement.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright 2022 VMware, Inc. All rights reserved. Licensed under the Apache v2 License.
*/

package govcd

import (
"fmt"
"github.com/vmware/go-vcloud-director/v2/types/v56"
)

// GetNsxtRouteAdvertisement retrieves the list of subnets that will be advertised so that the Edge Gateway can route
// out to the connected external network.
func (egw *NsxtEdgeGateway) GetNsxtRouteAdvertisement(useTenantContext bool) (*types.RouteAdvertisement, error) {
err := checkSanityNsxtEdgeGatewayRouteAdvertisement(egw)
if err != nil {
return nil, err
}

endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointNsxtRouteAdvertisement

highestApiVersion, err := egw.client.getOpenApiHighestElevatedVersion(endpoint)
if err != nil {
return nil, err
}

urlRef, err := egw.client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, egw.EdgeGateway.ID))
if err != nil {
return nil, err
}

var tenantContextHeaders map[string]string
if useTenantContext {
tenantContext, err := egw.getTenantContext()
if err != nil {
return nil, err
}

tenantContextHeaders = getTenantContextHeader(tenantContext)
}

routeAdvertisement := &types.RouteAdvertisement{}
err = egw.client.OpenApiGetItem(highestApiVersion, urlRef, nil, routeAdvertisement, tenantContextHeaders)
if err != nil {
return nil, err
}

return routeAdvertisement, nil
}

// UpdateNsxtRouteAdvertisement updates the list of subnets that will be advertised so that the Edge Gateway can route
// out to the connected external network.
func (egw *NsxtEdgeGateway) UpdateNsxtRouteAdvertisement(enable bool, subnets []string, useTenantContext bool) (*types.RouteAdvertisement, error) {
err := checkSanityNsxtEdgeGatewayRouteAdvertisement(egw)
if err != nil {
return nil, err
}

endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointNsxtRouteAdvertisement

highestApiVersion, err := egw.client.getOpenApiHighestElevatedVersion(endpoint)
if err != nil {
return nil, err
}

urlRef, err := egw.client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, egw.EdgeGateway.ID))
if err != nil {
return nil, err
}

var tenantContextHeaders map[string]string
if useTenantContext {
tenantContext, err := egw.getTenantContext()
if err != nil {
return nil, err
}

tenantContextHeaders = getTenantContextHeader(tenantContext)
}

routeAdvertisement := &types.RouteAdvertisement{
Enable: enable,
Subnets: subnets,
}

err = egw.client.OpenApiPutItem(highestApiVersion, urlRef, nil, routeAdvertisement, nil, tenantContextHeaders)
if err != nil {
return nil, err
}

return egw.GetNsxtRouteAdvertisement(useTenantContext)
}

// DeleteNsxtRouteAdvertisement deletes the list of subnets that will be advertised.
func (egw *NsxtEdgeGateway) DeleteNsxtRouteAdvertisement(useTenantContext bool) error {
_, err := egw.UpdateNsxtRouteAdvertisement(false, []string{}, useTenantContext)
return err
}

// checkSanityNsxtEdgeGatewayRouteAdvertisement function performs some checks to *NsxtEdgeGateway parameter and returns error
// if something is wrong. It is useful with methods NsxtEdgeGateway.[Get/Update/Delete]NsxtRouteAdvertisement
func checkSanityNsxtEdgeGatewayRouteAdvertisement(egw *NsxtEdgeGateway) error {
if egw.EdgeGateway == nil {
return fmt.Errorf("the EdgeGateway pointer is nil. Please initialize it first before using this method")
}

if egw.EdgeGateway.ID == "" {
return fmt.Errorf("the EdgeGateway ID is empty. Please initialize it first before using this method")
}

return nil
}
63 changes: 63 additions & 0 deletions govcd/nsxt_route_advertisement_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//go:build network || nsxt || functional || openapi || ALL
// +build network nsxt functional openapi ALL

/*
* Copyright 2022 VMware, Inc. All rights reserved. Licensed under the Apache v2 License.
*/

package govcd

import (
"fmt"
"github.com/vmware/go-vcloud-director/v2/types/v56"
. "gopkg.in/check.v1"
)

func (vcd *TestVCD) Test_NsxtEdgeRouteAdvertisement(check *C) {
skipNoNsxtConfiguration(vcd, check)
skipOpenApiEndpointTest(vcd, check, types.OpenApiPathVersion1_0_0+types.OpenApiEndpointNsxtRouteAdvertisement)

org, err := vcd.client.GetOrgByName(vcd.config.VCD.Org)
check.Assert(err, IsNil)
nsxtVdc, err := org.GetVDCByName(vcd.config.VCD.Nsxt.Vdc, false)
check.Assert(err, IsNil)
edge, err := nsxtVdc.GetNsxtEdgeGatewayByName(vcd.config.VCD.Nsxt.EdgeGateway)
check.Assert(err, IsNil)

// Make sure we are using a dedicated Tier-0 gateway (otherwise route advertisement won't be available)
edge.EdgeGateway.EdgeGatewayUplinks[0].Dedicated = true
edge, err = edge.Update(edge.EdgeGateway)
check.Assert(err, IsNil)
check.Assert(edge, NotNil)

network1 := "192.168.1.0/24"
network2 := "192.168.2.0/24"
networksToAdvertise := []string{network1, network2} // Sample networks to advertise

// Test UpdateNsxtRouteAdvertisement
nsxtEdgeRouteAdvertisement, err := edge.UpdateNsxtRouteAdvertisement(true, networksToAdvertise, true)
check.Assert(err, IsNil)
check.Assert(nsxtEdgeRouteAdvertisement, NotNil)
check.Assert(nsxtEdgeRouteAdvertisement.Enable, Equals, true)
check.Assert(len(nsxtEdgeRouteAdvertisement.Subnets), Equals, 2)
check.Assert(checkNetworkInSubnetsSlice(network1, networksToAdvertise), IsNil)
check.Assert(checkNetworkInSubnetsSlice(network2, networksToAdvertise), IsNil)

// Test DeleteNsxtRouteAdvertisement
err = edge.DeleteNsxtRouteAdvertisement(true)
check.Assert(err, IsNil)
nsxtEdgeRouteAdvertisement, err = edge.GetNsxtRouteAdvertisement(true)
check.Assert(err, IsNil)
check.Assert(nsxtEdgeRouteAdvertisement, NotNil)
check.Assert(nsxtEdgeRouteAdvertisement.Enable, Equals, false)
check.Assert(len(nsxtEdgeRouteAdvertisement.Subnets), Equals, 0)
}

func checkNetworkInSubnetsSlice(network string, subnets []string) error {
for _, subnet := range subnets {
if subnet == network {
return nil
}
}
return fmt.Errorf("network %s is not within the slice provided", network)
}
1 change: 1 addition & 0 deletions govcd/openapi_endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ var endpointMinApiVersions = map[string]string{
types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointVdcGroupsDfwPolicies: "35.0", // VCD 10.2+
types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointVdcGroupsDfwDefaultPolicies: "35.0", // VCD 10.2+
types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointSecurityTags: "36.0", // VCD 10.3+
types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointNsxtRouteAdvertisement: "34.0", // VCD 10.1+

// NSX-T ALB (Advanced/AVI Load Balancer) support was introduced in 10.2
types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointAlbController: "35.0", // VCD 10.2+
Expand Down
12 changes: 12 additions & 0 deletions govcd/tenant_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,3 +183,15 @@ func (vdcGroup *VdcGroup) getTenantContext() (*TenantContext, error) {
}
return org.tenantContext()
}

func (egw *NsxtEdgeGateway) getTenantContext() (*TenantContext, error) {
if egw != nil && egw.EdgeGateway.Org != nil {
if egw.EdgeGateway.Org.Name == "" || egw.EdgeGateway.Org.ID == "" {
return nil, fmt.Errorf("either parent NsxtEdgeGateway Org name or ID is empty and both must be set. Org name is [%s] and Org ID is [%s]", egw.EdgeGateway.Org.Name, egw.EdgeGateway.Org.ID)
}

return &TenantContext{OrgId: egw.EdgeGateway.Org.ID, OrgName: egw.EdgeGateway.Org.Name}, nil
}

return nil, fmt.Errorf("NsxtEdgeGateway is not fully initialized. Please initialized before using this method")
}
1 change: 1 addition & 0 deletions types/v56/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,7 @@ const (
OpenApiEndpointVdcGroupsDfwRules = "vdcGroups/%s/dfwPolicies/%s/rules"
OpenApiEndpointNetworkContextProfiles = "networkContextProfiles"
OpenApiEndpointSecurityTags = "securityTags"
OpenApiEndpointNsxtRouteAdvertisement = "edgeGateways/%s/routing/advertisement"

// NSX-T ALB related endpoints

Expand Down
10 changes: 10 additions & 0 deletions types/v56/nsxt_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -1244,3 +1244,13 @@ type EntitySecurityTags struct {
// Tags is the list of tags. The value is case-agnostic and will be converted to lower-case.
Tags []string `json:"tags"`
}

// RouteAdvertisement lists the subnets that will be advertised so that the Edge Gateway can route out to the
// connected external network.
type RouteAdvertisement struct {
// Enable if true, means that the subnets will be advertised. The default is true.
Enable bool `json:"enable"`
// Subnets is the list of subnets that will be advertised so that the Edge Gateway can route out to the connected
// external network.
Subnets []string `json:"subnets"`
}