From a5768997b09f122aab1d9302457fb51944fd1923 Mon Sep 17 00:00:00 2001 From: Dainius Serplis Date: Thu, 29 Jun 2023 12:01:33 +0300 Subject: [PATCH 1/5] Add support for NSX-T Edge Gateway Static Routes --- govcd/api_vcd_test.go | 26 +- govcd/nsxt_edgegateway_static_route.go | 282 ++++++++++++++++++++ govcd/nsxt_edgegateway_static_route_test.go | 104 ++++++++ govcd/openapi.go | 4 +- govcd/openapi_endpoints.go | 1 + types/v56/constants.go | 1 + types/v56/nsxt_types.go | 50 ++++ 7 files changed, 454 insertions(+), 14 deletions(-) create mode 100644 govcd/nsxt_edgegateway_static_route.go create mode 100644 govcd/nsxt_edgegateway_static_route_test.go diff --git a/govcd/api_vcd_test.go b/govcd/api_vcd_test.go index ee778aa67..e729cf793 100644 --- a/govcd/api_vcd_test.go +++ b/govcd/api_vcd_test.go @@ -178,19 +178,19 @@ type TestConfig struct { VimServer string `yaml:"vimServer,omitempty"` LdapServer string `yaml:"ldapServer,omitempty"` Nsxt struct { - Manager string `yaml:"manager"` - Tier0router string `yaml:"tier0router"` - Tier0routerVrf string `yaml:"tier0routerVrf"` - NsxtDvpg string `yaml:"nsxtDvpg"` - GatewayQosProfile string `yaml:"gatewayQosProfile"` - Vdc string `yaml:"vdc"` - ExternalNetwork string `yaml:"externalNetwork"` - EdgeGateway string `yaml:"edgeGateway"` - NsxtImportSegment string `yaml:"nsxtImportSegment"` - VdcGroup string `yaml:"vdcGroup"` - VdcGroupEdgeGateway string `yaml:"vdcGroupEdgeGateway"` - NsxtEdgeCluster string `yaml:"nsxtEdgeCluster"` - + Manager string `yaml:"manager"` + Tier0router string `yaml:"tier0router"` + Tier0routerVrf string `yaml:"tier0routerVrf"` + NsxtDvpg string `yaml:"nsxtDvpg"` + GatewayQosProfile string `yaml:"gatewayQosProfile"` + Vdc string `yaml:"vdc"` + ExternalNetwork string `yaml:"externalNetwork"` + EdgeGateway string `yaml:"edgeGateway"` + NsxtImportSegment string `yaml:"nsxtImportSegment"` + VdcGroup string `yaml:"vdcGroup"` + VdcGroupEdgeGateway string `yaml:"vdcGroupEdgeGateway"` + NsxtEdgeCluster string `yaml:"nsxtEdgeCluster"` + RoutedNetwork string `yaml:"routedNetwork"` NsxtAlbControllerUrl string `yaml:"nsxtAlbControllerUrl"` NsxtAlbControllerUser string `yaml:"nsxtAlbControllerUser"` NsxtAlbControllerPassword string `yaml:"nsxtAlbControllerPassword"` diff --git a/govcd/nsxt_edgegateway_static_route.go b/govcd/nsxt_edgegateway_static_route.go new file mode 100644 index 000000000..3bba187db --- /dev/null +++ b/govcd/nsxt_edgegateway_static_route.go @@ -0,0 +1,282 @@ +/* + * 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" +) + +// NsxtEdgeGatewayStaticRoute represents NSX-T Edge Gateway Static Route +type NsxtEdgeGatewayStaticRoute struct { + NsxtEdgeGatewayStaticRoute *types.NsxtEdgeGatewayStaticRoute + client *Client + // edgeGatewayId is stored for usage in NsxtEdgeGatewayStaticRoute receiver functions + edgeGatewayId string +} + +// CreateStaticRoute based on type definition +func (egw *NsxtEdgeGateway) CreateStaticRoute(staticRouteConfig *types.NsxtEdgeGatewayStaticRoute) (*NsxtEdgeGatewayStaticRoute, error) { + client := egw.client + endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGatewayStaticRoutes + 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 := &NsxtEdgeGatewayStaticRoute{ + client: egw.client, + edgeGatewayId: egw.EdgeGateway.ID, + NsxtEdgeGatewayStaticRoute: &types.NsxtEdgeGatewayStaticRoute{}, + } + + // Non standard behavior of entity - `Details` field in task contains ID of newly created object while the Owner is Edge Gateway + task, err := client.OpenApiPostItemAsync(apiVersion, urlRef, nil, staticRouteConfig) + if err != nil { + return nil, fmt.Errorf("error creating NSX-T Edge Gateway Static Route: %s", err) + } + + err = task.WaitTaskCompletion() + if err != nil { + return nil, fmt.Errorf("error creating NSX-T Edge Gateway Static Route: %s", err) + } + + // API is not consistent across different versions therefore explicit manual handling is + // required to lookup newly created object + // + // VCD 10.4.1 -> 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 + // VCD versions that don't return IDs, but there is no better way for VCD versions that don't + // return IDs for created objects + + staticRouteId := task.Task.Details + if staticRouteId != "" { + getUrlRef, err := client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, egw.EdgeGateway.ID), staticRouteId) + if err != nil { + return nil, err + } + err = client.OpenApiGetItem(apiVersion, getUrlRef, nil, returnObject.NsxtEdgeGatewayStaticRoute, nil) + if err != nil { + return nil, fmt.Errorf("error retrieving NSX-T Edge Gateway Static Route after creation: %s", err) + } + } else { + + // ID was not present in response, therefore Static Route needs to be found manually. Using + // 'Name', 'Description' and 'NetworkCidr' for finding the entity. Duplicate entries can + // exist, but but it should be a good enough combination for finding unique entry until VCD API is fixed + allStaticRoutes, err := egw.GetAllStaticRoutes(nil) + if err != nil { + return nil, fmt.Errorf("error retrieving NSX-T Edge Gateway Static Route by CIDR after creation: %s", err) + } + + var foundStaticRoute bool + for _, singleStaticRoute := range allStaticRoutes { + if singleStaticRoute.NsxtEdgeGatewayStaticRoute.Name == staticRouteConfig.Name && + singleStaticRoute.NsxtEdgeGatewayStaticRoute.NetworkCidr == staticRouteConfig.NetworkCidr && + singleStaticRoute.NsxtEdgeGatewayStaticRoute.Description == staticRouteConfig.Description { + foundStaticRoute = true + returnObject = singleStaticRoute + } + } + + if !foundStaticRoute { + return nil, fmt.Errorf("error finding Static Route after creation by Name '%s', NetworkCidr '%s', Description '%s'", + staticRouteConfig.Name, staticRouteConfig.NetworkCidr, staticRouteConfig.Description) + } + + } + + return returnObject, nil +} + +// GetAllStaticRoutes retrieves all Static Routes for a particular NSX-T Edge Gateway +func (egw *NsxtEdgeGateway) GetAllStaticRoutes(queryParameters url.Values) ([]*NsxtEdgeGatewayStaticRoute, error) { + queryParams := copyOrNewUrlValues(queryParameters) + + client := egw.client + endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGatewayStaticRoutes + 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.NsxtEdgeGatewayStaticRoute{{}} + err = client.OpenApiGetAllItems(apiVersion, urlRef, queryParams, &typeResponses, nil) + if err != nil { + return nil, err + } + + wrappedResponses := make([]*NsxtEdgeGatewayStaticRoute, len(typeResponses)) + for sliceIndex := range typeResponses { + wrappedResponses[sliceIndex] = &NsxtEdgeGatewayStaticRoute{ + NsxtEdgeGatewayStaticRoute: typeResponses[sliceIndex], + client: client, + edgeGatewayId: egw.EdgeGateway.ID, + } + } + + return wrappedResponses, nil +} + +// GetStaticRouteByNetworkCidr retrieves Static Route by network CIDR +// +// Note. It will return an error if more than one items is found +func (egw *NsxtEdgeGateway) GetStaticRouteByNetworkCidr(networkCidr string) (*NsxtEdgeGatewayStaticRoute, error) { + if networkCidr == "" { + return nil, fmt.Errorf("cidr cannot be empty") + } + + allStaticRoutes, err := egw.GetAllStaticRoutes(nil) + if err != nil { + return nil, fmt.Errorf("error retrieving NSX-T Edge Gateway Static Route: %s", err) + } + + filteredByNetworkCidr := make([]*NsxtEdgeGatewayStaticRoute, 0) + // First - filter by name + for _, sr := range allStaticRoutes { + if sr.NsxtEdgeGatewayStaticRoute.NetworkCidr == networkCidr { + filteredByNetworkCidr = append(filteredByNetworkCidr, sr) + } + } + + singleResult, err := oneOrError("networkCidr", networkCidr, filteredByNetworkCidr) + if err != nil { + return nil, err + } + + return singleResult, nil +} + +// GetStaticRouteByName retrieves Static Route by name +// +// Note. It will return an error if more than one items is found +func (egw *NsxtEdgeGateway) GetStaticRouteByName(name string) (*NsxtEdgeGatewayStaticRoute, error) { + if name == "" { + return nil, fmt.Errorf("cidr cannot be empty") + } + + allStaticRoutes, err := egw.GetAllStaticRoutes(nil) + if err != nil { + return nil, fmt.Errorf("error retrieving NSX-T Edge Gateway Static Route: %s", err) + } + + filteredByNetworkName := make([]*NsxtEdgeGatewayStaticRoute, 0) + // First - filter by name + for _, sr := range allStaticRoutes { + if sr.NsxtEdgeGatewayStaticRoute.Name == name { + filteredByNetworkName = append(filteredByNetworkName, sr) + } + } + + singleResult, err := oneOrError("name", name, filteredByNetworkName) + if err != nil { + return nil, err + } + + return singleResult, nil +} + +// GetStaticRouteById retrieves Static Route by given ID +func (egw *NsxtEdgeGateway) GetStaticRouteById(id string) (*NsxtEdgeGatewayStaticRoute, error) { + if id == "" { + return nil, fmt.Errorf("id is required") + } + + client := egw.client + endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGatewayStaticRoutes + 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 := &NsxtEdgeGatewayStaticRoute{ + client: egw.client, + edgeGatewayId: egw.EdgeGateway.ID, + NsxtEdgeGatewayStaticRoute: &types.NsxtEdgeGatewayStaticRoute{}, + } + + err = client.OpenApiGetItem(apiVersion, urlRef, nil, returnObject.NsxtEdgeGatewayStaticRoute, nil) + if err != nil { + return nil, fmt.Errorf("error retrieving NSX-T Edge Gateway Static Route: %s", err) + } + + return returnObject, nil +} + +// Update Static Route +func (staticRoute *NsxtEdgeGatewayStaticRoute) Update(StaticRouteConfig *types.NsxtEdgeGatewayStaticRoute) (*NsxtEdgeGatewayStaticRoute, error) { + client := staticRoute.client + endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGatewayStaticRoutes + 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, staticRoute.edgeGatewayId), StaticRouteConfig.ID) + if err != nil { + return nil, err + } + + returnObject := &NsxtEdgeGatewayStaticRoute{ + client: staticRoute.client, + edgeGatewayId: staticRoute.edgeGatewayId, + NsxtEdgeGatewayStaticRoute: &types.NsxtEdgeGatewayStaticRoute{}, + } + + err = client.OpenApiPutItem(apiVersion, urlRef, nil, StaticRouteConfig, returnObject.NsxtEdgeGatewayStaticRoute, nil) + if err != nil { + return nil, fmt.Errorf("error setting NSX-T Edge Gateway Static Route: %s", err) + } + + return returnObject, nil +} + +// Delete Static Route +func (staticRoute *NsxtEdgeGatewayStaticRoute) Delete() error { + client := staticRoute.client + endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGatewayStaticRoutes + apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) + if err != nil { + return err + } + + // Insert Edge Gateway ID into endpoint path + urlRef, err := client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, staticRoute.edgeGatewayId), staticRoute.NsxtEdgeGatewayStaticRoute.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 Static Route: %s", err) + } + + return nil +} diff --git a/govcd/nsxt_edgegateway_static_route_test.go b/govcd/nsxt_edgegateway_static_route_test.go new file mode 100644 index 000000000..ee21a934f --- /dev/null +++ b/govcd/nsxt_edgegateway_static_route_test.go @@ -0,0 +1,104 @@ +//go: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_NsxEdgeStaticRoute(check *C) { + skipNoNsxtConfiguration(vcd, check) + skipOpenApiEndpointTest(vcd, check, types.OpenApiPathVersion1_0_0+types.OpenApiEndpointEdgeGatewayStaticRoutes) + + 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 Org VDC routed network + orgVdcNet, err := nsxtVdc.GetOpenApiOrgVdcNetworkByName(vcd.config.VCD.Nsxt.RoutedNetwork) + check.Assert(err, IsNil) + check.Assert(orgVdcNet, NotNil) + + staticRouteConfig := &types.NsxtEdgeGatewayStaticRoute{ + Name: check.TestName(), + Description: "description", + NetworkCidr: "1.1.1.0/24", + NextHops: []types.NsxtEdgeGatewayStaticRouteNextHops{ + { + IPAddress: orgVdcNet.OpenApiOrgVdcNetwork.Subnets.Values[0].Gateway, + AdminDistance: 4, + Scope: &types.NsxtEdgeGatewayStaticRouteNextHopScope{ + ID: orgVdcNet.OpenApiOrgVdcNetwork.ID, + ScopeType: "NETWORK", + }, + }, + }, + } + + staticRoute, err := edge.CreateStaticRoute(staticRouteConfig) + check.Assert(err, IsNil) + check.Assert(staticRoute, NotNil) + + // Get all BGP IP Prefix Lists + staticRouteList, err := edge.GetAllStaticRoutes(nil) + check.Assert(err, IsNil) + check.Assert(staticRouteList, NotNil) + check.Assert(len(staticRouteList), Equals, 1) + check.Assert(staticRouteList[0].NsxtEdgeGatewayStaticRoute.Name, Equals, staticRoute.NsxtEdgeGatewayStaticRoute.Name) + + // Get By Name + staticRouteByName, err := edge.GetStaticRouteByName(staticRoute.NsxtEdgeGatewayStaticRoute.Name) + check.Assert(err, IsNil) + check.Assert(staticRouteByName, NotNil) + + // Get By Id + staticRouteById, err := edge.GetStaticRouteById(staticRoute.NsxtEdgeGatewayStaticRoute.ID) + check.Assert(err, IsNil) + check.Assert(staticRouteById, NotNil) + + // Get By Network CIDR + staticRouteByNetworkCidr, err := edge.GetStaticRouteByNetworkCidr(staticRoute.NsxtEdgeGatewayStaticRoute.NetworkCidr) + check.Assert(err, IsNil) + check.Assert(staticRouteByNetworkCidr, NotNil) + + // Update + staticRouteConfig.Name = check.TestName() + "-updated" + staticRouteConfig.Description = "test-description-updated" + staticRouteConfig.ID = staticRouteByNetworkCidr.NsxtEdgeGatewayStaticRoute.ID + + // staticRoute + updatedStaticRoute, err := staticRoute.Update(staticRouteConfig) + check.Assert(err, IsNil) + check.Assert(updatedStaticRoute, NotNil) + + check.Assert(updatedStaticRoute.NsxtEdgeGatewayStaticRoute.ID, Equals, staticRouteByName.NsxtEdgeGatewayStaticRoute.ID) + + // Delete + err = staticRoute.Delete() + check.Assert(err, IsNil) + + // Try to get once again and ensure it is not there + notFoundByName, err := edge.GetStaticRouteByName(staticRoute.NsxtEdgeGatewayStaticRoute.Name) + check.Assert(err, NotNil) + check.Assert(ContainsNotFound(err), Equals, true) + check.Assert(notFoundByName, IsNil) + + notFoundById, err := edge.GetStaticRouteById(staticRoute.NsxtEdgeGatewayStaticRoute.ID) + check.Assert(err, NotNil) + check.Assert(ContainsNotFound(err), Equals, true) + check.Assert(notFoundById, IsNil) + + notFoundByCidr, err := edge.GetStaticRouteByNetworkCidr(staticRoute.NsxtEdgeGatewayStaticRoute.NetworkCidr) + check.Assert(err, NotNil) + check.Assert(ContainsNotFound(err), Equals, true) + check.Assert(notFoundByCidr, IsNil) +} diff --git a/govcd/openapi.go b/govcd/openapi.go index 77aa2c847..0e83aaf12 100644 --- a/govcd/openapi.go +++ b/govcd/openapi.go @@ -708,7 +708,9 @@ func (client *Client) newOpenApiRequest(apiVersion string, params url.Values, me } // Inject JSON mime type - req.Header.Add("Content-Type", types.JSONMime) + if req.Header.Get("Content-Type") == "" { + req.Header.Add("Content-Type", types.JSONMime) + } setHttpUserAgent(client.UserAgent, req) diff --git a/govcd/openapi_endpoints.go b/govcd/openapi_endpoints.go index e95cc1658..c12a48428 100644 --- a/govcd/openapi_endpoints.go +++ b/govcd/openapi_endpoints.go @@ -38,6 +38,7 @@ var endpointMinApiVersions = map[string]string{ types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointQosProfiles: "36.2", // VCD 10.3.2+ (NSX-T only) types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGatewayQos: "36.2", // VCD 10.3.2+ (NSX-T only) types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGatewayDhcpForwarder: "36.1", // VCD 10.3.1+ (NSX-T only) + types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGatewayStaticRoutes: "37.0", // VCD 10.4.0+ (NSX-T only) types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGateways: "34.0", types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGatewayUsedIpAddresses: "34.0", diff --git a/types/v56/constants.go b/types/v56/constants.go index 0631a282a..9f3247116 100644 --- a/types/v56/constants.go +++ b/types/v56/constants.go @@ -373,6 +373,7 @@ const ( OpenApiEndpointEdgeGateways = "edgeGateways/" OpenApiEndpointEdgeGatewayQos = "edgeGateways/%s/qos" OpenApiEndpointEdgeGatewayDhcpForwarder = "edgeGateways/%s/dhcpForwarder" + OpenApiEndpointEdgeGatewayStaticRoutes = "edgeGateways/%s/routing/staticRoutes/" OpenApiEndpointEdgeGatewayUsedIpAddresses = "edgeGateways/%s/usedIpAddresses" OpenApiEndpointNsxtFirewallRules = "edgeGateways/%s/firewall/rules" OpenApiEndpointFirewallGroups = "firewallGroups/" diff --git a/types/v56/nsxt_types.go b/types/v56/nsxt_types.go index 7da5a4c80..64cee76fc 100644 --- a/types/v56/nsxt_types.go +++ b/types/v56/nsxt_types.go @@ -1705,3 +1705,53 @@ type VcenterImportableDvpg struct { VirtualCenter *OpenApiReference `json:"virtualCenter"` Vlan string `json:"vlan"` } + +// NsxtEdgeGatewayStaticRoute provides configuration structure for NSX-T Edge Gateway static route +// configuration +type NsxtEdgeGatewayStaticRoute struct { + // ID of this static route. On updates, the ID is required for the object, while for create a + // new ID will be generated. This ID is not a VCD URN + ID string `json:"id,omitempty"` + Name string `json:"name"` + // Description + Description string `json:"description,omitempty"` + // NetworkCidr contains network prefix in CIDR format. Both IPv4 and IPv6 formats are supported + NetworkCidr string `json:"networkCidr"` + // NextHops contains the list of next hops to use within the static route. List must contain at + // least one valid next hop + NextHops []NsxtEdgeGatewayStaticRouteNextHops `json:"nextHops"` + + // SystemOwned contains a read-only flag whether this static route is managed by the system + SystemOwned *bool `json:"systemOwned,omitempty"` + // Version 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 string `json:"version,omitempty"` +} + +// NsxtEdgeGatewayStaticRouteNextHops sets one next hop entry for the list +type NsxtEdgeGatewayStaticRouteNextHops struct { + // AdminDistance for the next hop + AdminDistance int `json:"adminDistance"` + // IPAddress for next hop gateway IP Address for the static route. + IPAddress string `json:"ipAddress"` + // Scope holds a reference to an entity where the next hop of a static route is reachable. In + // general, the reference should be an org vDC network or segment backed external network, but + // scope could also reference a SYSTEM_OWNED entity if the next hop is configured outside of + // VCD. + Scope *NsxtEdgeGatewayStaticRouteNextHopScope `json:"scope,omitempty"` +} + +// NsxtEdgeGatewayStaticRouteNextHopScope for a single NsxtEdgeGatewayStaticRouteNextHops entry +type NsxtEdgeGatewayStaticRouteNextHopScope struct { + // ID of this scoped entity. + ID string `json:"id"` + // Name of the scoped entity. + Name string `json:"name"` + // ScopeType of this entity. This can be an network or a system-owned entity if the static + // route is SYSTEM_OWNED. Supported types are: + // * NETWORK + // * SYSTEM_OWNED + ScopeType string `json:"scopeType"` +} From 5fbc9f09c17ad08e0d9641ed28b95f665805f5f4 Mon Sep 17 00:00:00 2001 From: Dainius Serplis Date: Fri, 30 Jun 2023 13:25:24 +0300 Subject: [PATCH 2/5] Add changelog Signed-off-by: Dainius Serplis --- .changes/v2.21.0/586-features.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changes/v2.21.0/586-features.md diff --git a/.changes/v2.21.0/586-features.md b/.changes/v2.21.0/586-features.md new file mode 100644 index 000000000..bf4d65766 --- /dev/null +++ b/.changes/v2.21.0/586-features.md @@ -0,0 +1,6 @@ +* Added support for NSX-T Edge Gateway Static Route configuration via types + `NsxtEdgeGatewayStaticRoute`, `types.NsxtEdgeGatewayStaticRoute` and methods + `NsxtEdgeGateway.CreateStaticRoute`, `NsxtEdgeGateway.GetAllStaticRoutes`, + `NsxtEdgeGateway.GetStaticRouteByNetworkCidr`, `NsxtEdgeGateway.GetStaticRouteByName`, + `NsxtEdgeGateway.GetStaticRouteById`, `NsxtEdgeGatewayStaticRoute.Update`, + `NsxtEdgeGatewayStaticRoute.Delete` [GH-586] From 4b5ccd408a1c5db193e5247e9e0b3a65453408f3 Mon Sep 17 00:00:00 2001 From: Dainius Serplis Date: Mon, 3 Jul 2023 07:43:10 +0300 Subject: [PATCH 3/5] Self review Signed-off-by: Dainius Serplis --- govcd/nsxt_edgegateway_static_route.go | 25 ++++++------------------- govcd/openapi.go | 4 +--- 2 files changed, 7 insertions(+), 22 deletions(-) diff --git a/govcd/nsxt_edgegateway_static_route.go b/govcd/nsxt_edgegateway_static_route.go index 3bba187db..a79967bd1 100644 --- a/govcd/nsxt_edgegateway_static_route.go +++ b/govcd/nsxt_edgegateway_static_route.go @@ -51,18 +51,9 @@ func (egw *NsxtEdgeGateway) CreateStaticRoute(staticRouteConfig *types.NsxtEdgeG return nil, fmt.Errorf("error creating NSX-T Edge Gateway Static Route: %s", err) } - // API is not consistent across different versions therefore explicit manual handling is - // required to lookup newly created object - // - // VCD 10.4.1 -> 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 - // VCD versions that don't return IDs, but there is no better way for VCD versions that don't - // return IDs for created objects - + // API does not return an ID for created object - we know that it is expected to be in + // task.Task.Details therefore attempt to find it, but if it is empty - look for an entity by a + // set of requested parameters staticRouteId := task.Task.Details if staticRouteId != "" { getUrlRef, err := client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, egw.EdgeGateway.ID), staticRouteId) @@ -74,13 +65,12 @@ func (egw *NsxtEdgeGateway) CreateStaticRoute(staticRouteConfig *types.NsxtEdgeG return nil, fmt.Errorf("error retrieving NSX-T Edge Gateway Static Route after creation: %s", err) } } else { - // ID was not present in response, therefore Static Route needs to be found manually. Using // 'Name', 'Description' and 'NetworkCidr' for finding the entity. Duplicate entries can // exist, but but it should be a good enough combination for finding unique entry until VCD API is fixed allStaticRoutes, err := egw.GetAllStaticRoutes(nil) if err != nil { - return nil, fmt.Errorf("error retrieving NSX-T Edge Gateway Static Route by CIDR after creation: %s", err) + return nil, fmt.Errorf("error retrieving NSX-T Edge Gateway Static Route after creation: %s", err) } var foundStaticRoute bool @@ -105,8 +95,6 @@ func (egw *NsxtEdgeGateway) CreateStaticRoute(staticRouteConfig *types.NsxtEdgeG // GetAllStaticRoutes retrieves all Static Routes for a particular NSX-T Edge Gateway func (egw *NsxtEdgeGateway) GetAllStaticRoutes(queryParameters url.Values) ([]*NsxtEdgeGatewayStaticRoute, error) { - queryParams := copyOrNewUrlValues(queryParameters) - client := egw.client endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGatewayStaticRoutes apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) @@ -121,7 +109,7 @@ func (egw *NsxtEdgeGateway) GetAllStaticRoutes(queryParameters url.Values) ([]*N } typeResponses := []*types.NsxtEdgeGatewayStaticRoute{{}} - err = client.OpenApiGetAllItems(apiVersion, urlRef, queryParams, &typeResponses, nil) + err = client.OpenApiGetAllItems(apiVersion, urlRef, queryParameters, &typeResponses, nil) if err != nil { return nil, err } @@ -152,7 +140,6 @@ func (egw *NsxtEdgeGateway) GetStaticRouteByNetworkCidr(networkCidr string) (*Ns } filteredByNetworkCidr := make([]*NsxtEdgeGatewayStaticRoute, 0) - // First - filter by name for _, sr := range allStaticRoutes { if sr.NsxtEdgeGatewayStaticRoute.NetworkCidr == networkCidr { filteredByNetworkCidr = append(filteredByNetworkCidr, sr) @@ -199,7 +186,7 @@ func (egw *NsxtEdgeGateway) GetStaticRouteByName(name string) (*NsxtEdgeGatewayS // GetStaticRouteById retrieves Static Route by given ID func (egw *NsxtEdgeGateway) GetStaticRouteById(id string) (*NsxtEdgeGatewayStaticRoute, error) { if id == "" { - return nil, fmt.Errorf("id is required") + return nil, fmt.Errorf("ID is required") } client := egw.client diff --git a/govcd/openapi.go b/govcd/openapi.go index 0e83aaf12..77aa2c847 100644 --- a/govcd/openapi.go +++ b/govcd/openapi.go @@ -708,9 +708,7 @@ func (client *Client) newOpenApiRequest(apiVersion string, params url.Values, me } // Inject JSON mime type - if req.Header.Get("Content-Type") == "" { - req.Header.Add("Content-Type", types.JSONMime) - } + req.Header.Add("Content-Type", types.JSONMime) setHttpUserAgent(client.UserAgent, req) From c393bcd3c2a8bb512282b0b47fb59adaf8056e98 Mon Sep 17 00:00:00 2001 From: Dainius Serplis Date: Mon, 10 Jul 2023 14:59:05 +0300 Subject: [PATCH 4/5] Address comment Signed-off-by: Dainius Serplis --- govcd/nsxt_edgegateway_static_route.go | 1 + 1 file changed, 1 insertion(+) diff --git a/govcd/nsxt_edgegateway_static_route.go b/govcd/nsxt_edgegateway_static_route.go index a79967bd1..9ab9ca582 100644 --- a/govcd/nsxt_edgegateway_static_route.go +++ b/govcd/nsxt_edgegateway_static_route.go @@ -80,6 +80,7 @@ func (egw *NsxtEdgeGateway) CreateStaticRoute(staticRouteConfig *types.NsxtEdgeG singleStaticRoute.NsxtEdgeGatewayStaticRoute.Description == staticRouteConfig.Description { foundStaticRoute = true returnObject = singleStaticRoute + break } } From 3a4d34a5c0d73d7cbaa7aa66e13f44865512b0ad Mon Sep 17 00:00:00 2001 From: Dainius Serplis Date: Wed, 12 Jul 2023 22:59:27 +0300 Subject: [PATCH 5/5] Address comment Signed-off-by: Dainius Serplis --- govcd/nsxt_edgegateway_static_route_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/govcd/nsxt_edgegateway_static_route_test.go b/govcd/nsxt_edgegateway_static_route_test.go index ee21a934f..63fe1f90e 100644 --- a/govcd/nsxt_edgegateway_static_route_test.go +++ b/govcd/nsxt_edgegateway_static_route_test.go @@ -21,7 +21,10 @@ func (vcd *TestVCD) Test_NsxEdgeStaticRoute(check *C) { // 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 + defer func() { + err = switchEdgeGatewayDedication(edge, false) + check.Assert(err, IsNil) + }() // Get Org VDC routed network orgVdcNet, err := nsxtVdc.GetOpenApiOrgVdcNetworkByName(vcd.config.VCD.Nsxt.RoutedNetwork)