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 NSX-T Edge Gateway DNS Configuration management #627

Merged
merged 18 commits into from
Nov 23, 2023
4 changes: 4 additions & 0 deletions .changes/v2.22.0/627-features.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
* Added `NsxtEdgeGateway` methods `GetDnsConfig` and `UpdateDnsConfig`. [GH-627]
* Added types `types.NsxtEdgeGatewayDns`, `types.NsxtDnsForwarderZoneConfig`
for creation and management of DNS forwarder configuration. [GH-627]
* Added `NsxtEdgeGatewayDns` methods `Refresh`, `Update` and `Delete`. [GH-627]
23 changes: 23 additions & 0 deletions govcd/api_vcd_test.go
Original file line number Diff line number Diff line change
@@ -1699,6 +1699,29 @@ func (vcd *TestVCD) removeLeftoverEntities(entity CleanupEntity) {
vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
}

vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
return
case "nsxtEdgeGatewayDns":
edge, err := vcd.nsxtVdc.GetNsxtEdgeGatewayByName(entity.Name)
if err != nil {
vcd.infoCleanup("removeLeftoverEntries: [ERROR] %s \n", err)
}

dns, err := edge.GetDnsConfig()
if err != nil {
vcd.infoCleanup("removeLeftoverEntries: [ERROR] %s \n", err)
}

if dns.NsxtEdgeGatewayDns.Enabled == false && dns.NsxtEdgeGatewayDns.DefaultForwarderZone == nil {
vcd.infoCleanup(notFoundMsg, entity.EntityType, entity.Name)
return
}

err = dns.Delete()
if err != nil {
vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
}

vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
return
case "slaacProfile":
115 changes: 115 additions & 0 deletions govcd/nsxt_edgegateway_dns.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* Copyright 2023 VMware, Inc. All rights reserved. Licensed under the Apache v2 License.
*/

package govcd

import (
"fmt"

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

// NsxtEdgeGatewayDns can be used to configure DNS on NSX-T Edge Gateway.
type NsxtEdgeGatewayDns struct {
NsxtEdgeGatewayDns *types.NsxtEdgeGatewayDns
client *Client
EdgeGatewayId string
}

// GetDnsConfig retrieves the DNS configuration for the underlying edge gateway
func (egw *NsxtEdgeGateway) GetDnsConfig() (*NsxtEdgeGatewayDns, error) {
return getDnsConfig(egw.client, egw.EdgeGateway.ID)
}

// UpdateDnsConfig updates the DNS configuration for the Edge Gateway
func (egw *NsxtEdgeGateway) UpdateDnsConfig(updatedConfig *types.NsxtEdgeGatewayDns) (*NsxtEdgeGatewayDns, error) {
return updateDnsConfig(updatedConfig, egw.client, egw.EdgeGateway.ID)
}

// Update updates the DNS configuration for the underlying Edge Gateway
func (dns *NsxtEdgeGatewayDns) Update(updatedConfig *types.NsxtEdgeGatewayDns) (*NsxtEdgeGatewayDns, error) {
return updateDnsConfig(updatedConfig, dns.client, dns.EdgeGatewayId)
}

// Refresh refreshes the DNS configuration for the Edge Gateway
func (dns *NsxtEdgeGatewayDns) Refresh() error {
updatedDns, err := getDnsConfig(dns.client, dns.EdgeGatewayId)
if err != nil {
return err
}
dns.NsxtEdgeGatewayDns = updatedDns.NsxtEdgeGatewayDns

return nil
}

// Delete deletes the DNS configuration for the Edge Gateway
func (dns *NsxtEdgeGatewayDns) Delete() error {
client := dns.client
endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGatewayDns
apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint)
if err != nil {
return err
}

urlRef, err := client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, dns.EdgeGatewayId))
if err != nil {
return err
}

err = client.OpenApiDeleteItem(apiVersion, urlRef, nil, nil)
if err != nil {
return err
}

return nil
}

func getDnsConfig(client *Client, edgeGatewayId string) (*NsxtEdgeGatewayDns, error) {
endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGatewayDns
apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint)
if err != nil {
return nil, err
}

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

dnsConfig := &NsxtEdgeGatewayDns{
client: client,
EdgeGatewayId: edgeGatewayId,
}
err = client.OpenApiGetItem(apiVersion, urlRef, nil, &dnsConfig.NsxtEdgeGatewayDns, nil)
if err != nil {
return nil, err
}

return dnsConfig, nil

}

func updateDnsConfig(updatedConfig *types.NsxtEdgeGatewayDns, client *Client, edgeGatewayId string) (*NsxtEdgeGatewayDns, error) {
endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGatewayDns
apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint)
if err != nil {
return nil, err
}

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

dns := &NsxtEdgeGatewayDns{
client: client,
EdgeGatewayId: edgeGatewayId,
}
err = client.OpenApiPutItem(apiVersion, urlRef, nil, updatedConfig, &dns.NsxtEdgeGatewayDns, nil)
if err != nil {
return nil, err
}

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

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

package govcd

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

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

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)
AddToCleanupList(vcd.config.VCD.Nsxt.EdgeGateway, "nsxtEdgeGatewayDns", vcd.config.VCD.Org, check.TestName())

disabledDns, err := edge.GetDnsConfig()
check.Assert(err, IsNil)
check.Assert(disabledDns.NsxtEdgeGatewayDns.Enabled, Equals, false)

enabledDnsConfig := &types.NsxtEdgeGatewayDns{
Enabled: true,
DefaultForwarderZone: &types.NsxtDnsForwarderZoneConfig{
DisplayName: "test",
UpstreamServers: []string{
"1.2.3.4",
"2.3.4.5",
},
},
ConditionalForwarderZones: []*types.NsxtDnsForwarderZoneConfig{
{
DisplayName: "test-conditional",
UpstreamServers: []string{
"5.5.5.5",
"2.3.4.1",
},
DnsDomainNames: []string{
"test.com",
"abc.com",
"example.org",
},
},
},
}

enabledDns, err := disabledDns.Update(enabledDnsConfig)
check.Assert(err, IsNil)
dnsConfig := enabledDns.NsxtEdgeGatewayDns
check.Assert(dnsConfig.Enabled, Equals, true)
check.Assert(dnsConfig.DefaultForwarderZone.DisplayName, Equals, "test")
check.Assert(len(dnsConfig.DefaultForwarderZone.UpstreamServers), Equals, 2)
check.Assert(len(dnsConfig.ConditionalForwarderZones), Equals, 1)
check.Assert(dnsConfig.ConditionalForwarderZones[0].DisplayName, Equals, "test-conditional")

updatedDnsConfig := &types.NsxtEdgeGatewayDns{
Enabled: true,
DefaultForwarderZone: &types.NsxtDnsForwarderZoneConfig{
DisplayName: "test",
UpstreamServers: []string{
"1.2.3.5",
"2.3.4.6",
"2.3.4.5",
},
},
ConditionalForwarderZones: []*types.NsxtDnsForwarderZoneConfig{
{
DisplayName: "test-conditional",
UpstreamServers: []string{
"5.5.5.5",
"2.3.4.1",
},
DnsDomainNames: []string{
"test.com",
"abc.com",
"example.org",
},
},
{
DisplayName: "test-conditional-2",
UpstreamServers: []string{
"1.2.3.4",
"4.3.2.1",
},
DnsDomainNames: []string{
"example.com",
},
},
},
}
updatedDns, err := enabledDns.Update(updatedDnsConfig)
updatedDnsConfig = updatedDns.NsxtEdgeGatewayDns
check.Assert(err, IsNil)
check.Assert(updatedDnsConfig.Enabled, Equals, true)
check.Assert(updatedDnsConfig.DefaultForwarderZone.DisplayName, Equals, "test")
check.Assert(len(updatedDnsConfig.DefaultForwarderZone.UpstreamServers), Equals, 3)
conditionalZones := updatedDnsConfig.ConditionalForwarderZones
check.Assert(len(conditionalZones), Equals, 2)
// Flip the asserts in both cases of conditional zones arrays returned
if conditionalZones[0].DisplayName == "test-conditional" {
check.Assert(len(conditionalZones[0].UpstreamServers), Equals, 2)
check.Assert(len(conditionalZones[0].DnsDomainNames), Equals, 3)
check.Assert(len(conditionalZones[1].UpstreamServers), Equals, 2)
check.Assert(len(conditionalZones[1].DnsDomainNames), Equals, 1)
} else {
check.Assert(len(conditionalZones[1].UpstreamServers), Equals, 2)
check.Assert(len(conditionalZones[1].DnsDomainNames), Equals, 3)
check.Assert(len(conditionalZones[0].UpstreamServers), Equals, 2)
check.Assert(len(conditionalZones[0].DnsDomainNames), Equals, 1)
}

err = enabledDns.Delete()
check.Assert(err, IsNil)

deletedDns, err := edge.GetDnsConfig()
check.Assert(err, IsNil)
check.Assert(deletedDns.NsxtEdgeGatewayDns.Enabled, Equals, false)
}
5 changes: 5 additions & 0 deletions govcd/openapi_endpoints.go
Original file line number Diff line number Diff line change
@@ -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.OpenApiEndpointEdgeGatewayDns: "37.0", // VCD 10.4.0+ (NSX-T only)
types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGatewayL2VpnTunnel: "35.0", // VCD 10.2.0+ (NSX-T only)
types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGatewaySlaacProfile: "35.0",
types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGatewayStaticRoutes: "37.0", // VCD 10.4.0+ (NSX-T only)
@@ -205,6 +206,10 @@ var endpointElevatedApiVersions = map[string][]string{
//"35.0", // Introduced support
"37.1", // Exposes computed field `UsingIpSpace` in `types.EdgeGatewayUplinks`
},
types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGatewayDns: {
"37.0", // Introduced support
"38.0", // New field SnatRuleExternalIpAddress
},
types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointIpSpaceUplinksAllocate: {
//"37.1", // Introduced support
"37.2", // Adds 'value' field
1 change: 1 addition & 0 deletions types/v56/constants.go
Original file line number Diff line number Diff line change
@@ -384,6 +384,7 @@ const (
OpenApiEndpointEdgeGateways = "edgeGateways/"
OpenApiEndpointEdgeGatewayQos = "edgeGateways/%s/qos"
OpenApiEndpointEdgeGatewayDhcpForwarder = "edgeGateways/%s/dhcpForwarder"
OpenApiEndpointEdgeGatewayDns = "edgeGateways/%s/dns"
OpenApiEndpointEdgeGatewaySlaacProfile = "edgeGateways/%s/slaacProfile"
OpenApiEndpointEdgeGatewayStaticRoutes = "edgeGateways/%s/routing/staticRoutes/"
OpenApiEndpointEdgeGatewayUsedIpAddresses = "edgeGateways/%s/usedIpAddresses"
37 changes: 37 additions & 0 deletions types/v56/nsxt_types.go
Original file line number Diff line number Diff line change
@@ -1916,3 +1916,40 @@ type NsxtManager struct {
LocalManagerLocationName interface{} `json:"localManagerLocationName"`
VCloudExtension []interface{} `json:"vCloudExtension"`
}

// NsxtEdgeGatewayDns is used for configuring the DNS forwarder for a specific Edge Gateway
type NsxtEdgeGatewayDns struct {
// Status of the DNS forwarder
Enabled bool `json:"enabled"`
// The IP on which the DNS forwarder listens. If the Edge Gateway has a dedicated
// external network, this can be changed.
ListenerIp string `json:"listenerIp,omitempty"`
// Whether an SNAT rule exists for the DNS forwarder or not. In NAT
// routed environments, an SNAT rule is required for the Edge DNS forwarder
// to send traffic to an upstream server. In fully routed environments,
// this is not needed if the listener IP is on an advertised subnet.
SnatRuleEnabled bool `json:"snatRuleEnabled,omitempty"`
// The external IP address of the SNAT rule. This property only applies if the
// Edge Gateway is connected to a Provider Gateway using IP Spaces.
SnatRuleExternalIpAddress string `json:"snatRuleExternalIpAddress,omitempty"`
// The default forwarder zone to use if there’s no matching domain in the conditional forwarder zone.
DefaultForwarderZone *NsxtDnsForwarderZoneConfig `json:"defaultForwarderZone,omitempty"`
// The list of forwarder zones with its matching DNS domains.
ConditionalForwarderZones []*NsxtDnsForwarderZoneConfig `json:"conditionalForwarderZones,omitempty"`
Version *VersionField `json:"version,omitempty"`
}

type NsxtDnsForwarderZoneConfig struct {
// The unique id of the DNS forwarder zone. If value is set,
// the zone is updated; if not, a new zone is created.
ID string `json:"id,omitempty"`
// User friendly name for the zone
DisplayName string `json:"displayName,omitempty"`
// DNS servers to which the DNS request needs to be forwarded.
UpstreamServers []string `json:"upstreamServers,omitempty"`
// List of domain names on which conditional forwarding is based. This
// field is required if the DNS Zone is being used for a conditional forwarder.
// This field will also be used for conditional reverse lookup. This field should
// not be set if the zone is used as default forwarder zone.
DnsDomainNames []string `json:"dnsDomainNames,omitempty"`
}