Skip to content

Commit 744588a

Browse files
authored
Add support for NSX-T Edge Gateway BGP Neighbor configuration (#489)
1 parent 43c1890 commit 744588a

6 files changed

+403
-3
lines changed

.changes/v2.16.0/489-features.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
* Add support for managing NSX-T Edge Gateway BGP Neighbor. It is done by adding types `EdgeBgpNeighbor` and
2+
`types.EdgeBgpNeighbor` with functions `CreateBgpNeighbor`, `GetAllBgpNeighbors`,
3+
`GetBgpNeighborByIp`, `GetBgpNeighborById`, `Update` and `Delete` [GH-489]
+239
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
/*
2+
* Copyright 2022 VMware, Inc. All rights reserved. Licensed under the Apache v2 License.
3+
*/
4+
5+
package govcd
6+
7+
import (
8+
"fmt"
9+
"net/url"
10+
11+
"github.com/vmware/go-vcloud-director/v2/types/v56"
12+
)
13+
14+
// EdgeBgpNeighbor represents NSX-T Edge Gateway BGP Neighbor
15+
type EdgeBgpNeighbor struct {
16+
EdgeBgpNeighbor *types.EdgeBgpNeighbor
17+
client *Client
18+
// edgeGatewayId is stored for usage in EdgeBgpNeighbor receiver functions
19+
edgeGatewayId string
20+
}
21+
22+
// CreateBgpNeighbor creates BGP Neighbor with the given configuration
23+
func (egw *NsxtEdgeGateway) CreateBgpNeighbor(bgpNeighborConfig *types.EdgeBgpNeighbor) (*EdgeBgpNeighbor, error) {
24+
client := egw.client
25+
endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeBgpNeighbor
26+
apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint)
27+
if err != nil {
28+
return nil, err
29+
}
30+
31+
// Insert Edge Gateway ID into endpoint path
32+
urlRef, err := client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, egw.EdgeGateway.ID))
33+
if err != nil {
34+
return nil, err
35+
}
36+
37+
returnObject := &EdgeBgpNeighbor{
38+
client: egw.client,
39+
edgeGatewayId: egw.EdgeGateway.ID,
40+
EdgeBgpNeighbor: &types.EdgeBgpNeighbor{},
41+
}
42+
43+
task, err := client.OpenApiPostItemAsync(apiVersion, urlRef, nil, bgpNeighborConfig)
44+
if err != nil {
45+
return nil, fmt.Errorf("error creating NSX-T Edge Gateway BGP Neighbor: %s", err)
46+
}
47+
48+
err = task.WaitTaskCompletion()
49+
if err != nil {
50+
return nil, fmt.Errorf("error creating NSX-T Edge Gateway BGP Neighbor: %s", err)
51+
}
52+
53+
// API has problems therefore explicit manual handling is required to lookup newly created object
54+
// VCD 10.2 -> no ID for newly created object is returned at all
55+
// VCD 10.3 -> `Details` field in task contains ID of newly created object
56+
// To cover all cases this code will at first look for ID in `Details` field and fall back to
57+
// lookup by name if `Details` field is empty.
58+
//
59+
// The drawback of this is that it is possible to create duplicate records with the same name on VCDs that don't
60+
// return IDs, but there is no better way for VCD versions that don't return API code
61+
62+
bgpNeighborId := task.Task.Details
63+
if bgpNeighborId != "" {
64+
getUrlRef, err := client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, egw.EdgeGateway.ID), bgpNeighborId)
65+
if err != nil {
66+
return nil, err
67+
}
68+
err = client.OpenApiGetItem(apiVersion, getUrlRef, nil, returnObject.EdgeBgpNeighbor, nil)
69+
if err != nil {
70+
return nil, fmt.Errorf("error retrieving NSX-T Edge Gateway BGP Neighbor after creation: %s", err)
71+
}
72+
} else {
73+
// ID after object creation was not returned therefore retrieving the entity by Name to lookup ID
74+
// This has a risk of duplicate items, but is the only way to find the object when ID is not returned
75+
bgpNeighbor, err := egw.GetBgpNeighborByIp(bgpNeighborConfig.NeighborAddress)
76+
if err != nil {
77+
return nil, fmt.Errorf("error retrieving NSX-T Edge Gateway BGP Neighbor after creation: %s", err)
78+
}
79+
returnObject = bgpNeighbor
80+
}
81+
82+
return returnObject, nil
83+
}
84+
85+
// GetAllBgpNeighbors retrieves all BGP Neighbors with an optional filter
86+
func (egw *NsxtEdgeGateway) GetAllBgpNeighbors(queryParameters url.Values) ([]*EdgeBgpNeighbor, error) {
87+
queryParams := copyOrNewUrlValues(queryParameters)
88+
89+
client := egw.client
90+
endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeBgpNeighbor
91+
apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint)
92+
if err != nil {
93+
return nil, err
94+
}
95+
96+
// Insert Edge Gateway ID into endpoint path
97+
urlRef, err := client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, egw.EdgeGateway.ID))
98+
if err != nil {
99+
return nil, err
100+
}
101+
102+
typeResponses := []*types.EdgeBgpNeighbor{{}}
103+
err = client.OpenApiGetAllItems(apiVersion, urlRef, queryParams, &typeResponses, nil)
104+
if err != nil {
105+
return nil, err
106+
}
107+
108+
wrappedResponses := make([]*EdgeBgpNeighbor, len(typeResponses))
109+
for sliceIndex := range typeResponses {
110+
wrappedResponses[sliceIndex] = &EdgeBgpNeighbor{
111+
EdgeBgpNeighbor: typeResponses[sliceIndex],
112+
client: client,
113+
edgeGatewayId: egw.EdgeGateway.ID,
114+
}
115+
}
116+
117+
return wrappedResponses, nil
118+
}
119+
120+
// GetBgpNeighborByIp retrieves BGP Neighbor by Neighbor IP address
121+
// It is meant to retrieve exactly one entry:
122+
// * Will fail if more than one entry with the same Neighbor IP found (should not happen as uniqueness is
123+
// enforced by API)
124+
// * Will return an error containing `ErrorEntityNotFound` if no entries are found
125+
//
126+
// Note. API does not support filtering by 'neighborIpAddress' field therefore filtering is performed on client
127+
// side
128+
func (egw *NsxtEdgeGateway) GetBgpNeighborByIp(neighborIpAddress string) (*EdgeBgpNeighbor, error) {
129+
if neighborIpAddress == "" {
130+
return nil, fmt.Errorf("neighborIpAddress cannot be empty")
131+
}
132+
133+
allBgpNeighbors, err := egw.GetAllBgpNeighbors(nil)
134+
if err != nil {
135+
return nil, fmt.Errorf("error retrieving NSX-T Edge Gateway BGP Neighbor: %s", err)
136+
}
137+
138+
var filteredBgpNeighbors []*EdgeBgpNeighbor
139+
for _, bgpNeighbor := range allBgpNeighbors {
140+
if bgpNeighbor.EdgeBgpNeighbor.NeighborAddress == neighborIpAddress {
141+
filteredBgpNeighbors = append(filteredBgpNeighbors, bgpNeighbor)
142+
}
143+
}
144+
145+
if len(filteredBgpNeighbors) > 1 {
146+
return nil, fmt.Errorf("more than one NSX-T Edge Gateway BGP Neighbor found with IP Address '%s'", neighborIpAddress)
147+
}
148+
149+
if len(filteredBgpNeighbors) == 0 {
150+
return nil, fmt.Errorf("%s: no NSX-T Edge Gateway BGP Neighbor found with IP Address '%s'", ErrorEntityNotFound, neighborIpAddress)
151+
}
152+
153+
return filteredBgpNeighbors[0], nil
154+
}
155+
156+
// GetBgpNeighborById retrieves BGP Neighbor By ID
157+
func (egw *NsxtEdgeGateway) GetBgpNeighborById(id string) (*EdgeBgpNeighbor, error) {
158+
if id == "" {
159+
return nil, fmt.Errorf("id is required")
160+
}
161+
162+
client := egw.client
163+
endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeBgpNeighbor
164+
apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint)
165+
if err != nil {
166+
return nil, err
167+
}
168+
169+
// Insert Edge Gateway ID into endpoint path
170+
urlRef, err := client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, egw.EdgeGateway.ID), id)
171+
if err != nil {
172+
return nil, err
173+
}
174+
175+
returnObject := &EdgeBgpNeighbor{
176+
client: egw.client,
177+
edgeGatewayId: egw.EdgeGateway.ID,
178+
EdgeBgpNeighbor: &types.EdgeBgpNeighbor{},
179+
}
180+
181+
err = client.OpenApiGetItem(apiVersion, urlRef, nil, returnObject.EdgeBgpNeighbor, nil)
182+
if err != nil {
183+
return nil, fmt.Errorf("error retrieving NSX-T Edge Gateway BGP Neighbor: %s", err)
184+
}
185+
186+
return returnObject, nil
187+
}
188+
189+
// Update updates existing BGP Neighbor with new configuration and returns it
190+
func (bgpNeighbor *EdgeBgpNeighbor) Update(bgpNeighborConfig *types.EdgeBgpNeighbor) (*EdgeBgpNeighbor, error) {
191+
client := bgpNeighbor.client
192+
endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeBgpNeighbor
193+
apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint)
194+
if err != nil {
195+
return nil, err
196+
}
197+
198+
// Insert Edge Gateway ID into endpoint path
199+
urlRef, err := client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, bgpNeighbor.edgeGatewayId), bgpNeighborConfig.ID)
200+
if err != nil {
201+
return nil, err
202+
}
203+
204+
returnObject := &EdgeBgpNeighbor{
205+
client: bgpNeighbor.client,
206+
edgeGatewayId: bgpNeighbor.edgeGatewayId,
207+
EdgeBgpNeighbor: &types.EdgeBgpNeighbor{},
208+
}
209+
210+
err = client.OpenApiPutItem(apiVersion, urlRef, nil, bgpNeighborConfig, returnObject.EdgeBgpNeighbor, nil)
211+
if err != nil {
212+
return nil, fmt.Errorf("error setting NSX-T Edge Gateway BGP Neighbor: %s", err)
213+
}
214+
215+
return returnObject, nil
216+
}
217+
218+
// Delete deletes existing BGP Neighbor
219+
func (bgpNeighbor *EdgeBgpNeighbor) Delete() error {
220+
client := bgpNeighbor.client
221+
endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeBgpNeighbor
222+
apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint)
223+
if err != nil {
224+
return err
225+
}
226+
227+
// Insert Edge Gateway ID into endpoint path
228+
urlRef, err := client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, bgpNeighbor.edgeGatewayId), bgpNeighbor.EdgeBgpNeighbor.ID)
229+
if err != nil {
230+
return err
231+
}
232+
233+
err = client.OpenApiDeleteItem(apiVersion, urlRef, nil, nil)
234+
if err != nil {
235+
return fmt.Errorf("error deleting NSX-T Edge Gateway BGP Neighbor: %s", err)
236+
}
237+
238+
return nil
239+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
//go:build network || nsxt || functional || openapi || ALL
2+
// +build network nsxt functional openapi ALL
3+
4+
package govcd
5+
6+
import (
7+
"github.com/vmware/go-vcloud-director/v2/types/v56"
8+
. "gopkg.in/check.v1"
9+
)
10+
11+
func (vcd *TestVCD) Test_NsxEdgeBgpNeighbor(check *C) {
12+
skipNoNsxtConfiguration(vcd, check)
13+
skipOpenApiEndpointTest(vcd, check, types.OpenApiPathVersion1_0_0+types.OpenApiEndpointEdgeBgpNeighbor)
14+
15+
org, err := vcd.client.GetOrgByName(vcd.config.VCD.Org)
16+
check.Assert(err, IsNil)
17+
nsxtVdc, err := org.GetVDCByName(vcd.config.VCD.Nsxt.Vdc, false)
18+
check.Assert(err, IsNil)
19+
edge, err := nsxtVdc.GetNsxtEdgeGatewayByName(vcd.config.VCD.Nsxt.EdgeGateway)
20+
check.Assert(err, IsNil)
21+
22+
// Switch Edge Gateway to use dedicated uplink for the time of this test and then turn it off
23+
err = switchEdgeGatewayDedication(edge, true) // Turn on Dedicated Tier 0 gateway
24+
check.Assert(err, IsNil)
25+
defer switchEdgeGatewayDedication(edge, false) // Turn off Dedicated Tier 0 gateway
26+
27+
// Create a new BGP IP Neighbor
28+
bgpNeighbor := &types.EdgeBgpNeighbor{
29+
NeighborAddress: "11.11.11.11",
30+
RemoteASNumber: "64123",
31+
KeepAliveTimer: 80,
32+
HoldDownTimer: 241,
33+
NeighborPassword: "iQuee-ph2phe",
34+
AllowASIn: true,
35+
GracefulRestartMode: "HELPER_ONLY",
36+
IpAddressTypeFiltering: "DISABLED",
37+
}
38+
39+
createdBgpNeighbor, err := edge.CreateBgpNeighbor(bgpNeighbor)
40+
check.Assert(err, IsNil)
41+
check.Assert(createdBgpNeighbor, NotNil)
42+
43+
// Get all BGP Neighbors
44+
BgpNeighbors, err := edge.GetAllBgpNeighbors(nil)
45+
check.Assert(err, IsNil)
46+
check.Assert(BgpNeighbors, NotNil)
47+
check.Assert(len(BgpNeighbors), Equals, 1)
48+
check.Assert(BgpNeighbors[0].EdgeBgpNeighbor.NeighborAddress, Equals, bgpNeighbor.NeighborAddress)
49+
50+
// Get BGP Neighbor By Neighbor IP Address
51+
bgpNeighborByIp, err := edge.GetBgpNeighborByIp(bgpNeighbor.NeighborAddress)
52+
check.Assert(err, IsNil)
53+
check.Assert(bgpNeighborByIp, NotNil)
54+
55+
// Get BGP Neighbor By Id
56+
bgpNeighborById, err := edge.GetBgpNeighborById(createdBgpNeighbor.EdgeBgpNeighbor.ID)
57+
check.Assert(err, IsNil)
58+
check.Assert(bgpNeighborById, NotNil)
59+
60+
// Update BGP Neighbor
61+
bgpNeighbor.NeighborAddress = "12.12.12.12"
62+
bgpNeighbor.ID = BgpNeighbors[0].EdgeBgpNeighbor.ID
63+
64+
updatedBgpNeighbor, err := BgpNeighbors[0].Update(bgpNeighbor)
65+
check.Assert(err, IsNil)
66+
check.Assert(updatedBgpNeighbor, NotNil)
67+
68+
check.Assert(updatedBgpNeighbor.EdgeBgpNeighbor.ID, Equals, BgpNeighbors[0].EdgeBgpNeighbor.ID)
69+
70+
// Delete BGP Neighbor
71+
err = BgpNeighbors[0].Delete()
72+
check.Assert(err, IsNil)
73+
74+
// Try to get deleted BGP Neighbor once again and ensure it is not there
75+
notFoundByIp, err := edge.GetBgpNeighborByIp(bgpNeighbor.NeighborAddress)
76+
check.Assert(err, NotNil)
77+
check.Assert(notFoundByIp, IsNil)
78+
79+
notFoundById, err := edge.GetBgpNeighborById(createdBgpNeighbor.EdgeBgpNeighbor.ID)
80+
check.Assert(err, NotNil)
81+
check.Assert(notFoundById, IsNil)
82+
83+
}

govcd/openapi_endpoints.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,10 @@ var endpointMinApiVersions = map[string]string{
6969
types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointSSLCertificateLibraryOld: "35.0", // VCD 10.2+ and deprecated from 10.3
7070
types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointVdcGroupsDfwRules: "35.0", // VCD 10.2+
7171
types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointNetworkContextProfiles: "35.0", // VCD 10.2+
72-
types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeBgpConfigPrefixLists: "35.0", // VCD 10.2+
73-
types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeBgpConfig: "35.0", // VCD 10.2+
72+
73+
types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeBgpNeighbor: "35.0", // VCD 10.2+
74+
types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeBgpConfigPrefixLists: "35.0", // VCD 10.2+
75+
types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeBgpConfig: "35.0", // VCD 10.2+
7476
}
7577

7678
// elevateNsxtNatRuleApiVersion helps to elevate API version to consume newer NSX-T NAT Rule features

types/v56/constants.go

+1
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,7 @@ const (
375375
OpenApiEndpointSecurityTags = "securityTags"
376376
OpenApiEndpointNsxtRouteAdvertisement = "edgeGateways/%s/routing/advertisement"
377377

378+
OpenApiEndpointEdgeBgpNeighbor = "edgeGateways/%s/routing/bgp/neighbors/" // '%s' is NSX-T Edge Gateway ID
378379
OpenApiEndpointEdgeBgpConfigPrefixLists = "edgeGateways/%s/routing/bgp/prefixLists/" // '%s' is NSX-T Edge Gateway ID
379380
OpenApiEndpointEdgeBgpConfig = "edgeGateways/%s/routing/bgp" // '%s' is NSX-T Edge Gateway ID
380381

0 commit comments

Comments
 (0)