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 support for NSX-T Edge Gateway BGP IP Prefix lists #488

Merged
merged 5 commits into from
Jul 27, 2022
Merged
Show file tree
Hide file tree
Changes from 3 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
3 changes: 3 additions & 0 deletions .changes/v2.16.0/488-features.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
* Add support for managing NSX-T Edge Gateway BGP IP Prefix Lists. It is done by adding types `EdgeBgpIpPrefixList` and
`types.EdgeBgpIpPrefixList` with functions `CreateBgpIpPrefixList`, `GetAllBgpIpPrefixLists`,
`GetBgpIpPrefixListByName`, `GetBgpIpPrefixListById`, `Update` and `Delete` [GH-488]
243 changes: 243 additions & 0 deletions govcd/nsxt_edgegateway_bgp_ip_prefix_list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
/*
* Copyright 2022 VMware, Inc. All rights reserved. Licensed under the Apache v2 License.
*/

package govcd

import (
"fmt"
"net/url"

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

// EdgeBgpIpPrefixList helps to configure BGP IP Prefix Lists in NSX-T Edge Gateways
type EdgeBgpIpPrefixList struct {
EdgeBgpIpPrefixList *types.EdgeBgpIpPrefixList
client *Client
// edgeGatewayId is stored for usage in EdgeBgpIpPrefixList receiver functions
edgeGatewayId string
}

// CreateBgpIpPrefixList creates a BGP IP Prefix List with supplied configuration
//
// Note. VCD 10.2 versions do not automatically return ID for created BGP IP Prefix List. To work around it this code
// automatically retrieves the entity by Name after the task is finished. This function may fail on VCD 10.2 if
// duplicate BGP IP Prefix Lists exist.
func (egw *NsxtEdgeGateway) CreateBgpIpPrefixList(bgpIpPrefixList *types.EdgeBgpIpPrefixList) (*EdgeBgpIpPrefixList, error) {
client := egw.client
endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeBgpConfigPrefixLists
apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint)
if err != nil {
return nil, err
}

// Insert Edge Gateway ID into endpoint path
urlRef, err := client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, egw.EdgeGateway.ID))
if err != nil {
return nil, err
}

returnObject := &EdgeBgpIpPrefixList{
client: egw.client,
edgeGatewayId: egw.EdgeGateway.ID,
EdgeBgpIpPrefixList: &types.EdgeBgpIpPrefixList{},
}

task, err := client.OpenApiPostItemAsync(apiVersion, urlRef, nil, bgpIpPrefixList)
if err != nil {
return nil, fmt.Errorf("error creating NSX-T Edge Gateway BGP IP Prefix List: %s", err)
}

err = task.WaitTaskCompletion()
if err != nil {
return nil, fmt.Errorf("error creating NSX-T Edge Gateway BGP IP Prefix List: %s", err)
}

// API has problems therefore explicit manual handling is required to lookup newly created object
// VCD 10.2 -> no ID for newly created object is returned at all
// VCD 10.3 -> `Details` field in task contains ID of newly created object
// To cover all cases this code will at first look for ID in `Details` field and fall back to
// lookup by name if `Details` field is empty.
//
// The drawback of this is that it is possible to create duplicate records with the same name on VCDs that don't
// return IDs, but there is no better way for VCD versions that don't return API code

bgpIpPrefixListId := task.Task.Details
if bgpIpPrefixListId != "" {
getUrlRef, err := client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, egw.EdgeGateway.ID), bgpIpPrefixListId)
if err != nil {
return nil, err
}
err = client.OpenApiGetItem(apiVersion, getUrlRef, nil, returnObject.EdgeBgpIpPrefixList, nil)
if err != nil {
return nil, fmt.Errorf("error retrieving NSX-T Edge Gateway BGP IP Prefix List after creation: %s", err)
}
} else {
// ID after object creation was not returned therefore retrieving the entity by Name to lookup ID
// This has a risk of duplicate items, but is the only way to find the object when ID is not returned
bgpIpPrefixList, err := egw.GetBgpIpPrefixListByName(bgpIpPrefixList.Name)
if err != nil {
return nil, fmt.Errorf("error retrieving NSX-T Edge Gateway BGP IP Prefix List after creation: %s", err)
}
returnObject = bgpIpPrefixList
}

return returnObject, nil
}

// GetAllBgpIpPrefixLists retrieves all BGP IP Prefix Lists in a given NSX-T Edge Gateway with optional queryParameters
func (egw *NsxtEdgeGateway) GetAllBgpIpPrefixLists(queryParameters url.Values) ([]*EdgeBgpIpPrefixList, error) {
queryParams := copyOrNewUrlValues(queryParameters)

client := egw.client
endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeBgpConfigPrefixLists
apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint)
if err != nil {
return nil, err
}

// Insert Edge Gateway ID into endpoint path
urlRef, err := client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, egw.EdgeGateway.ID))
if err != nil {
return nil, err
}

typeResponses := []*types.EdgeBgpIpPrefixList{{}}
err = client.OpenApiGetAllItems(apiVersion, urlRef, queryParams, &typeResponses, nil)
if err != nil {
return nil, err
}

// Wrap all typeResponses into NsxtNatRule types with client
wrappedResponses := make([]*EdgeBgpIpPrefixList, len(typeResponses))
for sliceIndex := range typeResponses {
wrappedResponses[sliceIndex] = &EdgeBgpIpPrefixList{
EdgeBgpIpPrefixList: typeResponses[sliceIndex],
client: client,
edgeGatewayId: egw.EdgeGateway.ID,
}
}

return wrappedResponses, nil
}

// GetBgpIpPrefixListByName retrieves BGP IP Prefix List By Name
// It is meant to retrieve exactly one entry:
// * Will fail if more than one entry with the same name found
// * Will return an error containing `ErrorEntityNotFound` if no entries are found
//
// Note. API does not support filtering by 'name' field therefore filtering is performed on client
// side
func (egw *NsxtEdgeGateway) GetBgpIpPrefixListByName(name string) (*EdgeBgpIpPrefixList, error) {
if name == "" {
return nil, fmt.Errorf("name cannot be empty")
}

allBgpIpPrefixLists, err := egw.GetAllBgpIpPrefixLists(nil)
if err != nil {
return nil, fmt.Errorf("error retrieving NSX-T Edge Gateway BGP IP Prefix List: %s", err)
}

var filteredBgpIpPrefixLists []*EdgeBgpIpPrefixList
for _, bgpIpPrefixList := range allBgpIpPrefixLists {
if bgpIpPrefixList.EdgeBgpIpPrefixList.Name == name {
filteredBgpIpPrefixLists = append(filteredBgpIpPrefixLists, bgpIpPrefixList)
}
}

if len(filteredBgpIpPrefixLists) > 1 {
return nil, fmt.Errorf("more than one NSX-T Edge Gateway BGP IP Prefix List found with Name '%s'", name)
}

if len(filteredBgpIpPrefixLists) == 0 {
return nil, fmt.Errorf("%s: no NSX-T Edge Gateway BGP IP Prefix List found with name '%s'", ErrorEntityNotFound, name)
}

return filteredBgpIpPrefixLists[0], nil
}

// GetBgpIpPrefixListById retrieves BGP IP Prefix List By ID
func (egw *NsxtEdgeGateway) GetBgpIpPrefixListById(id string) (*EdgeBgpIpPrefixList, error) {
if id == "" {
return nil, fmt.Errorf("id is required")
}

client := egw.client
endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeBgpConfigPrefixLists
apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint)
if err != nil {
return nil, err
}

// Insert Edge Gateway ID into endpoint path
urlRef, err := client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, egw.EdgeGateway.ID), id)
if err != nil {
return nil, err
}

returnObject := &EdgeBgpIpPrefixList{
client: egw.client,
edgeGatewayId: egw.EdgeGateway.ID,
EdgeBgpIpPrefixList: &types.EdgeBgpIpPrefixList{},
}

err = client.OpenApiGetItem(apiVersion, urlRef, nil, returnObject.EdgeBgpIpPrefixList, nil)
if err != nil {
return nil, fmt.Errorf("error retrieving NSX-T Edge Gateway BGP IP Prefix List: %s", err)
}

return returnObject, nil
}

// Update updates existing BGP IP Prefix List with new configuration and returns it
func (bgpIpPrefixListCfg *EdgeBgpIpPrefixList) Update(bgpIpPrefixList *types.EdgeBgpIpPrefixList) (*EdgeBgpIpPrefixList, error) {
client := bgpIpPrefixListCfg.client
endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeBgpConfigPrefixLists
apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint)
if err != nil {
return nil, err
}

// Insert Edge Gateway ID into endpoint path
urlRef, err := client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, bgpIpPrefixListCfg.edgeGatewayId), bgpIpPrefixList.ID)
if err != nil {
return nil, err
}

returnObject := &EdgeBgpIpPrefixList{
client: bgpIpPrefixListCfg.client,
edgeGatewayId: bgpIpPrefixListCfg.edgeGatewayId,
EdgeBgpIpPrefixList: &types.EdgeBgpIpPrefixList{},
}

err = client.OpenApiPutItem(apiVersion, urlRef, nil, bgpIpPrefixList, returnObject.EdgeBgpIpPrefixList, nil)
if err != nil {
return nil, fmt.Errorf("error setting NSX-T Edge Gateway BGP IP Prefix List: %s", err)
}

return returnObject, nil
}

// Delete deletes existing BGP IP Prefix List
func (bgpIpPrefixListCfg *EdgeBgpIpPrefixList) Delete() error {
client := bgpIpPrefixListCfg.client
endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeBgpConfigPrefixLists
apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint)
if err != nil {
return err
}

// Insert Edge Gateway ID into endpoint path
urlRef, err := client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, bgpIpPrefixListCfg.edgeGatewayId), bgpIpPrefixListCfg.EdgeBgpIpPrefixList.ID)
if err != nil {
return err
}

err = client.OpenApiDeleteItem(apiVersion, urlRef, nil, nil)
if err != nil {
return fmt.Errorf("error deleting NSX-T Edge Gateway BGP IP Prefix List: %s", err)
}

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

package govcd

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

// Test_NsxEdgeBgpIpPrefixList tests CRUD operations for NSX-T Edge Gateway BGP IP Prefix Lists
func (vcd *TestVCD) Test_NsxEdgeBgpIpPrefixList(check *C) {
skipNoNsxtConfiguration(vcd, check)
skipOpenApiEndpointTest(vcd, check, types.OpenApiPathVersion1_0_0+types.OpenApiEndpointEdgeBgpConfigPrefixLists)

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)

// Switch Edge Gateway to use dedicated uplink for the time of this test and then turn it off
err = switchEdgeGatewayDedication(edge, true) // Turn on Dedicated Tier 0 gateway
check.Assert(err, IsNil)
defer switchEdgeGatewayDedication(edge, false) // Turn off Dedicated Tier 0 gateway

// Create a new BGP IP Prefix List
bgpIpPrefixList := &types.EdgeBgpIpPrefixList{
Name: check.TestName(),
Description: "test-description",
Prefixes: []types.EdgeBgpConfigPrefixListPrefixes{
{
Network: "1.1.1.0/24",
Action: "PERMIT",
},
{
Network: "2.1.0.0/16",
Action: "PERMIT",
LessThanEqualTo: 29,
GreaterThanEqualTo: 24,
},
},
}

bgpIpPrefix, err := edge.CreateBgpIpPrefixList(bgpIpPrefixList)
check.Assert(err, IsNil)
check.Assert(bgpIpPrefix, NotNil)

// Get all BGP IP Prefix Lists
bgpIpPrefixLists, err := edge.GetAllBgpIpPrefixLists(nil)
check.Assert(err, IsNil)
check.Assert(bgpIpPrefixLists, NotNil)
check.Assert(len(bgpIpPrefixLists), Equals, 1)
check.Assert(bgpIpPrefixLists[0].EdgeBgpIpPrefixList.Name, Equals, bgpIpPrefixList.Name)

// Get By Name
bgpPrefixListByName, err := edge.GetBgpIpPrefixListByName(bgpIpPrefixList.Name)
check.Assert(err, IsNil)
check.Assert(bgpPrefixListByName, NotNil)

// Get By Id
bgpPrefixListById, err := edge.GetBgpIpPrefixListById(bgpIpPrefix.EdgeBgpIpPrefixList.ID)
check.Assert(err, IsNil)
check.Assert(bgpPrefixListById, NotNil)

// Update
bgpIpPrefixList.Name = check.TestName() + "-updated"
bgpIpPrefixList.Description = "test-description-updated"
bgpIpPrefixList.ID = bgpIpPrefixLists[0].EdgeBgpIpPrefixList.ID

updatedBgpIpPrefixList, err := bgpIpPrefixLists[0].Update(bgpIpPrefixList)
check.Assert(err, IsNil)
check.Assert(updatedBgpIpPrefixList, NotNil)

check.Assert(updatedBgpIpPrefixList.EdgeBgpIpPrefixList.ID, Equals, bgpIpPrefixLists[0].EdgeBgpIpPrefixList.ID)

// Delete
err = bgpIpPrefixLists[0].Delete()
check.Assert(err, IsNil)

// Try to get once again and ensure it is not there
notFoundByName, err := edge.GetBgpIpPrefixListByName(bgpIpPrefixList.Name)
check.Assert(err, NotNil)
check.Assert(notFoundByName, IsNil)

notFoundById, err := edge.GetBgpIpPrefixListById(bgpIpPrefix.EdgeBgpIpPrefixList.ID)
check.Assert(err, NotNil)
check.Assert(notFoundById, IsNil)

}

func switchEdgeGatewayDedication(edge *NsxtEdgeGateway, isDedicated bool) error {
edge.EdgeGateway.EdgeGatewayUplinks[0].Dedicated = isDedicated
_, err := edge.Update(edge.EdgeGateway)

return err
}
1 change: 1 addition & 0 deletions govcd/openapi_endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ var endpointMinApiVersions = map[string]string{
types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointSSLCertificateLibraryOld: "35.0", // VCD 10.2+ and deprecated from 10.3
types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointVdcGroupsDfwRules: "35.0", // VCD 10.2+
types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointNetworkContextProfiles: "35.0", // VCD 10.2+
types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeBgpConfigPrefixLists: "35.0", // VCD 10.2+
}

// elevateNsxtNatRuleApiVersion helps to elevate API version to consume newer NSX-T NAT Rule features
Expand Down
2 changes: 2 additions & 0 deletions types/v56/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,8 @@ const (
OpenApiEndpointSecurityTags = "securityTags"
OpenApiEndpointNsxtRouteAdvertisement = "edgeGateways/%s/routing/advertisement"

OpenApiEndpointEdgeBgpConfigPrefixLists = "edgeGateways/%s/routing/bgp/prefixLists/" // '%s' is NSX-T Edge gateway ID

// NSX-T ALB related endpoints

OpenApiEndpointAlbController = "loadBalancer/controllers/"
Expand Down
Loading