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 BGP Configuration on NSX-T Edge Gateways #480

Merged
merged 15 commits into from
Jul 26, 2022
2 changes: 1 addition & 1 deletion .changes/v2.16.0/478-features.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
* Add NSX-T Edge Gateway methods `NsxtEdgeGateway.GetNsxtRouteAdvertisement`, `NsxtEdgeGateway.GetNsxtRouteAdvertisementWithContext`, `NsxtEdgeGateway.UpdateNsxtRouteAdvertisement`, `NsxtEdgeGateway.UpdateNsxtRouteAdvertisementWithContext`, `NsxtEdgeGateway.DeleteNsxtRouteAdvertisement` and `NsxtEdgeGateway.DeleteNsxtRouteAdvertisementWithContext` that allows to manage NSX-T route advertisement [GH-478]
* Add NSX-T Edge Gateway methods `NsxtEdgeGateway.GetNsxtRouteAdvertisement`, `NsxtEdgeGateway.GetNsxtRouteAdvertisementWithContext`, `NsxtEdgeGateway.UpdateNsxtRouteAdvertisement`, `NsxtEdgeGateway.UpdateNsxtRouteAdvertisementWithContext`, `NsxtEdgeGateway.DeleteNsxtRouteAdvertisement` and `NsxtEdgeGateway.DeleteNsxtRouteAdvertisementWithContext` that allows to manage NSX-T route advertisement [GH-478, GH-480]
5 changes: 5 additions & 0 deletions .changes/v2.16.0/480-features.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
* Add new methods `NsxtEdgeGateway.GetBgpConfiguration`, `NsxtEdgeGateway.UpdateBgpConfiguration`,
`NsxtEdgeGateway.DisableBgpConfiguration` for BGP Configuration management on NSX-T Edge Gateway
[GH-480]
* Add new structs `types.EdgeBgpConfig`, `types.EdgeBgpGracefulRestartConfig`,
`types.EdgeBgpConfigVersion` for BGP Configuration management on NSX-T Edge Gateway [GH-480]
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.16

require (
github.com/araddon/dateparse v0.0.0-20190622164848-0fb0a474d195
github.com/davecgh/go-spew v1.1.0
github.com/hashicorp/go-version v1.2.0
github.com/kr/pretty v0.2.1
github.com/peterhellberg/link v1.1.0
Expand Down
16 changes: 16 additions & 0 deletions govcd/catalog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"strings"
"time"

"github.com/davecgh/go-spew/spew"
"github.com/vmware/go-vcloud-director/v2/types/v56"
"github.com/vmware/go-vcloud-director/v2/util"
. "gopkg.in/check.v1"
Expand Down Expand Up @@ -838,6 +839,17 @@ func (vcd *TestVCD) Test_PublishToExternalOrganizations(check *C) {
check.Assert(err, IsNil)
check.Assert(adminOrg, NotNil)

// TODO - remove once VCD is fixed.
// Every Org update causes catalog publishing to be removed and therefore this test fails.
// Turning publishing on right before test to be sure it is tested and passes.
// VCD 10.2.0 <-> 10.3.3 have a bug that even though catalog publishing is enabled adminOrg.
fmt.Println("Overcomming VCD 10.2.0 <-> 10.3.3 bug - explicitly setting catalog sharing")
adminOrg.AdminOrg.OrgSettings.OrgGeneralSettings.CanPublishCatalogs = true
adminOrg.AdminOrg.OrgSettings.OrgGeneralSettings.CanPublishExternally = true
updatedAdminOrg, err := adminOrg.Update()
check.Assert(err, IsNil)
check.Assert(updatedAdminOrg, NotNil)

adminCatalog, err := adminOrg.CreateCatalog(catalogName, catalogDescription)
check.Assert(err, IsNil)
check.Assert(adminCatalog.AdminCatalog.Name, Equals, catalogName)
Expand Down Expand Up @@ -958,6 +970,10 @@ func (vcd *TestVCD) Test_CatalogQueryMediaList(check *C) {
check.Assert(medias, NotNil)

// Check that number of medias is 1
// Dump all media structures to easily identify leftover objects if number is not 1
if len(medias) > 1 {
spew.Dump(medias)
}
check.Assert(len(medias), Equals, 1)

// Check that media name is what it should be
Expand Down
5 changes: 5 additions & 0 deletions govcd/catalogitem_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

. "gopkg.in/check.v1"

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

Expand Down Expand Up @@ -250,6 +251,10 @@ func (vcd *TestVCD) Test_QueryVappTemplateList(check *C) {
check.Assert(vAppTemplates, NotNil)

// Check the number of vApp templates is one
// Dump all vApp template structures to easily identify leftover objects if number is not 1
if len(vAppTemplates) > 1 {
spew.Dump(vAppTemplates)
}
check.Assert(len(vAppTemplates), Equals, 1)

// Check the name of the vApp template is what it should be
Expand Down
6 changes: 6 additions & 0 deletions govcd/metadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,12 @@ func (vcd *TestVCD) Test_AddAndDeleteMetadataOnMediaRecord(check *C) {
metadata, err = mediaRecord.GetMetadata()
check.Assert(err, IsNil)
check.Assert(len(metadata.MetadataEntry), Equals, 0)

// Remove catalog item so far other tests don't fail
task, err := mediaRecord.Delete()
check.Assert(err, IsNil)
err = task.WaitTaskCompletion()
check.Assert(err, IsNil)
}

func (vcd *TestVCD) Test_MetadataOnAdminCatalogCRUD(check *C) {
Expand Down
15 changes: 15 additions & 0 deletions govcd/nsxt_alb_pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,21 @@ func setupAlbPoolPrerequisites(check *C, vcd *TestVCD) (*NsxtAlbController, *Nsx
Enabled: true,
}
enabledSettings, err := edge.UpdateAlbSettings(albSettingsConfig)
if err != nil {
fmt.Printf("# error occured while enabling ALB on Edge Gateway. Cleaning up Service Engine Group, ALB Cloud and ALB Controller: %s", err)
err2 := seGroup.Delete()
if err2 != nil {
fmt.Printf("# got error while cleaning up Service Engine Group: %s", err)
}
err2 = cloud.Delete()
if err2 != nil {
fmt.Printf("# got error while cleaning up ALB Cloud: %s", err)
}
err2 = controller.Delete()
if err2 != nil {
fmt.Printf("# got error while cleaning up ALB Controller: %s", err)
}
}
check.Assert(err, IsNil)
check.Assert(enabledSettings.Enabled, Equals, true)
PrependToCleanupList(check.TestName()+"-ALB-settings", "OpenApiEntityAlbSettingsDisable", edge.EdgeGateway.Name, check.TestName())
Expand Down
78 changes: 78 additions & 0 deletions govcd/nsxt_edgegateway_bgp_configuration.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* 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"
)

func (egw *NsxtEdgeGateway) GetBgpConfiguration() (*types.EdgeBgpConfig, error) {
client := egw.client
endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeBgpConfig
apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint)
if err != nil {
return nil, err
}

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

returnObject := &types.EdgeBgpConfig{}

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

return returnObject, nil
}

func (egw *NsxtEdgeGateway) UpdateBgpConfiguration(bgpConfig *types.EdgeBgpConfig) (*types.EdgeBgpConfig, error) {
client := egw.client
endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeBgpConfig
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
}

// Update of BGP config requires version to be specified. This function automatically handles it.
existingBgpConfig, err := egw.GetBgpConfiguration()
if err != nil {
return nil, fmt.Errorf("error getting NSX-T Edge Gateway BGP Configuration: %s", err)
}
bgpConfig.Version = existingBgpConfig.Version

returnObject := &types.EdgeBgpConfig{}

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

return returnObject, nil
}

func (egw *NsxtEdgeGateway) DisableBgpConfiguration() error {
// Get existring BGP configuration so that when disabling it - other settings remain as they are
bgpConfig, err := egw.GetBgpConfiguration()
if err != nil {
return fmt.Errorf("error retrieving BGP configuration: %s", err)
}
bgpConfig.Enabled = false

_, err = egw.UpdateBgpConfiguration(bgpConfig)
return err
}
70 changes: 70 additions & 0 deletions govcd/nsxt_edgegateway_bgp_configuration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//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"
)

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

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

// Get and store existing BGP configuration
bgpConfig, err := edge.GetBgpConfiguration()
check.Assert(err, IsNil)
check.Assert(bgpConfig, NotNil)

// Disable BGP
err = edge.DisableBgpConfiguration()
check.Assert(err, IsNil)

newBgpConfig := &types.EdgeBgpConfig{
Enabled: true,
Ecmp: true,
LocalASNumber: "65420",
GracefulRestart: &types.EdgeBgpGracefulRestartConfig{
StaleRouteTimer: 190,
RestartTimer: 600,
Mode: "HELPER_ONLY",
},
}

updatedBgpConfig, err := edge.UpdateBgpConfiguration(newBgpConfig)
check.Assert(err, IsNil)
check.Assert(updatedBgpConfig, NotNil)

newBgpConfig.Version = updatedBgpConfig.Version // Version is constantly iterated and cant be checked
check.Assert(updatedBgpConfig, DeepEquals, newBgpConfig)

// Check "disable" function which keeps the all fields the same, but sets "Enabled: false"
err = edge.DisableBgpConfiguration()
check.Assert(err, IsNil)

bgpConfigAfterDisabling, err := edge.GetBgpConfiguration()
check.Assert(err, IsNil)
check.Assert(bgpConfig, NotNil)
check.Assert(bgpConfigAfterDisabling.Enabled, Equals, false)

}

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/nsxt_route_advertisement.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package govcd

import (
"fmt"

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

Expand Down
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.OpenApiEndpointEdgeBgpConfig: "35.0", // VCD 10.2+
}

// elevateNsxtNatRuleApiVersion helps to elevate API version to consume newer NSX-T NAT Rule features
Expand Down
6 changes: 5 additions & 1 deletion govcd/tenant_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,11 @@ func getTenantContextHeader(tenantContext *TenantContext) map[string]string {
return nil
}
return map[string]string{
types.HeaderTenantContext: tenantContext.OrgId,
// Some versions of VCD do not like when URN is sent for Tenant context ID - they fail with
// 401 Unauthorized when such request is sent with URN formatted ID:
// * Fails with 401: urn:vcloud:org:6127c856-7315-46b8-b774-f2b8f1686c80
// * Works fine: 6127c856-7315-46b8-b774-f2b8f1686c80
types.HeaderTenantContext: extractUuid(tenantContext.OrgId),
types.HeaderAuthContext: tenantContext.OrgName,
}
}
Expand Down
7 changes: 7 additions & 0 deletions govcd/vapp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1442,6 +1442,13 @@ func (vcd *TestVCD) Test_AddNewVMFromMultiVmTemplate(check *C) {
err = task.WaitTaskCompletion()
check.Assert(err, IsNil)
check.Assert(task.Task.Status, Equals, "success")

// Remove catalog item so far other tests don't fail
catalogItem, err := catalog.GetCatalogItemByName(itemName, true)
check.Assert(err, IsNil)

err = catalogItem.Delete()
check.Assert(err, IsNil)
}

// Test_AddNewVMWithComputeCapacity creates a new VM in vApp with VM using compute capacity
Expand Down
6 changes: 6 additions & 0 deletions govcd/vm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,12 @@ func (vcd *TestVCD) Test_AnswerVmQuestion(check *C) {
err = vm.Refresh()
check.Assert(err, IsNil)
check.Assert(isMediaInjected(vm.VM.VirtualHardwareSection.Item), Equals, false)

// Remove catalog item so far other tests don't fail
task, err := media.Delete()
check.Assert(err, IsNil)
err = task.WaitTaskCompletion()
check.Assert(err, IsNil)
}

func (vcd *TestVCD) Test_VMChangeCPUCountWithCore(check *C) {
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"

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

// NSX-T ALB related endpoints

OpenApiEndpointAlbController = "loadBalancer/controllers/"
Expand Down
57 changes: 57 additions & 0 deletions types/v56/nsxt_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -1254,3 +1254,60 @@ type RouteAdvertisement struct {
// external network.
Subnets []string `json:"subnets"`
}

// EdgeBgpConfig defines BGP configuration on NSX-T Edge Gateways (Tier1 NSX-T Gateways)
type EdgeBgpConfig struct {
// A flag indicating whether BGP configuration is enabled or not.
Enabled bool `json:"enabled"`

// Ecmp A flag indicating whether ECMP is enabled or not.
Ecmp bool `json:"ecmp"`

// BGP AS (Autonomous system) number to advertise to BGP peers. BGP AS number can be specified
// in either ASPLAIN or ASDOT formats, like ASPLAIN format :- '65546', ASDOT format :- '1.10'.
//
// Read only if using a VRF-Lite backed external network.
LocalASNumber string `json:"localASNumber,omitempty"`

// BGP Graceful Restart configuration. Not specifying a value results in default bahavior.
//
// Read only if using a VRF-Lite backed external network.
GracefulRestart *EdgeBgpGracefulRestartConfig `json:"gracefulRestart,omitempty"`

// This property describes the current version of the entity. To prevent clients from
// overwriting each other's changes, update operations must include the version which can be
// obtained by issuing a GET operation. If the version number on an update call is missing, the
// operation will be rejected. This is only needed on update calls.
Version EdgeBgpConfigVersion `json:"version"`
}

// EdgeBgpGracefulRestartConfig describes current graceful restart configuration mode and timer for
// BGP configuration on an edge gateway.
type EdgeBgpGracefulRestartConfig struct {
// Mode describes Graceful Restart configuration Modes for BGP configuration on an edge gateway.
// HELPER_ONLY mode is the ability for a BGP speaker to indicate its ability to preserve
// forwarding state during BGP restart. GRACEFUL_RESTART mode is the ability of a BGP speaker to
// advertise its restart to its peers.
//
// DISABLE - Both graceful restart and helper modes are disabled.
// HELPER_ONLY - Only helper mode is enabled.
// GRACEFUL_AND_HELPER - Both graceful restart and helper modes are enabled.
//
// Possible values are: DISABLE , HELPER_ONLY , GRACEFUL_AND_HELPER
Mode string `json:"mode"`

// RestartTimer specifies maximum time taken (in seconds) for a BGP session to be established
// after a restart. If the session is not re-established within this timer, the receiving
// speaker will delete all the stale routes from that peer.
RestartTimer int `json:"restartTimer"`

// StaleRouteTimer defines maximum time (in seconds) before stale routes are removed when BGP
// restarts.
StaleRouteTimer int `json:"staleRouteTimer"`
}

// EdgeBgpConfigVersion is part of EdgeBgpConfig type and describes current version of the entity
// being modified
type EdgeBgpConfigVersion struct {
Version int `json:"version"`
}