From b3c63c814a8ec4149cc1b8aef600493a1f288917 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Tue, 9 Jul 2019 17:03:24 +0300 Subject: [PATCH 01/22] Add functions to allow create SNAT and DNAT rules for ext netw Signed-off-by: Vaidotas Bauzys --- CHANGELOG.md | 1 + govcd/edgegateway.go | 116 ++++++++++++++++++++++++++++ govcd/edgegateway_test.go | 154 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 271 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe8be7fd7..5a7701ea3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * Added VDC meta data create/get/delete functions [#203](https://github.com/vmware/go-vcloud-director/pull/203) * Added org user create/delete/update functions [#18](https://github.com/vmware/go-vcloud-director/issues/18) * Added load balancer application profile [#208](https://github.com/vmware/go-vcloud-director/pull/208) +* Added edge gateway SNAT/DNAT rules functions which supports org VDC network and external network [#225](https://github.com/terraform-providers/terraform-provider-vcd/issues/225) ## 2.2.0 (May 15, 2019) diff --git a/govcd/edgegateway.go b/govcd/edgegateway.go index ace1efc3b..b64949857 100644 --- a/govcd/edgegateway.go +++ b/govcd/edgegateway.go @@ -186,6 +186,121 @@ func (eGW *EdgeGateway) RemoveNATPortMapping(natType, externalIP, externalPort s } +// AddDNATRule creates firewall DNAT rule and return refreshed existing NAT rules array or error +func (eGW *EdgeGateway) AddDNATRule(networkHref, externalIP, externalPort, internalIP, internalPort, protocol, icmpSubType, description string) ([]*types.NatRule, error) { + + task, err := eGW.AddNATRuleAsync(networkHref, "DNAT", externalIP, externalPort, internalIP, internalPort, protocol, icmpSubType, description) + if err != nil { + return nil, fmt.Errorf("error creating DNAT rule: %#v", err) + } + err = task.WaitTaskCompletion() + if err != nil { + return nil, fmt.Errorf("%s", combinedTaskErrorMessage(task.Task, err)) + } + + err = eGW.Refresh() + if err != nil { + return nil, fmt.Errorf("error refreshing edge gateway: %#v", err) + } + + return eGW.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService.NatRule, nil +} + +// AddSNATRule creates firewall SNAT rule and return refreshed existing NAT rules array or error +func (eGW *EdgeGateway) AddSNATRule(networkHref, externalIP, internalIP, description string) ([]*types.NatRule, error) { + + task, err := eGW.AddNATRuleAsync(networkHref, "SNAT", externalIP, "any", internalIP, "any", "any", "", description) + if err != nil { + return nil, fmt.Errorf("error creating SNAT rule: %#v", err) + } + err = task.WaitTaskCompletion() + if err != nil { + return nil, fmt.Errorf("%s", combinedTaskErrorMessage(task.Task, err)) + } + + err = eGW.Refresh() + if err != nil { + return nil, fmt.Errorf("error refreshing edge gateway: %#v", err) + } + + return eGW.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService.NatRule, nil +} + +// AddNATRuleAsync creates firewall NAT rule and return task or err +func (eGW *EdgeGateway) AddNATRuleAsync(networkHref, natType, externalIP, externalPort, internalIP, internalPort, protocol, icmpSubType, description string) (Task, error) { + if !isValidProtocol(protocol) { + return Task{}, fmt.Errorf("provided protocol is not one of TCP, UDP, TCPUDP, ICMP, ANY") + } + + if strings.ToUpper(protocol) == "ICMP" && !isValidIcmpSubType(icmpSubType) { + return Task{}, fmt.Errorf("provided icmp sub type is not correct") + } + + newEdgeConfig := eGW.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration + + // Take care of the NAT service + newNatService := &types.NatService{} + + if newEdgeConfig.NatService == nil { + newNatService.IsEnabled = true + } else { + newNatService.IsEnabled = newEdgeConfig.NatService.IsEnabled + newNatService.NatType = newEdgeConfig.NatService.NatType + newNatService.Policy = newEdgeConfig.NatService.Policy + newNatService.ExternalIP = newEdgeConfig.NatService.ExternalIP + + for _, natRule := range newEdgeConfig.NatService.NatRule { + + // Kludgy IF to avoid deleting DNAT rules not created by us. + // If matches, let's skip it and continue the loop + if natRule.RuleType == natType && + natRule.GatewayNatRule.OriginalIP == externalIP && + natRule.GatewayNatRule.OriginalPort == externalPort && + natRule.GatewayNatRule.TranslatedIP == internalIP && + natRule.GatewayNatRule.TranslatedPort == internalPort && + natRule.GatewayNatRule.Interface.HREF == networkHref { + continue + } + + newNatService.NatRule = append(newNatService.NatRule, natRule) + } + } + + //add rule + natRule := &types.NatRule{ + RuleType: natType, + IsEnabled: true, + Description: description, + GatewayNatRule: &types.GatewayNatRule{ + Interface: &types.Reference{ + HREF: networkHref, + }, + OriginalIP: externalIP, + OriginalPort: externalPort, + TranslatedIP: internalIP, + TranslatedPort: internalPort, + Protocol: protocol, + IcmpSubType: icmpSubType, + }, + } + newNatService.NatRule = append(newNatService.NatRule, natRule) + + newEdgeConfig.NatService = newNatService + + newRules := &types.EdgeGatewayServiceConfiguration{ + Xmlns: types.XMLNamespaceVCloud, + NatService: newNatService, + } + + apiEndpoint, _ := url.ParseRequestURI(eGW.EdgeGateway.HREF) + apiEndpoint.Path += "/action/configureServices" + + // Return the task + return eGW.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPost, + "application/vnd.vmware.admin.edgeGatewayServiceConfiguration+xml", "error reconfiguring Edge Gateway: %s", newRules) +} + +// Deprecated: Use eGW.AddSNATRule() or eGW.AddDNATRule() func (eGW *EdgeGateway) AddNATRule(network *types.OrgVDCNetwork, natType, externalIP, internalIP string) (Task, error) { return eGW.AddNATPortMappingWithUplink(network, natType, externalIP, "any", internalIP, "any", "any", "") } @@ -250,6 +365,7 @@ func isValidIcmpSubType(protocol string) bool { return false } +// Deprecated: Use eGW.AddNATFirewallRule() func (eGW *EdgeGateway) AddNATPortMappingWithUplink(network *types.OrgVDCNetwork, natType, externalIP, externalPort, internalIP, internalPort, protocol, icmpSubType string) (Task, error) { // if a network is provided take it, otherwise find first uplink on the edge gateway var uplinkRef string diff --git a/govcd/edgegateway_test.go b/govcd/edgegateway_test.go index 77d162be6..eed683476 100644 --- a/govcd/edgegateway_test.go +++ b/govcd/edgegateway_test.go @@ -277,3 +277,157 @@ func (vcd *TestVCD) TestEdgeGateway_GetNetworks(check *C) { } } + +func (vcd *TestVCD) Test_AddSNATRule(check *C) { + if vcd.config.VCD.ExternalIp == "" || vcd.config.VCD.InternalIp == "" { + check.Skip("Skipping test because no valid ip given") + } + if vcd.config.VCD.ExternalNetwork == "" { + check.Skip("Skipping test because no external network given") + } + if vcd.config.VCD.EdgeGateway == "" { + check.Skip("Skipping test because no edge gateway given") + } + if vcd.config.VCD.Network.Net1 == "" { + check.Skip("Skipping test because no network was given") + } + + description1 := "my Description 1" + description2 := "my Description 2" + + edge, err := vcd.vdc.FindEdgeGateway(vcd.config.VCD.EdgeGateway) + check.Assert(err, IsNil) + check.Assert(edge.EdgeGateway.Name, Equals, vcd.config.VCD.EdgeGateway) + + orgVdcNetwork, err := vcd.vdc.FindVDCNetwork(vcd.config.VCD.Network.Net1) + check.Assert(err, IsNil) + check.Assert(orgVdcNetwork.OrgVDCNetwork.Name, Equals, vcd.config.VCD.Network.Net1) + + externalNetwork, err := GetExternalNetwork(vcd.client, vcd.config.VCD.ExternalNetwork) + check.Assert(err, IsNil) + check.Assert(externalNetwork.ExternalNetwork.Name, Equals, vcd.config.VCD.ExternalNetwork) + + natRules, err := edge.AddSNATRule(orgVdcNetwork.OrgVDCNetwork.HREF, vcd.config.VCD.ExternalIp, vcd.config.VCD.InternalIp, description1) + check.Assert(err, IsNil) + + found := false + var rule *types.NatRule + for _, r := range natRules { + if r.RuleType == "SNAT" && r.GatewayNatRule.Interface.Name == orgVdcNetwork.OrgVDCNetwork.Name { + found = true + rule = r + } + } + + check.Assert(found, Equals, true) + check.Assert(rule.GatewayNatRule.TranslatedIP, Equals, vcd.config.VCD.InternalIp) + check.Assert(rule.GatewayNatRule.OriginalIP, Equals, vcd.config.VCD.ExternalIp) + check.Assert(rule.Description, Equals, description1) + + task, err := edge.RemoveNATMapping("SNAT", vcd.config.VCD.ExternalIp, vcd.config.VCD.InternalIp, "77") + check.Assert(err, IsNil) + err = task.WaitTaskCompletion() + check.Assert(err, IsNil) + + // check with external network + natRules, err = edge.AddSNATRule(externalNetwork.ExternalNetwork.HREF, vcd.config.VCD.InternalIp, vcd.config.VCD.ExternalIp, description2) + check.Assert(err, IsNil) + + found = false + for _, r := range natRules { + if r.RuleType == "SNAT" && r.GatewayNatRule.Interface.Name == externalNetwork.ExternalNetwork.Name { + found = true + rule = r + } + } + + check.Assert(found, Equals, true) + check.Assert(rule.GatewayNatRule.TranslatedIP, Equals, vcd.config.VCD.ExternalIp) + check.Assert(rule.GatewayNatRule.OriginalIP, Equals, vcd.config.VCD.InternalIp) + check.Assert(rule.Description, Equals, description2) + + task, err = edge.RemoveNATMapping("SNAT", vcd.config.VCD.InternalIp, vcd.config.VCD.ExternalIp, "77") + check.Assert(err, IsNil) + err = task.WaitTaskCompletion() + check.Assert(err, IsNil) +} + +func (vcd *TestVCD) Test_AddDNATRule(check *C) { + if vcd.config.VCD.ExternalIp == "" || vcd.config.VCD.InternalIp == "" { + check.Skip("Skipping test because no valid ip given") + } + if vcd.config.VCD.ExternalNetwork == "" { + check.Skip("Skipping test because no external network given") + } + if vcd.config.VCD.EdgeGateway == "" { + check.Skip("Skipping test because no edge gateway given") + } + if vcd.config.VCD.Network.Net1 == "" { + check.Skip("Skipping test because no network was given") + } + + edge, err := vcd.vdc.FindEdgeGateway(vcd.config.VCD.EdgeGateway) + check.Assert(err, IsNil) + check.Assert(edge.EdgeGateway.Name, Equals, vcd.config.VCD.EdgeGateway) + + orgVdcNetwork, err := vcd.vdc.FindVDCNetwork(vcd.config.VCD.Network.Net1) + check.Assert(err, IsNil) + check.Assert(orgVdcNetwork.OrgVDCNetwork.Name, Equals, vcd.config.VCD.Network.Net1) + + externalNetwork, err := GetExternalNetwork(vcd.client, vcd.config.VCD.ExternalNetwork) + check.Assert(err, IsNil) + check.Assert(externalNetwork.ExternalNetwork.Name, Equals, vcd.config.VCD.ExternalNetwork) + + description1 := "my Dnat Description 1" + description2 := "my Dnatt Description 2" + + natRules, err := edge.AddDNATRule(orgVdcNetwork.OrgVDCNetwork.HREF, vcd.config.VCD.ExternalIp, "1177", vcd.config.VCD.InternalIp, "77", "TCP", "", description1) + check.Assert(err, IsNil) + + found := false + var rule *types.NatRule + for _, r := range natRules { + if r.RuleType == "DNAT" && r.GatewayNatRule.Interface.Name == orgVdcNetwork.OrgVDCNetwork.Name { + found = true + rule = r + } + } + + check.Assert(found, Equals, true) + check.Assert(rule.GatewayNatRule.TranslatedIP, Equals, vcd.config.VCD.InternalIp) + check.Assert(rule.GatewayNatRule.TranslatedPort, Equals, "77") + check.Assert(rule.GatewayNatRule.OriginalIP, Equals, vcd.config.VCD.ExternalIp) + check.Assert(rule.GatewayNatRule.OriginalPort, Equals, "1177") + check.Assert(rule.GatewayNatRule.Protocol, Equals, "tcp") + check.Assert(rule.GatewayNatRule.IcmpSubType, Equals, "") + + task, err := edge.RemoveNATPortMapping("DNAT", vcd.config.VCD.ExternalIp, "1177", vcd.config.VCD.InternalIp, "77") + check.Assert(err, IsNil) + err = task.WaitTaskCompletion() + check.Assert(err, IsNil) + + // check with external network + natRules, err = edge.AddDNATRule(externalNetwork.ExternalNetwork.HREF, vcd.config.VCD.ExternalIp, "1188", vcd.config.VCD.InternalIp, "88", "TCP", "", description2) + check.Assert(err, IsNil) + + found = false + for _, r := range natRules { + if r.RuleType == "DNAT" && r.GatewayNatRule.Interface.Name == externalNetwork.ExternalNetwork.Name { + found = true + rule = r + } + } + + check.Assert(found, Equals, true) + check.Assert(rule.GatewayNatRule.TranslatedIP, Equals, vcd.config.VCD.InternalIp) + check.Assert(rule.GatewayNatRule.TranslatedPort, Equals, "88") + check.Assert(rule.GatewayNatRule.OriginalIP, Equals, vcd.config.VCD.ExternalIp) + check.Assert(rule.GatewayNatRule.OriginalPort, Equals, "1188") + check.Assert(rule.GatewayNatRule.Protocol, Equals, "tcp") + check.Assert(rule.GatewayNatRule.IcmpSubType, Equals, "") + + task, err = edge.RemoveNATPortMapping("DNAT", vcd.config.VCD.ExternalIp, "1188", vcd.config.VCD.InternalIp, "88") + check.Assert(err, IsNil) + err = task.WaitTaskCompletion() + check.Assert(err, IsNil) +} From 7c5f5959911b0ca1b33ec14f72c9eb7fb9c1bd00 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Wed, 10 Jul 2019 16:31:23 +0300 Subject: [PATCH 02/22] Fix SNAT, DNAT removing Signed-off-by: Vaidotas Bauzys --- govcd/edgegateway.go | 75 +++++++++++++++++++++++++++++++++++++-- govcd/edgegateway_test.go | 40 ++++++++++++++++++--- 2 files changed, 108 insertions(+), 7 deletions(-) diff --git a/govcd/edgegateway.go b/govcd/edgegateway.go index b64949857..a6e81ba2a 100644 --- a/govcd/edgegateway.go +++ b/govcd/edgegateway.go @@ -10,6 +10,7 @@ import ( "fmt" "net/http" "net/url" + "path" "regexp" "strconv" "strings" @@ -131,11 +132,81 @@ func (eGW *EdgeGateway) AddDhcpPool(network *types.OrgVDCNetwork, dhcppool []int } +// Temporary fix for existing Remove functions - until ID support +func (eGW *EdgeGateway) RemoveNATMappingRule(networkHref, natType, externalIP, internalIP, port string) (Task, error) { + return eGW.RemoveNATPortRule(networkHref, natType, externalIP, port, internalIP, port) +} + +// Temporary fix for existing Remove functions - until ID support +func (eGW *EdgeGateway) RemoveNATPortRule(networkHref, natType, externalIP, externalPort, internalIP, internalPort string) (Task, error) { + newEdgeConfig := eGW.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration + + // Take care of the NAT service + newNatService := &types.NatService{} + + newNatService.IsEnabled = newEdgeConfig.NatService.IsEnabled + newNatService.NatType = newEdgeConfig.NatService.NatType + newNatService.Policy = newEdgeConfig.NatService.Policy + newNatService.ExternalIP = newEdgeConfig.NatService.ExternalIP + + for _, natRule := range newEdgeConfig.NatService.NatRule { + + // Kludgy IF to avoid deleting DNAT rules not created by us. + // If matches, let's skip it and continue the loop + interfaceId, err := extractObjectIDfromPath(natRule.GatewayNatRule.Interface.HREF) + networkHrefId, err := extractObjectIDfromPath(networkHref) + if err != nil { + return Task{}, nil + } + if natRule.RuleType == natType && + natRule.GatewayNatRule.OriginalIP == externalIP && + natRule.GatewayNatRule.OriginalPort == externalPort && + interfaceId == networkHrefId { + util.Logger.Printf("[DEBUG] REMOVING %s Rule: %#v", natRule.RuleType, natRule.GatewayNatRule) + continue + } + util.Logger.Printf("[DEBUG] KEEPING %s Rule: %#v", natRule.RuleType, natRule.GatewayNatRule) + newNatService.NatRule = append(newNatService.NatRule, natRule) + } + + newEdgeConfig.NatService = newNatService + + newRules := &types.EdgeGatewayServiceConfiguration{ + Xmlns: types.XMLNamespaceVCloud, + NatService: newNatService, + } + + apiEndpoint, _ := url.ParseRequestURI(eGW.EdgeGateway.HREF) + apiEndpoint.Path += "/action/configureServices" + + // Return the task + return eGW.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPost, + "application/vnd.vmware.admin.edgeGatewayServiceConfiguration+xml", "error reconfiguring Edge Gateway: %s", newRules) + +} + +func extractObjectIDfromPath(locationPath string) (string, error) { + if locationPath == "" { + return "", fmt.Errorf("unable to get ID from empty path") + } + + cleanPath := path.Clean(locationPath) // Removes trailing slash if there is one + splitPath := strings.Split(cleanPath, "/") + + if len(splitPath) < 2 { + return "", fmt.Errorf("path does not contain url path: %s", splitPath) + } + + objectID := splitPath[len(splitPath)-1] + + return objectID, nil +} + func (eGW *EdgeGateway) RemoveNATMapping(natType, externalIP, internalIP, port string) (Task, error) { return eGW.RemoveNATPortMapping(natType, externalIP, port, internalIP, port) } -func (eGW *EdgeGateway) RemoveNATPortMapping(natType, externalIP, externalPort string, internalIP, internalPort string) (Task, error) { +func (eGW *EdgeGateway) RemoveNATPortMapping(natType, externalIP, externalPort, internalIP, internalPort string) (Task, error) { // Find uplink interface var uplink types.Reference for _, gi := range eGW.EdgeGateway.Configuration.GatewayInterfaces.GatewayInterface { @@ -157,8 +228,6 @@ func (eGW *EdgeGateway) RemoveNATPortMapping(natType, externalIP, externalPort s for _, natRule := range newEdgeConfig.NatService.NatRule { - // Kludgy IF to avoid deleting DNAT rules not created by us. - // If matches, let's skip it and continue the loop if natRule.RuleType == natType && natRule.GatewayNatRule.OriginalIP == externalIP && natRule.GatewayNatRule.OriginalPort == externalPort && diff --git a/govcd/edgegateway_test.go b/govcd/edgegateway_test.go index eed683476..0f101743e 100644 --- a/govcd/edgegateway_test.go +++ b/govcd/edgegateway_test.go @@ -307,6 +307,8 @@ func (vcd *TestVCD) Test_AddSNATRule(check *C) { check.Assert(err, IsNil) check.Assert(externalNetwork.ExternalNetwork.Name, Equals, vcd.config.VCD.ExternalNetwork) + beforeChangeNatRulesNumber := len(edge.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService.NatRule) + natRules, err := edge.AddSNATRule(orgVdcNetwork.OrgVDCNetwork.HREF, vcd.config.VCD.ExternalIp, vcd.config.VCD.InternalIp, description1) check.Assert(err, IsNil) @@ -324,11 +326,17 @@ func (vcd *TestVCD) Test_AddSNATRule(check *C) { check.Assert(rule.GatewayNatRule.OriginalIP, Equals, vcd.config.VCD.ExternalIp) check.Assert(rule.Description, Equals, description1) - task, err := edge.RemoveNATMapping("SNAT", vcd.config.VCD.ExternalIp, vcd.config.VCD.InternalIp, "77") + task, err := edge.RemoveNATMappingRule(orgVdcNetwork.OrgVDCNetwork.HREF, "SNAT", vcd.config.VCD.ExternalIp, vcd.config.VCD.InternalIp, "") check.Assert(err, IsNil) err = task.WaitTaskCompletion() check.Assert(err, IsNil) + // verify delete + err = edge.Refresh() + check.Assert(err, IsNil) + + check.Assert(len(edge.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService.NatRule), Equals, beforeChangeNatRulesNumber) + // check with external network natRules, err = edge.AddSNATRule(externalNetwork.ExternalNetwork.HREF, vcd.config.VCD.InternalIp, vcd.config.VCD.ExternalIp, description2) check.Assert(err, IsNil) @@ -346,10 +354,20 @@ func (vcd *TestVCD) Test_AddSNATRule(check *C) { check.Assert(rule.GatewayNatRule.OriginalIP, Equals, vcd.config.VCD.InternalIp) check.Assert(rule.Description, Equals, description2) - task, err = edge.RemoveNATMapping("SNAT", vcd.config.VCD.InternalIp, vcd.config.VCD.ExternalIp, "77") + task, err = edge.RemoveNATMappingRule(externalNetwork.ExternalNetwork.HREF, "SNAT", vcd.config.VCD.InternalIp, vcd.config.VCD.ExternalIp, "") check.Assert(err, IsNil) err = task.WaitTaskCompletion() check.Assert(err, IsNil) + + err = edge.Refresh() + check.Assert(err, IsNil) + + // verify delete + err = edge.Refresh() + check.Assert(err, IsNil) + + check.Assert(len(edge.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService.NatRule), Equals, beforeChangeNatRulesNumber) + } func (vcd *TestVCD) Test_AddDNATRule(check *C) { @@ -378,6 +396,8 @@ func (vcd *TestVCD) Test_AddDNATRule(check *C) { check.Assert(err, IsNil) check.Assert(externalNetwork.ExternalNetwork.Name, Equals, vcd.config.VCD.ExternalNetwork) + beforeChangeNatRulesNumber := len(edge.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService.NatRule) + description1 := "my Dnat Description 1" description2 := "my Dnatt Description 2" @@ -401,11 +421,17 @@ func (vcd *TestVCD) Test_AddDNATRule(check *C) { check.Assert(rule.GatewayNatRule.Protocol, Equals, "tcp") check.Assert(rule.GatewayNatRule.IcmpSubType, Equals, "") - task, err := edge.RemoveNATPortMapping("DNAT", vcd.config.VCD.ExternalIp, "1177", vcd.config.VCD.InternalIp, "77") + task, err := edge.RemoveNATPortRule(orgVdcNetwork.OrgVDCNetwork.HREF, "DNAT", vcd.config.VCD.ExternalIp, "1177", vcd.config.VCD.InternalIp, "77") check.Assert(err, IsNil) err = task.WaitTaskCompletion() check.Assert(err, IsNil) + // verify delete + err = edge.Refresh() + check.Assert(err, IsNil) + + check.Assert(len(edge.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService.NatRule), Equals, beforeChangeNatRulesNumber) + // check with external network natRules, err = edge.AddDNATRule(externalNetwork.ExternalNetwork.HREF, vcd.config.VCD.ExternalIp, "1188", vcd.config.VCD.InternalIp, "88", "TCP", "", description2) check.Assert(err, IsNil) @@ -426,8 +452,14 @@ func (vcd *TestVCD) Test_AddDNATRule(check *C) { check.Assert(rule.GatewayNatRule.Protocol, Equals, "tcp") check.Assert(rule.GatewayNatRule.IcmpSubType, Equals, "") - task, err = edge.RemoveNATPortMapping("DNAT", vcd.config.VCD.ExternalIp, "1188", vcd.config.VCD.InternalIp, "88") + task, err = edge.RemoveNATPortRule(externalNetwork.ExternalNetwork.HREF, "DNAT", vcd.config.VCD.ExternalIp, "1188", vcd.config.VCD.InternalIp, "88") check.Assert(err, IsNil) err = task.WaitTaskCompletion() check.Assert(err, IsNil) + + // verify delete + err = edge.Refresh() + check.Assert(err, IsNil) + + check.Assert(len(edge.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService.NatRule), Equals, beforeChangeNatRulesNumber) } From a9573b81decee4e4928ff492f5ee2b5666bb9112 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Wed, 10 Jul 2019 16:44:27 +0300 Subject: [PATCH 03/22] Fix comments Signed-off-by: Vaidotas Bauzys --- CHANGELOG.md | 2 +- govcd/edgegateway.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a7701ea3..ef6c2bdd6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ * Added VDC meta data create/get/delete functions [#203](https://github.com/vmware/go-vcloud-director/pull/203) * Added org user create/delete/update functions [#18](https://github.com/vmware/go-vcloud-director/issues/18) * Added load balancer application profile [#208](https://github.com/vmware/go-vcloud-director/pull/208) -* Added edge gateway SNAT/DNAT rules functions which supports org VDC network and external network [#225](https://github.com/terraform-providers/terraform-provider-vcd/issues/225) +* Added edge gateway SNAT/DNAT rule functions which support org VDC network and external network [#225](https://github.com/terraform-providers/terraform-provider-vcd/issues/225) ## 2.2.0 (May 15, 2019) diff --git a/govcd/edgegateway.go b/govcd/edgegateway.go index a6e81ba2a..c1d210341 100644 --- a/govcd/edgegateway.go +++ b/govcd/edgegateway.go @@ -275,7 +275,7 @@ func (eGW *EdgeGateway) AddDNATRule(networkHref, externalIP, externalPort, inter return eGW.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService.NatRule, nil } -// AddSNATRule creates firewall SNAT rule and return refreshed existing NAT rules array or error +// AddSNATRule creates SNAT rule and returns refreshed existing NAT rules array or error func (eGW *EdgeGateway) AddSNATRule(networkHref, externalIP, internalIP, description string) ([]*types.NatRule, error) { task, err := eGW.AddNATRuleAsync(networkHref, "SNAT", externalIP, "any", internalIP, "any", "any", "", description) @@ -295,7 +295,7 @@ func (eGW *EdgeGateway) AddSNATRule(networkHref, externalIP, internalIP, descrip return eGW.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService.NatRule, nil } -// AddNATRuleAsync creates firewall NAT rule and return task or err +// AddNATRuleAsync creates NAT rule and return task or err func (eGW *EdgeGateway) AddNATRuleAsync(networkHref, natType, externalIP, externalPort, internalIP, internalPort, protocol, icmpSubType, description string) (Task, error) { if !isValidProtocol(protocol) { return Task{}, fmt.Errorf("provided protocol is not one of TCP, UDP, TCPUDP, ICMP, ANY") From c1f0466f01b8f067a3d3c7b196d4295156b71fa7 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Wed, 10 Jul 2019 17:34:19 +0300 Subject: [PATCH 04/22] Moved func args to struct Signed-off-by: Vaidotas Bauzys --- govcd/edgegateway.go | 58 +++++++++++++++++++++++++-------------- govcd/edgegateway_test.go | 6 ++-- 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/govcd/edgegateway.go b/govcd/edgegateway.go index c1d210341..d95b5d4bd 100644 --- a/govcd/edgegateway.go +++ b/govcd/edgegateway.go @@ -40,6 +40,19 @@ func NewEdgeGateway(cli *Client) *EdgeGateway { } } +// Struct which covers NAT rule fields +type NatRule struct { + natType string + networkHref string + externalIP string + externalPort string + internalIP string + internalPort string + protocol string + icmpSubType string + description string +} + func (eGW *EdgeGateway) AddDhcpPool(network *types.OrgVDCNetwork, dhcppool []interface{}) (Task, error) { newEdgeConfig := eGW.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration util.Logger.Printf("[DEBUG] EDGE GATEWAY: %#v", newEdgeConfig) @@ -256,9 +269,10 @@ func (eGW *EdgeGateway) RemoveNATPortMapping(natType, externalIP, externalPort, } // AddDNATRule creates firewall DNAT rule and return refreshed existing NAT rules array or error -func (eGW *EdgeGateway) AddDNATRule(networkHref, externalIP, externalPort, internalIP, internalPort, protocol, icmpSubType, description string) ([]*types.NatRule, error) { +func (eGW *EdgeGateway) AddDNATRule(ruleDetails NatRule) ([]*types.NatRule, error) { - task, err := eGW.AddNATRuleAsync(networkHref, "DNAT", externalIP, externalPort, internalIP, internalPort, protocol, icmpSubType, description) + ruleDetails.natType = "DNAT" + task, err := eGW.AddNATRuleAsync(ruleDetails) if err != nil { return nil, fmt.Errorf("error creating DNAT rule: %#v", err) } @@ -278,7 +292,9 @@ func (eGW *EdgeGateway) AddDNATRule(networkHref, externalIP, externalPort, inter // AddSNATRule creates SNAT rule and returns refreshed existing NAT rules array or error func (eGW *EdgeGateway) AddSNATRule(networkHref, externalIP, internalIP, description string) ([]*types.NatRule, error) { - task, err := eGW.AddNATRuleAsync(networkHref, "SNAT", externalIP, "any", internalIP, "any", "any", "", description) + task, err := eGW.AddNATRuleAsync(NatRule{networkHref: networkHref, natType: "SNAT", externalIP: externalIP, + externalPort: "any", internalIP: internalIP, internalPort: "any", + icmpSubType: "", protocol: "any", description: description}) if err != nil { return nil, fmt.Errorf("error creating SNAT rule: %#v", err) } @@ -296,12 +312,12 @@ func (eGW *EdgeGateway) AddSNATRule(networkHref, externalIP, internalIP, descrip } // AddNATRuleAsync creates NAT rule and return task or err -func (eGW *EdgeGateway) AddNATRuleAsync(networkHref, natType, externalIP, externalPort, internalIP, internalPort, protocol, icmpSubType, description string) (Task, error) { - if !isValidProtocol(protocol) { +func (eGW *EdgeGateway) AddNATRuleAsync(ruleDetails NatRule) (Task, error) { + if !isValidProtocol(ruleDetails.protocol) { return Task{}, fmt.Errorf("provided protocol is not one of TCP, UDP, TCPUDP, ICMP, ANY") } - if strings.ToUpper(protocol) == "ICMP" && !isValidIcmpSubType(icmpSubType) { + if strings.ToUpper(ruleDetails.protocol) == "ICMP" && !isValidIcmpSubType(ruleDetails.icmpSubType) { return Task{}, fmt.Errorf("provided icmp sub type is not correct") } @@ -322,12 +338,12 @@ func (eGW *EdgeGateway) AddNATRuleAsync(networkHref, natType, externalIP, extern // Kludgy IF to avoid deleting DNAT rules not created by us. // If matches, let's skip it and continue the loop - if natRule.RuleType == natType && - natRule.GatewayNatRule.OriginalIP == externalIP && - natRule.GatewayNatRule.OriginalPort == externalPort && - natRule.GatewayNatRule.TranslatedIP == internalIP && - natRule.GatewayNatRule.TranslatedPort == internalPort && - natRule.GatewayNatRule.Interface.HREF == networkHref { + if natRule.RuleType == ruleDetails.natType && + natRule.GatewayNatRule.OriginalIP == ruleDetails.externalIP && + natRule.GatewayNatRule.OriginalPort == ruleDetails.externalPort && + natRule.GatewayNatRule.TranslatedIP == ruleDetails.internalIP && + natRule.GatewayNatRule.TranslatedPort == ruleDetails.internalPort && + natRule.GatewayNatRule.Interface.HREF == ruleDetails.networkHref { continue } @@ -337,19 +353,19 @@ func (eGW *EdgeGateway) AddNATRuleAsync(networkHref, natType, externalIP, extern //add rule natRule := &types.NatRule{ - RuleType: natType, + RuleType: ruleDetails.natType, IsEnabled: true, - Description: description, + Description: ruleDetails.description, GatewayNatRule: &types.GatewayNatRule{ Interface: &types.Reference{ - HREF: networkHref, + HREF: ruleDetails.networkHref, }, - OriginalIP: externalIP, - OriginalPort: externalPort, - TranslatedIP: internalIP, - TranslatedPort: internalPort, - Protocol: protocol, - IcmpSubType: icmpSubType, + OriginalIP: ruleDetails.externalIP, + OriginalPort: ruleDetails.externalPort, + TranslatedIP: ruleDetails.internalIP, + TranslatedPort: ruleDetails.internalPort, + Protocol: ruleDetails.protocol, + IcmpSubType: ruleDetails.icmpSubType, }, } newNatService.NatRule = append(newNatService.NatRule, natRule) diff --git a/govcd/edgegateway_test.go b/govcd/edgegateway_test.go index 0f101743e..5585e9446 100644 --- a/govcd/edgegateway_test.go +++ b/govcd/edgegateway_test.go @@ -401,7 +401,8 @@ func (vcd *TestVCD) Test_AddDNATRule(check *C) { description1 := "my Dnat Description 1" description2 := "my Dnatt Description 2" - natRules, err := edge.AddDNATRule(orgVdcNetwork.OrgVDCNetwork.HREF, vcd.config.VCD.ExternalIp, "1177", vcd.config.VCD.InternalIp, "77", "TCP", "", description1) + natRules, err := edge.AddDNATRule(NatRule{networkHref: orgVdcNetwork.OrgVDCNetwork.HREF, externalIP: vcd.config.VCD.ExternalIp, + externalPort: "1177", internalIP: vcd.config.VCD.InternalIp, internalPort: "77", protocol: "TCP", description: description1}) check.Assert(err, IsNil) found := false @@ -433,7 +434,8 @@ func (vcd *TestVCD) Test_AddDNATRule(check *C) { check.Assert(len(edge.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService.NatRule), Equals, beforeChangeNatRulesNumber) // check with external network - natRules, err = edge.AddDNATRule(externalNetwork.ExternalNetwork.HREF, vcd.config.VCD.ExternalIp, "1188", vcd.config.VCD.InternalIp, "88", "TCP", "", description2) + natRules, err = edge.AddDNATRule(NatRule{networkHref: externalNetwork.ExternalNetwork.HREF, externalIP: vcd.config.VCD.ExternalIp, + externalPort: "1188", internalIP: vcd.config.VCD.InternalIp, internalPort: "88", protocol: "TCP", description: description2}) check.Assert(err, IsNil) found = false From b6ba97e356e70d261315aa2cc5ed29965198693a Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Wed, 10 Jul 2019 17:40:16 +0300 Subject: [PATCH 05/22] add comments Signed-off-by: Vaidotas Bauzys --- govcd/edgegateway.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/govcd/edgegateway.go b/govcd/edgegateway.go index d95b5d4bd..511d49ad3 100644 --- a/govcd/edgegateway.go +++ b/govcd/edgegateway.go @@ -145,12 +145,14 @@ func (eGW *EdgeGateway) AddDhcpPool(network *types.OrgVDCNetwork, dhcppool []int } -// Temporary fix for existing Remove functions - until ID support +// Temporary fix for existing Remove functions as it doesn't support network interfaces +// TODO: remove this function when ruleId is available func (eGW *EdgeGateway) RemoveNATMappingRule(networkHref, natType, externalIP, internalIP, port string) (Task, error) { return eGW.RemoveNATPortRule(networkHref, natType, externalIP, port, internalIP, port) } -// Temporary fix for existing Remove functions - until ID support +// Temporary fix for existing Remove functions as it doesn't support network interfaces +// TODO: remove this function when ruleId is available func (eGW *EdgeGateway) RemoveNATPortRule(networkHref, natType, externalIP, externalPort, internalIP, internalPort string) (Task, error) { newEdgeConfig := eGW.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration @@ -198,6 +200,8 @@ func (eGW *EdgeGateway) RemoveNATPortRule(networkHref, natType, externalIP, exte } +// Temporary fix to support parsing network Id from HREF +// TODO: remove this function when ruleId is available func extractObjectIDfromPath(locationPath string) (string, error) { if locationPath == "" { return "", fmt.Errorf("unable to get ID from empty path") From 20b77337806372ceae2dba839710c29ae69c7443 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Thu, 11 Jul 2019 12:57:27 +0300 Subject: [PATCH 06/22] add comments Signed-off-by: Vaidotas Bauzys --- govcd/edgegateway.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/govcd/edgegateway.go b/govcd/edgegateway.go index 511d49ad3..beee5a493 100644 --- a/govcd/edgegateway.go +++ b/govcd/edgegateway.go @@ -272,7 +272,9 @@ func (eGW *EdgeGateway) RemoveNATPortMapping(natType, externalIP, externalPort, } -// AddDNATRule creates firewall DNAT rule and return refreshed existing NAT rules array or error +// AddDNATRule creates DNAT rule and return refreshed existing NAT rules array or error +// Allows to assign specific network Org VDC or external. Old function AddNATPortMapping and +// AddNATMapping assigns rule to first external network func (eGW *EdgeGateway) AddDNATRule(ruleDetails NatRule) ([]*types.NatRule, error) { ruleDetails.natType = "DNAT" @@ -294,6 +296,8 @@ func (eGW *EdgeGateway) AddDNATRule(ruleDetails NatRule) ([]*types.NatRule, erro } // AddSNATRule creates SNAT rule and returns refreshed existing NAT rules array or error +// Allows to assign specific network Org VDC or external. Old function AddNATPortMapping and +// AddNATMapping assigns rule to first external network func (eGW *EdgeGateway) AddSNATRule(networkHref, externalIP, internalIP, description string) ([]*types.NatRule, error) { task, err := eGW.AddNATRuleAsync(NatRule{networkHref: networkHref, natType: "SNAT", externalIP: externalIP, @@ -316,6 +320,8 @@ func (eGW *EdgeGateway) AddSNATRule(networkHref, externalIP, internalIP, descrip } // AddNATRuleAsync creates NAT rule and return task or err +// Allows to assign specific network Org VDC or external. Old function AddNATPortMapping and +// AddNATMapping assigns rule to first external network func (eGW *EdgeGateway) AddNATRuleAsync(ruleDetails NatRule) (Task, error) { if !isValidProtocol(ruleDetails.protocol) { return Task{}, fmt.Errorf("provided protocol is not one of TCP, UDP, TCPUDP, ICMP, ANY") From e3a379933b721801b9042ad2637e0a2e15134092 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Mon, 15 Jul 2019 13:57:52 +0300 Subject: [PATCH 07/22] Changed to support Nat Rule Id's Signed-off-by: Vaidotas Bauzys --- govcd/edgegateway.go | 347 ++++++++++++++++++++++++-------------- govcd/edgegateway_test.go | 123 +++++--------- 2 files changed, 263 insertions(+), 207 deletions(-) diff --git a/govcd/edgegateway.go b/govcd/edgegateway.go index beee5a493..11d05f068 100644 --- a/govcd/edgegateway.go +++ b/govcd/edgegateway.go @@ -6,11 +6,11 @@ package govcd import ( "bytes" + "crypto/rand" "encoding/xml" "fmt" "net/http" "net/url" - "path" "regexp" "strconv" "strings" @@ -43,14 +43,14 @@ func NewEdgeGateway(cli *Client) *EdgeGateway { // Struct which covers NAT rule fields type NatRule struct { natType string - networkHref string - externalIP string - externalPort string - internalIP string - internalPort string - protocol string - icmpSubType string - description string + NetworkHref string + ExternalIP string + ExternalPort string + InternalIP string + InternalPort string + Protocol string + IcmpSubType string + Description string } func (eGW *EdgeGateway) AddDhcpPool(network *types.OrgVDCNetwork, dhcppool []interface{}) (Task, error) { @@ -145,15 +145,22 @@ func (eGW *EdgeGateway) AddDhcpPool(network *types.OrgVDCNetwork, dhcppool []int } -// Temporary fix for existing Remove functions as it doesn't support network interfaces -// TODO: remove this function when ruleId is available -func (eGW *EdgeGateway) RemoveNATMappingRule(networkHref, natType, externalIP, internalIP, port string) (Task, error) { - return eGW.RemoveNATPortRule(networkHref, natType, externalIP, port, internalIP, port) +// Deprecated in favor of RemoveNATRuleAsync, RemoveNATRule +func (eGW *EdgeGateway) RemoveNATMapping(natType, externalIP, internalIP, port string) (Task, error) { + return eGW.RemoveNATPortMapping(natType, externalIP, port, internalIP, port) } -// Temporary fix for existing Remove functions as it doesn't support network interfaces -// TODO: remove this function when ruleId is available -func (eGW *EdgeGateway) RemoveNATPortRule(networkHref, natType, externalIP, externalPort, internalIP, internalPort string) (Task, error) { +// Deprecated in favor of RemoveNATRuleAsync, RemoveNATRule +func (eGW *EdgeGateway) RemoveNATPortMapping(natType, externalIP, externalPort, internalIP, internalPort string) (Task, error) { + // Find uplink interface + var uplink types.Reference + for _, gi := range eGW.EdgeGateway.Configuration.GatewayInterfaces.GatewayInterface { + if gi.InterfaceType != "uplink" { + continue + } + uplink = *gi.Network + } + newEdgeConfig := eGW.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration // Take care of the NAT service @@ -166,17 +173,10 @@ func (eGW *EdgeGateway) RemoveNATPortRule(networkHref, natType, externalIP, exte for _, natRule := range newEdgeConfig.NatService.NatRule { - // Kludgy IF to avoid deleting DNAT rules not created by us. - // If matches, let's skip it and continue the loop - interfaceId, err := extractObjectIDfromPath(natRule.GatewayNatRule.Interface.HREF) - networkHrefId, err := extractObjectIDfromPath(networkHref) - if err != nil { - return Task{}, nil - } if natRule.RuleType == natType && natRule.GatewayNatRule.OriginalIP == externalIP && natRule.GatewayNatRule.OriginalPort == externalPort && - interfaceId == networkHrefId { + natRule.GatewayNatRule.Interface.HREF == uplink.HREF { util.Logger.Printf("[DEBUG] REMOVING %s Rule: %#v", natRule.RuleType, natRule.GatewayNatRule) continue } @@ -200,82 +200,73 @@ func (eGW *EdgeGateway) RemoveNATPortRule(networkHref, natType, externalIP, exte } -// Temporary fix to support parsing network Id from HREF -// TODO: remove this function when ruleId is available -func extractObjectIDfromPath(locationPath string) (string, error) { - if locationPath == "" { - return "", fmt.Errorf("unable to get ID from empty path") +// RemoveNATRule removes NAT rule and handles task. Return errors when issue risen. +// Old functions RemoveNATPortMapping and RemoveNATMapping removes using rule details and expects interface is external network. +func (eGW *EdgeGateway) RemoveNATRule(id string) error { + task, err := eGW.RemoveNATRuleAsync(id) + if err != nil { + return fmt.Errorf("error creating DNAT rule: %#v", err) } - - cleanPath := path.Clean(locationPath) // Removes trailing slash if there is one - splitPath := strings.Split(cleanPath, "/") - - if len(splitPath) < 2 { - return "", fmt.Errorf("path does not contain url path: %s", splitPath) + err = task.WaitTaskCompletion() + if err != nil { + return fmt.Errorf("%s", combinedTaskErrorMessage(task.Task, err)) } - objectID := splitPath[len(splitPath)-1] - - return objectID, nil -} - -func (eGW *EdgeGateway) RemoveNATMapping(natType, externalIP, internalIP, port string) (Task, error) { - return eGW.RemoveNATPortMapping(natType, externalIP, port, internalIP, port) + return nil } -func (eGW *EdgeGateway) RemoveNATPortMapping(natType, externalIP, externalPort, internalIP, internalPort string) (Task, error) { - // Find uplink interface - var uplink types.Reference - for _, gi := range eGW.EdgeGateway.Configuration.GatewayInterfaces.GatewayInterface { - if gi.InterfaceType != "uplink" { - continue - } - uplink = *gi.Network +// RemoveNATRuleAsync removes NAT rule and return or error. +// Old functions RemoveNATPortMapping and RemoveNATMapping +// removes using rule details and expects interface is external network. +func (eGW *EdgeGateway) RemoveNATRuleAsync(id string) (Task, error) { + if "" == id { + return Task{}, fmt.Errorf("provided id is empty") } - newEdgeConfig := eGW.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration - - // Take care of the NAT service - newNatService := &types.NatService{} - - newNatService.IsEnabled = newEdgeConfig.NatService.IsEnabled - newNatService.NatType = newEdgeConfig.NatService.NatType - newNatService.Policy = newEdgeConfig.NatService.Policy - newNatService.ExternalIP = newEdgeConfig.NatService.ExternalIP - - for _, natRule := range newEdgeConfig.NatService.NatRule { + err := eGW.Refresh() + if err != nil { + return Task{}, fmt.Errorf("error refreshing edge gateway: %#v", err) + } - if natRule.RuleType == natType && - natRule.GatewayNatRule.OriginalIP == externalIP && - natRule.GatewayNatRule.OriginalPort == externalPort && - natRule.GatewayNatRule.Interface.HREF == uplink.HREF { - util.Logger.Printf("[DEBUG] REMOVING %s Rule: %#v", natRule.RuleType, natRule.GatewayNatRule) - continue + natServiceToUpdate := eGW.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService + ruleIndex := -1 + if natServiceToUpdate != nil { + for n, existingNatRule := range natServiceToUpdate.NatRule { + if existingNatRule.ID == id { + ruleIndex = n + break + } } - util.Logger.Printf("[DEBUG] KEEPING %s Rule: %#v", natRule.RuleType, natRule.GatewayNatRule) - newNatService.NatRule = append(newNatService.NatRule, natRule) + } else { + return Task{}, fmt.Errorf("edge gtw doesn't have NAT rules") } - newEdgeConfig.NatService = newNatService + if ruleIndex == -1 { + return Task{}, fmt.Errorf("edge gtw doesn't have rule with such Id") + } + + natServiceToUpdate.NatRule = append(natServiceToUpdate.NatRule[:ruleIndex], natServiceToUpdate.NatRule[ruleIndex+1:]...) newRules := &types.EdgeGatewayServiceConfiguration{ Xmlns: types.XMLNamespaceVCloud, - NatService: newNatService, + NatService: natServiceToUpdate, } - apiEndpoint, _ := url.ParseRequestURI(eGW.EdgeGateway.HREF) - apiEndpoint.Path += "/action/configureServices" + egwConfigureHref, _ := url.ParseRequestURI(eGW.EdgeGateway.HREF) + egwConfigureHref.Path += "/action/configureServices" // Return the task - return eGW.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPost, + return eGW.client.ExecuteTaskRequest(egwConfigureHref.String(), http.MethodPost, "application/vnd.vmware.admin.edgeGatewayServiceConfiguration+xml", "error reconfiguring Edge Gateway: %s", newRules) - } -// AddDNATRule creates DNAT rule and return refreshed existing NAT rules array or error -// Allows to assign specific network Org VDC or external. Old function AddNATPortMapping and -// AddNATMapping assigns rule to first external network -func (eGW *EdgeGateway) AddDNATRule(ruleDetails NatRule) ([]*types.NatRule, error) { +// AddDNATRule creates DNAT rule and return created NAT struct or error. +// Allows to assign specific Org VDC or external network. +// Old functions AddNATPortMapping and AddNATMapping assigns rule only to first external network +func (eGW *EdgeGateway) AddDNATRule(ruleDetails NatRule) (*types.NatRule, error) { + mappingId := getPseudoUuid() + originalDescription := ruleDetails.Description + ruleDetails.Description = mappingId ruleDetails.natType = "DNAT" task, err := eGW.AddNATRuleAsync(ruleDetails) @@ -287,22 +278,38 @@ func (eGW *EdgeGateway) AddDNATRule(ruleDetails NatRule) ([]*types.NatRule, erro return nil, fmt.Errorf("%s", combinedTaskErrorMessage(task.Task, err)) } + var createdNatRule *types.NatRule + err = eGW.Refresh() if err != nil { return nil, fmt.Errorf("error refreshing edge gateway: %#v", err) } - return eGW.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService.NatRule, nil + for _, natRule := range eGW.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService.NatRule { + if natRule.Description == mappingId { + createdNatRule = natRule + break + } + } + + if createdNatRule == nil { + return nil, fmt.Errorf("error creating SNAT rule, didn't matched created rule") + } + + createdNatRule.Description = originalDescription + + return eGW.UpdateNatRule(createdNatRule) } -// AddSNATRule creates SNAT rule and returns refreshed existing NAT rules array or error -// Allows to assign specific network Org VDC or external. Old function AddNATPortMapping and -// AddNATMapping assigns rule to first external network -func (eGW *EdgeGateway) AddSNATRule(networkHref, externalIP, internalIP, description string) ([]*types.NatRule, error) { +// AddSNATRule creates SNAT rule and returns created NAT rule or error. +// Allows to assign specific Org VDC or external network. +// Old function AddNATPortMapping and AddNATMapping assigns rule to only first external network +func (eGW *EdgeGateway) AddSNATRule(networkHref, externalIP, internalIP, description string) (*types.NatRule, error) { + mappingId := getPseudoUuid() - task, err := eGW.AddNATRuleAsync(NatRule{networkHref: networkHref, natType: "SNAT", externalIP: externalIP, - externalPort: "any", internalIP: internalIP, internalPort: "any", - icmpSubType: "", protocol: "any", description: description}) + task, err := eGW.AddNATRuleAsync(NatRule{NetworkHref: networkHref, natType: "SNAT", ExternalIP: externalIP, + ExternalPort: "any", InternalIP: internalIP, InternalPort: "any", + IcmpSubType: "", Protocol: "any", Description: mappingId}) if err != nil { return nil, fmt.Errorf("error creating SNAT rule: %#v", err) } @@ -311,87 +318,173 @@ func (eGW *EdgeGateway) AddSNATRule(networkHref, externalIP, internalIP, descrip return nil, fmt.Errorf("%s", combinedTaskErrorMessage(task.Task, err)) } + var createdNatRule *types.NatRule + err = eGW.Refresh() if err != nil { return nil, fmt.Errorf("error refreshing edge gateway: %#v", err) } - return eGW.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService.NatRule, nil + for _, natRule := range eGW.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService.NatRule { + if natRule.Description == mappingId { + createdNatRule = natRule + break + } + } + + if createdNatRule == nil { + return nil, fmt.Errorf("error creating SNAT rule, didn't matched created rule") + } + + createdNatRule.Description = description + + return eGW.UpdateNatRule(createdNatRule) +} + +// Creates unique ID/UUID +func getPseudoUuid() (uuid string) { + + b := make([]byte, 16) + _, err := rand.Read(b) + if err != nil { + fmt.Println("Error: ", err) + return + } + + uuid = fmt.Sprintf("%X-%X-%X-%X-%X", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]) + + return +} + +// UpdateNatRule updates NAT rule and handles task. Returns updated NAT rule or error. +func (eGW *EdgeGateway) UpdateNatRule(natRule *types.NatRule) (*types.NatRule, error) { + task, err := eGW.UpdateNatRuleAsync(natRule) + if err != nil { + return nil, fmt.Errorf("error updating SNAT rule: %#v", err) + } + err = task.WaitTaskCompletion() + if err != nil { + return nil, fmt.Errorf("%s", combinedTaskErrorMessage(task.Task, err)) + } + + return eGW.FetchNatRule(natRule.ID) +} + +// UpdateNatRuleAsync updates NAT rule and returns task or error. +func (eGW *EdgeGateway) UpdateNatRuleAsync(natRule *types.NatRule) (Task, error) { + if "" != natRule.GatewayNatRule.Protocol && !isValidProtocol(natRule.GatewayNatRule.Protocol) { + return Task{}, fmt.Errorf("provided protocol is not one of TCP, UDP, TCPUDP, ICMP, ANY") + } + + if strings.ToUpper(natRule.GatewayNatRule.Protocol) == "ICMP" && !isValidIcmpSubType(natRule.GatewayNatRule.IcmpSubType) { + return Task{}, fmt.Errorf("provided icmp sub type is not correct") + } + + err := eGW.Refresh() + if err != nil { + return Task{}, fmt.Errorf("error refreshing edge gateway: %#v", err) + } + + natServiceToUpdate := eGW.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService + + if natServiceToUpdate != nil { + for n, existingNatRule := range natServiceToUpdate.NatRule { + if existingNatRule.ID == natRule.ID { + natServiceToUpdate.NatRule[n] = natRule + } + } + } else { + return Task{}, fmt.Errorf("edge gtw doesn't have such nat rule") + } + + newRules := &types.EdgeGatewayServiceConfiguration{ + Xmlns: types.XMLNamespaceVCloud, + NatService: natServiceToUpdate, + } + + egwConfigureHref, _ := url.ParseRequestURI(eGW.EdgeGateway.HREF) + egwConfigureHref.Path += "/action/configureServices" + + // Return the task + return eGW.client.ExecuteTaskRequest(egwConfigureHref.String(), http.MethodPost, + "application/vnd.vmware.admin.edgeGatewayServiceConfiguration+xml", "error reconfiguring Edge Gateway: %s", newRules) +} + +// FetchNatRule returns NAT rule or error. +func (eGW *EdgeGateway) FetchNatRule(id string) (*types.NatRule, error) { + err := eGW.Refresh() + if err != nil { + return nil, fmt.Errorf("error refreshing edge gateway: %#v", err) + } + + if eGW.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService != nil { + for _, natRule := range eGW.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService.NatRule { + if natRule.ID == id { + return natRule, nil + } + } + } + + return nil, ErrorEntityNotFound } // AddNATRuleAsync creates NAT rule and return task or err // Allows to assign specific network Org VDC or external. Old function AddNATPortMapping and // AddNATMapping assigns rule to first external network func (eGW *EdgeGateway) AddNATRuleAsync(ruleDetails NatRule) (Task, error) { - if !isValidProtocol(ruleDetails.protocol) { + if !isValidProtocol(ruleDetails.Protocol) { return Task{}, fmt.Errorf("provided protocol is not one of TCP, UDP, TCPUDP, ICMP, ANY") } - if strings.ToUpper(ruleDetails.protocol) == "ICMP" && !isValidIcmpSubType(ruleDetails.icmpSubType) { + if strings.ToUpper(ruleDetails.Protocol) == "ICMP" && !isValidIcmpSubType(ruleDetails.IcmpSubType) { return Task{}, fmt.Errorf("provided icmp sub type is not correct") } - newEdgeConfig := eGW.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration + currentEdgeConfig := eGW.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration // Take care of the NAT service newNatService := &types.NatService{} - if newEdgeConfig.NatService == nil { + if currentEdgeConfig.NatService == nil { newNatService.IsEnabled = true } else { - newNatService.IsEnabled = newEdgeConfig.NatService.IsEnabled - newNatService.NatType = newEdgeConfig.NatService.NatType - newNatService.Policy = newEdgeConfig.NatService.Policy - newNatService.ExternalIP = newEdgeConfig.NatService.ExternalIP - - for _, natRule := range newEdgeConfig.NatService.NatRule { - - // Kludgy IF to avoid deleting DNAT rules not created by us. - // If matches, let's skip it and continue the loop - if natRule.RuleType == ruleDetails.natType && - natRule.GatewayNatRule.OriginalIP == ruleDetails.externalIP && - natRule.GatewayNatRule.OriginalPort == ruleDetails.externalPort && - natRule.GatewayNatRule.TranslatedIP == ruleDetails.internalIP && - natRule.GatewayNatRule.TranslatedPort == ruleDetails.internalPort && - natRule.GatewayNatRule.Interface.HREF == ruleDetails.networkHref { - continue - } - - newNatService.NatRule = append(newNatService.NatRule, natRule) - } + newNatService.IsEnabled = currentEdgeConfig.NatService.IsEnabled + newNatService.NatType = currentEdgeConfig.NatService.NatType + newNatService.Policy = currentEdgeConfig.NatService.Policy + newNatService.ExternalIP = currentEdgeConfig.NatService.ExternalIP + newNatService.NatRule = currentEdgeConfig.NatService.NatRule } - //add rule + //construct new rule natRule := &types.NatRule{ RuleType: ruleDetails.natType, IsEnabled: true, - Description: ruleDetails.description, + Description: ruleDetails.Description, GatewayNatRule: &types.GatewayNatRule{ Interface: &types.Reference{ - HREF: ruleDetails.networkHref, + HREF: ruleDetails.NetworkHref, }, - OriginalIP: ruleDetails.externalIP, - OriginalPort: ruleDetails.externalPort, - TranslatedIP: ruleDetails.internalIP, - TranslatedPort: ruleDetails.internalPort, - Protocol: ruleDetails.protocol, - IcmpSubType: ruleDetails.icmpSubType, + OriginalIP: ruleDetails.ExternalIP, + OriginalPort: ruleDetails.ExternalPort, + TranslatedIP: ruleDetails.InternalIP, + TranslatedPort: ruleDetails.InternalPort, + Protocol: ruleDetails.Protocol, + IcmpSubType: ruleDetails.IcmpSubType, }, } - newNatService.NatRule = append(newNatService.NatRule, natRule) - - newEdgeConfig.NatService = newNatService + newNatService.NatRule = append(newNatService.NatRule, natRule) + currentEdgeConfig.NatService = newNatService newRules := &types.EdgeGatewayServiceConfiguration{ Xmlns: types.XMLNamespaceVCloud, NatService: newNatService, } - apiEndpoint, _ := url.ParseRequestURI(eGW.EdgeGateway.HREF) - apiEndpoint.Path += "/action/configureServices" + egwConfigureHref, _ := url.ParseRequestURI(eGW.EdgeGateway.HREF) + egwConfigureHref.Path += "/action/configureServices" // Return the task - return eGW.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPost, + return eGW.client.ExecuteTaskRequest(egwConfigureHref.String(), http.MethodPost, "application/vnd.vmware.admin.edgeGatewayServiceConfiguration+xml", "error reconfiguring Edge Gateway: %s", newRules) } diff --git a/govcd/edgegateway_test.go b/govcd/edgegateway_test.go index 5585e9446..df14b3eea 100644 --- a/govcd/edgegateway_test.go +++ b/govcd/edgegateway_test.go @@ -8,6 +8,7 @@ package govcd import ( "github.com/vmware/go-vcloud-director/v2/types/v56" + "strings" . "gopkg.in/check.v1" ) @@ -309,26 +310,16 @@ func (vcd *TestVCD) Test_AddSNATRule(check *C) { beforeChangeNatRulesNumber := len(edge.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService.NatRule) - natRules, err := edge.AddSNATRule(orgVdcNetwork.OrgVDCNetwork.HREF, vcd.config.VCD.ExternalIp, vcd.config.VCD.InternalIp, description1) + natRule, err := edge.AddSNATRule(orgVdcNetwork.OrgVDCNetwork.HREF, vcd.config.VCD.ExternalIp, vcd.config.VCD.InternalIp, description1) check.Assert(err, IsNil) - found := false - var rule *types.NatRule - for _, r := range natRules { - if r.RuleType == "SNAT" && r.GatewayNatRule.Interface.Name == orgVdcNetwork.OrgVDCNetwork.Name { - found = true - rule = r - } - } - - check.Assert(found, Equals, true) - check.Assert(rule.GatewayNatRule.TranslatedIP, Equals, vcd.config.VCD.InternalIp) - check.Assert(rule.GatewayNatRule.OriginalIP, Equals, vcd.config.VCD.ExternalIp) - check.Assert(rule.Description, Equals, description1) + check.Assert(natRule.GatewayNatRule.TranslatedIP, Equals, vcd.config.VCD.InternalIp) + check.Assert(natRule.GatewayNatRule.OriginalIP, Equals, vcd.config.VCD.ExternalIp) + check.Assert(natRule.Description, Equals, description1) + check.Assert(natRule.RuleType, Equals, "SNAT") + check.Assert(strings.Split(natRule.GatewayNatRule.Interface.HREF, "network/")[1], Equals, strings.Split(orgVdcNetwork.OrgVDCNetwork.HREF, "network/")[1]) - task, err := edge.RemoveNATMappingRule(orgVdcNetwork.OrgVDCNetwork.HREF, "SNAT", vcd.config.VCD.ExternalIp, vcd.config.VCD.InternalIp, "") - check.Assert(err, IsNil) - err = task.WaitTaskCompletion() + err = edge.RemoveNATRule(natRule.ID) check.Assert(err, IsNil) // verify delete @@ -338,28 +329,16 @@ func (vcd *TestVCD) Test_AddSNATRule(check *C) { check.Assert(len(edge.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService.NatRule), Equals, beforeChangeNatRulesNumber) // check with external network - natRules, err = edge.AddSNATRule(externalNetwork.ExternalNetwork.HREF, vcd.config.VCD.InternalIp, vcd.config.VCD.ExternalIp, description2) + natRule, err = edge.AddSNATRule(externalNetwork.ExternalNetwork.HREF, vcd.config.VCD.InternalIp, vcd.config.VCD.ExternalIp, description2) check.Assert(err, IsNil) - found = false - for _, r := range natRules { - if r.RuleType == "SNAT" && r.GatewayNatRule.Interface.Name == externalNetwork.ExternalNetwork.Name { - found = true - rule = r - } - } - - check.Assert(found, Equals, true) - check.Assert(rule.GatewayNatRule.TranslatedIP, Equals, vcd.config.VCD.ExternalIp) - check.Assert(rule.GatewayNatRule.OriginalIP, Equals, vcd.config.VCD.InternalIp) - check.Assert(rule.Description, Equals, description2) - - task, err = edge.RemoveNATMappingRule(externalNetwork.ExternalNetwork.HREF, "SNAT", vcd.config.VCD.InternalIp, vcd.config.VCD.ExternalIp, "") - check.Assert(err, IsNil) - err = task.WaitTaskCompletion() - check.Assert(err, IsNil) + check.Assert(natRule.GatewayNatRule.TranslatedIP, Equals, vcd.config.VCD.ExternalIp) + check.Assert(natRule.GatewayNatRule.OriginalIP, Equals, vcd.config.VCD.InternalIp) + check.Assert(natRule.Description, Equals, description2) + check.Assert(natRule.RuleType, Equals, "SNAT") + check.Assert(strings.Split(natRule.GatewayNatRule.Interface.HREF, "network/")[1], Equals, strings.Split(externalNetwork.ExternalNetwork.HREF, "externalnet/")[1]) - err = edge.Refresh() + err = edge.RemoveNATRule(natRule.ID) check.Assert(err, IsNil) // verify delete @@ -401,30 +380,21 @@ func (vcd *TestVCD) Test_AddDNATRule(check *C) { description1 := "my Dnat Description 1" description2 := "my Dnatt Description 2" - natRules, err := edge.AddDNATRule(NatRule{networkHref: orgVdcNetwork.OrgVDCNetwork.HREF, externalIP: vcd.config.VCD.ExternalIp, - externalPort: "1177", internalIP: vcd.config.VCD.InternalIp, internalPort: "77", protocol: "TCP", description: description1}) + natRule, err := edge.AddDNATRule(NatRule{NetworkHref: orgVdcNetwork.OrgVDCNetwork.HREF, ExternalIP: vcd.config.VCD.ExternalIp, + ExternalPort: "1177", InternalIP: vcd.config.VCD.InternalIp, InternalPort: "77", Protocol: "TCP", Description: description1}) check.Assert(err, IsNil) - found := false - var rule *types.NatRule - for _, r := range natRules { - if r.RuleType == "DNAT" && r.GatewayNatRule.Interface.Name == orgVdcNetwork.OrgVDCNetwork.Name { - found = true - rule = r - } - } - - check.Assert(found, Equals, true) - check.Assert(rule.GatewayNatRule.TranslatedIP, Equals, vcd.config.VCD.InternalIp) - check.Assert(rule.GatewayNatRule.TranslatedPort, Equals, "77") - check.Assert(rule.GatewayNatRule.OriginalIP, Equals, vcd.config.VCD.ExternalIp) - check.Assert(rule.GatewayNatRule.OriginalPort, Equals, "1177") - check.Assert(rule.GatewayNatRule.Protocol, Equals, "tcp") - check.Assert(rule.GatewayNatRule.IcmpSubType, Equals, "") + check.Assert(natRule.GatewayNatRule.TranslatedIP, Equals, vcd.config.VCD.InternalIp) + check.Assert(natRule.GatewayNatRule.TranslatedPort, Equals, "77") + check.Assert(natRule.GatewayNatRule.OriginalIP, Equals, vcd.config.VCD.ExternalIp) + check.Assert(natRule.GatewayNatRule.OriginalPort, Equals, "1177") + check.Assert(natRule.GatewayNatRule.Protocol, Equals, "tcp") + check.Assert(natRule.GatewayNatRule.IcmpSubType, Equals, "") + check.Assert(natRule.Description, Equals, description1) + check.Assert(natRule.RuleType, Equals, "DNAT") + check.Assert(strings.Split(natRule.GatewayNatRule.Interface.HREF, "network/")[1], Equals, strings.Split(orgVdcNetwork.OrgVDCNetwork.HREF, "network/")[1]) - task, err := edge.RemoveNATPortRule(orgVdcNetwork.OrgVDCNetwork.HREF, "DNAT", vcd.config.VCD.ExternalIp, "1177", vcd.config.VCD.InternalIp, "77") - check.Assert(err, IsNil) - err = task.WaitTaskCompletion() + err = edge.RemoveNATRule(natRule.ID) check.Assert(err, IsNil) // verify delete @@ -434,29 +404,22 @@ func (vcd *TestVCD) Test_AddDNATRule(check *C) { check.Assert(len(edge.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService.NatRule), Equals, beforeChangeNatRulesNumber) // check with external network - natRules, err = edge.AddDNATRule(NatRule{networkHref: externalNetwork.ExternalNetwork.HREF, externalIP: vcd.config.VCD.ExternalIp, - externalPort: "1188", internalIP: vcd.config.VCD.InternalIp, internalPort: "88", protocol: "TCP", description: description2}) - check.Assert(err, IsNil) - - found = false - for _, r := range natRules { - if r.RuleType == "DNAT" && r.GatewayNatRule.Interface.Name == externalNetwork.ExternalNetwork.Name { - found = true - rule = r - } - } - - check.Assert(found, Equals, true) - check.Assert(rule.GatewayNatRule.TranslatedIP, Equals, vcd.config.VCD.InternalIp) - check.Assert(rule.GatewayNatRule.TranslatedPort, Equals, "88") - check.Assert(rule.GatewayNatRule.OriginalIP, Equals, vcd.config.VCD.ExternalIp) - check.Assert(rule.GatewayNatRule.OriginalPort, Equals, "1188") - check.Assert(rule.GatewayNatRule.Protocol, Equals, "tcp") - check.Assert(rule.GatewayNatRule.IcmpSubType, Equals, "") - - task, err = edge.RemoveNATPortRule(externalNetwork.ExternalNetwork.HREF, "DNAT", vcd.config.VCD.ExternalIp, "1188", vcd.config.VCD.InternalIp, "88") - check.Assert(err, IsNil) - err = task.WaitTaskCompletion() + natRule, err = edge.AddDNATRule(NatRule{NetworkHref: externalNetwork.ExternalNetwork.HREF, ExternalIP: vcd.config.VCD.ExternalIp, + ExternalPort: "1188", InternalIP: vcd.config.VCD.InternalIp, InternalPort: "88", Protocol: "TCP", Description: description2}) + check.Assert(err, IsNil) + + check.Assert(natRule.GatewayNatRule.TranslatedIP, Equals, vcd.config.VCD.InternalIp) + check.Assert(natRule.GatewayNatRule.TranslatedPort, Equals, "88") + check.Assert(natRule.GatewayNatRule.OriginalIP, Equals, vcd.config.VCD.ExternalIp) + check.Assert(natRule.GatewayNatRule.OriginalPort, Equals, "1188") + check.Assert(natRule.GatewayNatRule.Protocol, Equals, "tcp") + check.Assert(natRule.GatewayNatRule.IcmpSubType, Equals, "") + check.Assert(natRule.Description, Equals, description2) + check.Assert(natRule.RuleType, Equals, "DNAT") + //check.Assert(natRule.GatewayNatRule.Interface.HREF, Equals, externalNetwork.ExternalNetwork.HREF) + check.Assert(strings.Split(natRule.GatewayNatRule.Interface.HREF, "network/")[1], Equals, strings.Split(externalNetwork.ExternalNetwork.HREF, "externalnet/")[1]) + + err = edge.RemoveNATRule(natRule.ID) check.Assert(err, IsNil) // verify delete From ab8bf72d729bcaab4a8881ad9ee799895decd819 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Mon, 15 Jul 2019 14:02:22 +0300 Subject: [PATCH 08/22] add comment Signed-off-by: Vaidotas Bauzys --- govcd/edgegateway.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/govcd/edgegateway.go b/govcd/edgegateway.go index 11d05f068..cf79dfb41 100644 --- a/govcd/edgegateway.go +++ b/govcd/edgegateway.go @@ -262,6 +262,8 @@ func (eGW *EdgeGateway) RemoveNATRuleAsync(id string) (Task, error) { // AddDNATRule creates DNAT rule and return created NAT struct or error. // Allows to assign specific Org VDC or external network. +// When edge gtw is advanced vCD API uses element to map with NSX edge gtw ID. Currently existing issue, +// that changed rule using UI resets as so mapping and as result fetched NatRule.ID won't be valid anymore. // Old functions AddNATPortMapping and AddNATMapping assigns rule only to first external network func (eGW *EdgeGateway) AddDNATRule(ruleDetails NatRule) (*types.NatRule, error) { mappingId := getPseudoUuid() From 2c35536e6def7527a2ab7cf6613c558a7108a834 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Mon, 15 Jul 2019 14:13:02 +0300 Subject: [PATCH 09/22] Add comments Signed-off-by: Vaidotas Bauzys --- CHANGELOG.md | 1 + govcd/edgegateway.go | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e238bf6ba..4bd8a2be6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ * Added org user create/delete/update functions [#18](https://github.com/vmware/go-vcloud-director/issues/18) * Added load balancer application profile [#208](https://github.com/vmware/go-vcloud-director/pull/208) * Added edge gateway SNAT/DNAT rule functions which support org VDC network and external network [#225](https://github.com/terraform-providers/terraform-provider-vcd/issues/225) +* Added edge gateway SNAT/DNAT rule functions which work with IDs [#244](https://github.com/terraform-providers/terraform-provider-vcd/issues/244) ## 2.2.0 (May 15, 2019) diff --git a/govcd/edgegateway.go b/govcd/edgegateway.go index cf79dfb41..6a4a1589d 100644 --- a/govcd/edgegateway.go +++ b/govcd/edgegateway.go @@ -307,6 +307,12 @@ func (eGW *EdgeGateway) AddDNATRule(ruleDetails NatRule) (*types.NatRule, error) // Allows to assign specific Org VDC or external network. // Old function AddNATPortMapping and AddNATMapping assigns rule to only first external network func (eGW *EdgeGateway) AddSNATRule(networkHref, externalIP, internalIP, description string) (*types.NatRule, error) { + + // As vCD API don't return rule API we fetch it manually: + // * create rule with description which value is our generated Id + // * find rule which has description with our generated Id + // * update description with real value and return nat rule + mappingId := getPseudoUuid() task, err := eGW.AddNATRuleAsync(NatRule{NetworkHref: networkHref, natType: "SNAT", ExternalIP: externalIP, From 0bbd623eead2c4fc13c3456a05bef52aee8fcbe8 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Tue, 16 Jul 2019 11:30:32 +0300 Subject: [PATCH 10/22] Fixes Signed-off-by: Vaidotas Bauzys --- govcd/edgegateway.go | 38 ++++++++++++++++++++++---------------- govcd/edgegateway_test.go | 7 +++---- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/govcd/edgegateway.go b/govcd/edgegateway.go index 6a4a1589d..bf6d30d41 100644 --- a/govcd/edgegateway.go +++ b/govcd/edgegateway.go @@ -205,7 +205,7 @@ func (eGW *EdgeGateway) RemoveNATPortMapping(natType, externalIP, externalPort, func (eGW *EdgeGateway) RemoveNATRule(id string) error { task, err := eGW.RemoveNATRuleAsync(id) if err != nil { - return fmt.Errorf("error creating DNAT rule: %#v", err) + return fmt.Errorf("error removing DNAT rule: %#v", err) } err = task.WaitTaskCompletion() if err != nil { @@ -238,14 +238,18 @@ func (eGW *EdgeGateway) RemoveNATRuleAsync(id string) (Task, error) { } } } else { - return Task{}, fmt.Errorf("edge gtw doesn't have NAT rules") + return Task{}, fmt.Errorf("edge gateway doesn't have NAT rules") } if ruleIndex == -1 { - return Task{}, fmt.Errorf("edge gtw doesn't have rule with such Id") + return Task{}, fmt.Errorf("edge gateway doesn't have rule with such Id") } - natServiceToUpdate.NatRule = append(natServiceToUpdate.NatRule[:ruleIndex], natServiceToUpdate.NatRule[ruleIndex+1:]...) + if len(natServiceToUpdate.NatRule) > 1 { + natServiceToUpdate.NatRule = append(natServiceToUpdate.NatRule[:ruleIndex], natServiceToUpdate.NatRule[ruleIndex+1:]...) + } else { + natServiceToUpdate.NatRule = nil + } newRules := &types.EdgeGatewayServiceConfiguration{ Xmlns: types.XMLNamespaceVCloud, @@ -262,11 +266,14 @@ func (eGW *EdgeGateway) RemoveNATRuleAsync(id string) (Task, error) { // AddDNATRule creates DNAT rule and return created NAT struct or error. // Allows to assign specific Org VDC or external network. -// When edge gtw is advanced vCD API uses element to map with NSX edge gtw ID. Currently existing issue, +// When edge gateway is advanced vCD API uses element to map with NSX edge gateway ID. Currently existing issue, // that changed rule using UI resets as so mapping and as result fetched NatRule.ID won't be valid anymore. // Old functions AddNATPortMapping and AddNATMapping assigns rule only to first external network func (eGW *EdgeGateway) AddDNATRule(ruleDetails NatRule) (*types.NatRule, error) { - mappingId := getPseudoUuid() + mappingId, err := getPseudoUuid() + if err != nil { + return nil, err + } originalDescription := ruleDetails.Description ruleDetails.Description = mappingId @@ -295,7 +302,7 @@ func (eGW *EdgeGateway) AddDNATRule(ruleDetails NatRule) (*types.NatRule, error) } if createdNatRule == nil { - return nil, fmt.Errorf("error creating SNAT rule, didn't matched created rule") + return nil, fmt.Errorf("error creating DNAT rule, didn't match created rule") } createdNatRule.Description = originalDescription @@ -341,7 +348,7 @@ func (eGW *EdgeGateway) AddSNATRule(networkHref, externalIP, internalIP, descrip } if createdNatRule == nil { - return nil, fmt.Errorf("error creating SNAT rule, didn't matched created rule") + return nil, fmt.Errorf("error creating SNAT rule, didn't match created rule") } createdNatRule.Description = description @@ -349,26 +356,25 @@ func (eGW *EdgeGateway) AddSNATRule(networkHref, externalIP, internalIP, descrip return eGW.UpdateNatRule(createdNatRule) } -// Creates unique ID/UUID -func getPseudoUuid() (uuid string) { +// getPseudoUuid creates unique ID/UUID +func getPseudoUuid() (string, error) { b := make([]byte, 16) _, err := rand.Read(b) if err != nil { - fmt.Println("Error: ", err) - return + return "", err } - uuid = fmt.Sprintf("%X-%X-%X-%X-%X", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]) + uuid := fmt.Sprintf("%X-%X-%X-%X-%X", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]) - return + return uuid, nil } // UpdateNatRule updates NAT rule and handles task. Returns updated NAT rule or error. func (eGW *EdgeGateway) UpdateNatRule(natRule *types.NatRule) (*types.NatRule, error) { task, err := eGW.UpdateNatRuleAsync(natRule) if err != nil { - return nil, fmt.Errorf("error updating SNAT rule: %#v", err) + return nil, fmt.Errorf("error updating NAT rule: %#v", err) } err = task.WaitTaskCompletion() if err != nil { @@ -402,7 +408,7 @@ func (eGW *EdgeGateway) UpdateNatRuleAsync(natRule *types.NatRule) (Task, error) } } } else { - return Task{}, fmt.Errorf("edge gtw doesn't have such nat rule") + return Task{}, fmt.Errorf("edge gateway doesn't have such nat rule") } newRules := &types.EdgeGatewayServiceConfiguration{ diff --git a/govcd/edgegateway_test.go b/govcd/edgegateway_test.go index df14b3eea..3e713ffe6 100644 --- a/govcd/edgegateway_test.go +++ b/govcd/edgegateway_test.go @@ -67,7 +67,7 @@ func (vcd *TestVCD) Test_NATMapping(check *C) { check.Assert(rule.GatewayNatRule.TranslatedIP, Equals, vcd.config.VCD.InternalIp) check.Assert(rule.GatewayNatRule.OriginalIP, Equals, vcd.config.VCD.ExternalIp) - task, err = edge.RemoveNATMapping("DNAT", vcd.config.VCD.ExternalIp, vcd.config.VCD.InternalIp, "77") + task, err = edge.Remove1to1Mapping(vcd.config.VCD.InternalIp, vcd.config.VCD.ExternalIp) check.Assert(err, IsNil) err = task.WaitTaskCompletion() check.Assert(err, IsNil) @@ -93,7 +93,7 @@ func (vcd *TestVCD) Test_NATPortMapping(check *C) { check.Assert(err, IsNil) check.Assert(orgVdcNetwork.OrgVDCNetwork.Name, Equals, vcd.config.VCD.Network.Net1) - task, err := edge.AddNATPortMappingWithUplink(orgVdcNetwork.OrgVDCNetwork, "DNAT", vcd.config.VCD.ExternalIp, "1177", vcd.config.VCD.InternalIp, "77", "TCP", "") + task, err := edge.AddNATPortMappingWithUplink(orgVdcNetwork.OrgVDCNetwork, "DNAT", vcd.config.VCD.ExternalIp, "any", vcd.config.VCD.InternalIp, "any", "any", "") check.Assert(err, IsNil) err = task.WaitTaskCompletion() check.Assert(err, IsNil) @@ -117,7 +117,7 @@ func (vcd *TestVCD) Test_NATPortMapping(check *C) { check.Assert(rule.GatewayNatRule.Protocol, Equals, "tcp") check.Assert(rule.GatewayNatRule.IcmpSubType, Equals, "") - task, err = edge.RemoveNATPortMapping("DNAT", vcd.config.VCD.ExternalIp, "1177", vcd.config.VCD.InternalIp, "77") + task, err = edge.Remove1to1Mapping(vcd.config.VCD.InternalIp, vcd.config.VCD.ExternalIp) check.Assert(err, IsNil) err = task.WaitTaskCompletion() check.Assert(err, IsNil) @@ -416,7 +416,6 @@ func (vcd *TestVCD) Test_AddDNATRule(check *C) { check.Assert(natRule.GatewayNatRule.IcmpSubType, Equals, "") check.Assert(natRule.Description, Equals, description2) check.Assert(natRule.RuleType, Equals, "DNAT") - //check.Assert(natRule.GatewayNatRule.Interface.HREF, Equals, externalNetwork.ExternalNetwork.HREF) check.Assert(strings.Split(natRule.GatewayNatRule.Interface.HREF, "network/")[1], Equals, strings.Split(externalNetwork.ExternalNetwork.HREF, "externalnet/")[1]) err = edge.RemoveNATRule(natRule.ID) From 068b848256ff9ce4bda6a6c625cbd43c2e3b9575 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Tue, 16 Jul 2019 11:33:48 +0300 Subject: [PATCH 11/22] Fixes Signed-off-by: Vaidotas Bauzys --- govcd/edgegateway.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/govcd/edgegateway.go b/govcd/edgegateway.go index bf6d30d41..f828ea070 100644 --- a/govcd/edgegateway.go +++ b/govcd/edgegateway.go @@ -320,7 +320,10 @@ func (eGW *EdgeGateway) AddSNATRule(networkHref, externalIP, internalIP, descrip // * find rule which has description with our generated Id // * update description with real value and return nat rule - mappingId := getPseudoUuid() + mappingId, err := getPseudoUuid() + if err != nil { + return nil, err + } task, err := eGW.AddNATRuleAsync(NatRule{NetworkHref: networkHref, natType: "SNAT", ExternalIP: externalIP, ExternalPort: "any", InternalIP: internalIP, InternalPort: "any", From f37fe1bb2cb906dd99c89721cdf911b7c93a8859 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Tue, 16 Jul 2019 12:28:31 +0300 Subject: [PATCH 12/22] Added test for update Signed-off-by: Vaidotas Bauzys --- govcd/edgegateway_test.go | 106 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/govcd/edgegateway_test.go b/govcd/edgegateway_test.go index 3e713ffe6..6e0fb7340 100644 --- a/govcd/edgegateway_test.go +++ b/govcd/edgegateway_test.go @@ -427,3 +427,109 @@ func (vcd *TestVCD) Test_AddDNATRule(check *C) { check.Assert(len(edge.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService.NatRule), Equals, beforeChangeNatRulesNumber) } + +func (vcd *TestVCD) Test_UpdateNATRule(check *C) { + if vcd.config.VCD.ExternalIp == "" || vcd.config.VCD.InternalIp == "" { + check.Skip("Skipping test because no valid ip given") + } + if vcd.config.VCD.ExternalNetwork == "" { + check.Skip("Skipping test because no external network given") + } + if vcd.config.VCD.EdgeGateway == "" { + check.Skip("Skipping test because no edge gateway given") + } + if vcd.config.VCD.Network.Net1 == "" { + check.Skip("Skipping test because no network was given") + } + + edge, err := vcd.vdc.FindEdgeGateway(vcd.config.VCD.EdgeGateway) + check.Assert(err, IsNil) + check.Assert(edge.EdgeGateway.Name, Equals, vcd.config.VCD.EdgeGateway) + + orgVdcNetwork, err := vcd.vdc.FindVDCNetwork(vcd.config.VCD.Network.Net1) + check.Assert(err, IsNil) + check.Assert(orgVdcNetwork.OrgVDCNetwork.Name, Equals, vcd.config.VCD.Network.Net1) + + externalNetwork, err := GetExternalNetwork(vcd.client, vcd.config.VCD.ExternalNetwork) + check.Assert(err, IsNil) + check.Assert(externalNetwork.ExternalNetwork.Name, Equals, vcd.config.VCD.ExternalNetwork) + + beforeChangeNatRulesNumber := len(edge.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService.NatRule) + + description1 := "my Dnat Description 1" + description2 := "my Dnatt Description 2" + + natRule, err := edge.AddDNATRule(NatRule{NetworkHref: orgVdcNetwork.OrgVDCNetwork.HREF, ExternalIP: vcd.config.VCD.ExternalIp, + ExternalPort: "1177", InternalIP: vcd.config.VCD.InternalIp, InternalPort: "77", Protocol: "TCP", Description: description1}) + check.Assert(err, IsNil) + + check.Assert(natRule.GatewayNatRule.TranslatedIP, Equals, vcd.config.VCD.InternalIp) + check.Assert(natRule.GatewayNatRule.TranslatedPort, Equals, "77") + check.Assert(natRule.GatewayNatRule.OriginalIP, Equals, vcd.config.VCD.ExternalIp) + check.Assert(natRule.GatewayNatRule.OriginalPort, Equals, "1177") + check.Assert(natRule.GatewayNatRule.Protocol, Equals, "tcp") + check.Assert(natRule.GatewayNatRule.IcmpSubType, Equals, "") + check.Assert(natRule.Description, Equals, description1) + check.Assert(natRule.RuleType, Equals, "DNAT") + check.Assert(strings.Split(natRule.GatewayNatRule.Interface.HREF, "network/")[1], Equals, strings.Split(orgVdcNetwork.OrgVDCNetwork.HREF, "network/")[1]) + + err = edge.RemoveNATRule(natRule.ID) + check.Assert(err, IsNil) + + // verify delete + err = edge.Refresh() + check.Assert(err, IsNil) + + check.Assert(len(edge.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService.NatRule), Equals, beforeChangeNatRulesNumber) + + // check with external network + natRule, err = edge.AddDNATRule(NatRule{NetworkHref: externalNetwork.ExternalNetwork.HREF, ExternalIP: vcd.config.VCD.ExternalIp, + ExternalPort: "1188", InternalIP: vcd.config.VCD.InternalIp, InternalPort: "88", Protocol: "TCP", Description: description2}) + check.Assert(err, IsNil) + + check.Assert(natRule.GatewayNatRule.TranslatedIP, Equals, vcd.config.VCD.InternalIp) + check.Assert(natRule.GatewayNatRule.TranslatedPort, Equals, "88") + check.Assert(natRule.GatewayNatRule.OriginalIP, Equals, vcd.config.VCD.ExternalIp) + check.Assert(natRule.GatewayNatRule.OriginalPort, Equals, "1188") + check.Assert(natRule.GatewayNatRule.Protocol, Equals, "tcp") + check.Assert(natRule.GatewayNatRule.IcmpSubType, Equals, "") + check.Assert(natRule.Description, Equals, description2) + check.Assert(natRule.RuleType, Equals, "DNAT") + check.Assert(strings.Split(natRule.GatewayNatRule.Interface.HREF, "network/")[1], Equals, strings.Split(externalNetwork.ExternalNetwork.HREF, "externalnet/")[1]) + + err = edge.RemoveNATRule(natRule.ID) + check.Assert(err, IsNil) + + // update test + natRule, err = edge.AddDNATRule(NatRule{NetworkHref: orgVdcNetwork.OrgVDCNetwork.HREF, ExternalIP: vcd.config.VCD.ExternalIp, + ExternalPort: "1177", InternalIP: vcd.config.VCD.InternalIp, InternalPort: "77", Protocol: "TCP", Description: description1}) + check.Assert(err, IsNil) + + natRule.GatewayNatRule.OriginalPort = "1166" + natRule.GatewayNatRule.TranslatedPort = "66" + natRule.GatewayNatRule.Protocol = "udp" + natRule.Description = description2 + natRule.GatewayNatRule.Interface.HREF = externalNetwork.ExternalNetwork.HREF + + updateNatRule, err := edge.UpdateNatRule(natRule) + + check.Assert(err, IsNil) + check.Assert(updateNatRule.GatewayNatRule.TranslatedIP, Equals, vcd.config.VCD.InternalIp) + check.Assert(updateNatRule.GatewayNatRule.TranslatedPort, Equals, "66") + check.Assert(updateNatRule.GatewayNatRule.OriginalIP, Equals, vcd.config.VCD.ExternalIp) + check.Assert(updateNatRule.GatewayNatRule.OriginalPort, Equals, "1166") + check.Assert(updateNatRule.GatewayNatRule.Protocol, Equals, "udp") + check.Assert(updateNatRule.GatewayNatRule.IcmpSubType, Equals, "") + check.Assert(updateNatRule.Description, Equals, description2) + check.Assert(updateNatRule.RuleType, Equals, "DNAT") + check.Assert(strings.Split(updateNatRule.GatewayNatRule.Interface.HREF, "network/")[1], Equals, strings.Split(externalNetwork.ExternalNetwork.HREF, "externalnet/")[1]) + + err = edge.RemoveNATRule(updateNatRule.ID) + check.Assert(err, IsNil) + + // verify delete + err = edge.Refresh() + check.Assert(err, IsNil) + + check.Assert(len(edge.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService.NatRule), Equals, beforeChangeNatRulesNumber) +} From 5699abb75343178ab29dc3d3eb89c5e290b6213a Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Tue, 16 Jul 2019 17:08:50 +0300 Subject: [PATCH 13/22] Improved comments Signed-off-by: Vaidotas Bauzys --- govcd/edgegateway.go | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/govcd/edgegateway.go b/govcd/edgegateway.go index f828ea070..ee9718c54 100644 --- a/govcd/edgegateway.go +++ b/govcd/edgegateway.go @@ -200,8 +200,9 @@ func (eGW *EdgeGateway) RemoveNATPortMapping(natType, externalIP, externalPort, } -// RemoveNATRule removes NAT rule and handles task. Return errors when issue risen. -// Old functions RemoveNATPortMapping and RemoveNATMapping removes using rule details and expects interface is external network. +// RemoveNATRule removes NAT removes NAT rule identified by ID and handles task. Returns error if issues rise. +// Old functions RemoveNATPortMapping and RemoveNATMapping removed using rule details +// and expected interface to be of external network type. func (eGW *EdgeGateway) RemoveNATRule(id string) error { task, err := eGW.RemoveNATRuleAsync(id) if err != nil { @@ -216,8 +217,8 @@ func (eGW *EdgeGateway) RemoveNATRule(id string) error { } // RemoveNATRuleAsync removes NAT rule and return or error. -// Old functions RemoveNATPortMapping and RemoveNATMapping -// removes using rule details and expects interface is external network. +// Old functions RemoveNATPortMapping and RemoveNATMapping removed using rule details +// and expected interface to be of external network type. func (eGW *EdgeGateway) RemoveNATRuleAsync(id string) (Task, error) { if "" == id { return Task{}, fmt.Errorf("provided id is empty") @@ -267,8 +268,9 @@ func (eGW *EdgeGateway) RemoveNATRuleAsync(id string) (Task, error) { // AddDNATRule creates DNAT rule and return created NAT struct or error. // Allows to assign specific Org VDC or external network. // When edge gateway is advanced vCD API uses element to map with NSX edge gateway ID. Currently existing issue, -// that changed rule using UI resets as so mapping and as result fetched NatRule.ID won't be valid anymore. -// Old functions AddNATPortMapping and AddNATMapping assigns rule only to first external network +// that updating rule using User interface resets and as result mapping is lost. +// Fetching using NatRule.ID won't be valid anymore. +// Old functions AddNATPortMapping and AddNATMapping assigned rule only to first external network func (eGW *EdgeGateway) AddDNATRule(ruleDetails NatRule) (*types.NatRule, error) { mappingId, err := getPseudoUuid() if err != nil { @@ -312,12 +314,13 @@ func (eGW *EdgeGateway) AddDNATRule(ruleDetails NatRule) (*types.NatRule, error) // AddSNATRule creates SNAT rule and returns created NAT rule or error. // Allows to assign specific Org VDC or external network. -// Old function AddNATPortMapping and AddNATMapping assigns rule to only first external network +// Old functions AddNATPortMapping and AddNATMapping aren't correct as assigned rule only to first external network func (eGW *EdgeGateway) AddSNATRule(networkHref, externalIP, internalIP, description string) (*types.NatRule, error) { - // As vCD API don't return rule API we fetch it manually: + // As vCD API doesn't return rule ID we fetch it manually: // * create rule with description which value is our generated Id // * find rule which has description with our generated Id + // * read the real (vCD's) rule Id // * update description with real value and return nat rule mappingId, err := getPseudoUuid() @@ -447,7 +450,7 @@ func (eGW *EdgeGateway) FetchNatRule(id string) (*types.NatRule, error) { // AddNATRuleAsync creates NAT rule and return task or err // Allows to assign specific network Org VDC or external. Old function AddNATPortMapping and -// AddNATMapping assigns rule to first external network +// AddNATMapping function shouldn't be used cause assigns rule to first external network func (eGW *EdgeGateway) AddNATRuleAsync(ruleDetails NatRule) (Task, error) { if !isValidProtocol(ruleDetails.Protocol) { return Task{}, fmt.Errorf("provided protocol is not one of TCP, UDP, TCPUDP, ICMP, ANY") From 3e31d5e509e56b8f488a5a0711f7787ce7606885 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Wed, 17 Jul 2019 09:27:18 +0300 Subject: [PATCH 14/22] Improved comments Signed-off-by: Vaidotas Bauzys --- govcd/edgegateway.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/govcd/edgegateway.go b/govcd/edgegateway.go index ee9718c54..c82ca276f 100644 --- a/govcd/edgegateway.go +++ b/govcd/edgegateway.go @@ -145,12 +145,12 @@ func (eGW *EdgeGateway) AddDhcpPool(network *types.OrgVDCNetwork, dhcppool []int } -// Deprecated in favor of RemoveNATRuleAsync, RemoveNATRule +// Deprecated: use one of RemoveNATRuleAsync, RemoveNATRule func (eGW *EdgeGateway) RemoveNATMapping(natType, externalIP, internalIP, port string) (Task, error) { return eGW.RemoveNATPortMapping(natType, externalIP, port, internalIP, port) } -// Deprecated in favor of RemoveNATRuleAsync, RemoveNATRule +// Deprecated: use one of RemoveNATRuleAsync, RemoveNATRule func (eGW *EdgeGateway) RemoveNATPortMapping(natType, externalIP, externalPort, internalIP, internalPort string) (Task, error) { // Find uplink interface var uplink types.Reference From 83698c0d922e2a10dc0a47062779a975d9114804 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Wed, 17 Jul 2019 13:12:27 +0300 Subject: [PATCH 15/22] Improvements Signed-off-by: Vaidotas Bauzys --- govcd/edgegateway.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/govcd/edgegateway.go b/govcd/edgegateway.go index c82ca276f..c01bb7b2d 100644 --- a/govcd/edgegateway.go +++ b/govcd/edgegateway.go @@ -216,7 +216,7 @@ func (eGW *EdgeGateway) RemoveNATRule(id string) error { return nil } -// RemoveNATRuleAsync removes NAT rule and return or error. +// RemoveNATRuleAsync removes NAT rule or returns an error. // Old functions RemoveNATPortMapping and RemoveNATMapping removed using rule details // and expected interface to be of external network type. func (eGW *EdgeGateway) RemoveNATRuleAsync(id string) (Task, error) { @@ -265,9 +265,9 @@ func (eGW *EdgeGateway) RemoveNATRuleAsync(id string) (Task, error) { "application/vnd.vmware.admin.edgeGatewayServiceConfiguration+xml", "error reconfiguring Edge Gateway: %s", newRules) } -// AddDNATRule creates DNAT rule and return created NAT struct or error. -// Allows to assign specific Org VDC or external network. -// When edge gateway is advanced vCD API uses element to map with NSX edge gateway ID. Currently existing issue, +// AddDNATRule creates DNAT rule and returns the NAT struct that was created or an error. +// Allows assigning a specific Org VDC or an external network. +// When edge gateway is advanced vCD API uses element to map with NSX edge gateway ID. A known issue is // that updating rule using User interface resets and as result mapping is lost. // Fetching using NatRule.ID won't be valid anymore. // Old functions AddNATPortMapping and AddNATMapping assigned rule only to first external network @@ -313,7 +313,7 @@ func (eGW *EdgeGateway) AddDNATRule(ruleDetails NatRule) (*types.NatRule, error) } // AddSNATRule creates SNAT rule and returns created NAT rule or error. -// Allows to assign specific Org VDC or external network. +// Allows assigning a specific Org VDC or an external network. // Old functions AddNATPortMapping and AddNATMapping aren't correct as assigned rule only to first external network func (eGW *EdgeGateway) AddSNATRule(networkHref, externalIP, internalIP, description string) (*types.NatRule, error) { @@ -387,7 +387,7 @@ func (eGW *EdgeGateway) UpdateNatRule(natRule *types.NatRule) (*types.NatRule, e return nil, fmt.Errorf("%s", combinedTaskErrorMessage(task.Task, err)) } - return eGW.FetchNatRule(natRule.ID) + return eGW.ReadNatRule(natRule.ID) } // UpdateNatRuleAsync updates NAT rule and returns task or error. @@ -430,8 +430,8 @@ func (eGW *EdgeGateway) UpdateNatRuleAsync(natRule *types.NatRule) (Task, error) "application/vnd.vmware.admin.edgeGatewayServiceConfiguration+xml", "error reconfiguring Edge Gateway: %s", newRules) } -// FetchNatRule returns NAT rule or error. -func (eGW *EdgeGateway) FetchNatRule(id string) (*types.NatRule, error) { +// ReadNatRule returns NAT rule or error. +func (eGW *EdgeGateway) ReadNatRule(id string) (*types.NatRule, error) { err := eGW.Refresh() if err != nil { return nil, fmt.Errorf("error refreshing edge gateway: %#v", err) @@ -449,8 +449,8 @@ func (eGW *EdgeGateway) FetchNatRule(id string) (*types.NatRule, error) { } // AddNATRuleAsync creates NAT rule and return task or err -// Allows to assign specific network Org VDC or external. Old function AddNATPortMapping and -// AddNATMapping function shouldn't be used cause assigns rule to first external network +// Allows assigning specific network Org VDC or external. Old function AddNATPortMapping and +// AddNATMapping function shouldn't be used because assigns rule to first external network func (eGW *EdgeGateway) AddNATRuleAsync(ruleDetails NatRule) (Task, error) { if !isValidProtocol(ruleDetails.Protocol) { return Task{}, fmt.Errorf("provided protocol is not one of TCP, UDP, TCPUDP, ICMP, ANY") From 90755359bf6885087456688c35e052db4b599cbb Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Wed, 17 Jul 2019 13:48:53 +0300 Subject: [PATCH 16/22] change tests to use new delete Signed-off-by: Vaidotas Bauzys --- govcd/edgegateway_test.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/govcd/edgegateway_test.go b/govcd/edgegateway_test.go index 6e0fb7340..894191a92 100644 --- a/govcd/edgegateway_test.go +++ b/govcd/edgegateway_test.go @@ -67,7 +67,9 @@ func (vcd *TestVCD) Test_NATMapping(check *C) { check.Assert(rule.GatewayNatRule.TranslatedIP, Equals, vcd.config.VCD.InternalIp) check.Assert(rule.GatewayNatRule.OriginalIP, Equals, vcd.config.VCD.ExternalIp) - task, err = edge.Remove1to1Mapping(vcd.config.VCD.InternalIp, vcd.config.VCD.ExternalIp) + //task, err = edge.Remove1to1Mapping(vcd.config.VCD.InternalIp, vcd.config.VCD.ExternalIp) + // Cause Remove1to1Mapping isn't working correctly we will use new function + err = edge.RemoveNATRule(rule.ID) check.Assert(err, IsNil) err = task.WaitTaskCompletion() check.Assert(err, IsNil) @@ -93,7 +95,7 @@ func (vcd *TestVCD) Test_NATPortMapping(check *C) { check.Assert(err, IsNil) check.Assert(orgVdcNetwork.OrgVDCNetwork.Name, Equals, vcd.config.VCD.Network.Net1) - task, err := edge.AddNATPortMappingWithUplink(orgVdcNetwork.OrgVDCNetwork, "DNAT", vcd.config.VCD.ExternalIp, "any", vcd.config.VCD.InternalIp, "any", "any", "") + task, err := edge.AddNATPortMappingWithUplink(orgVdcNetwork.OrgVDCNetwork, "DNAT", vcd.config.VCD.ExternalIp, "1177", vcd.config.VCD.InternalIp, "77", "TCP", "") check.Assert(err, IsNil) err = task.WaitTaskCompletion() check.Assert(err, IsNil) @@ -117,7 +119,10 @@ func (vcd *TestVCD) Test_NATPortMapping(check *C) { check.Assert(rule.GatewayNatRule.Protocol, Equals, "tcp") check.Assert(rule.GatewayNatRule.IcmpSubType, Equals, "") - task, err = edge.Remove1to1Mapping(vcd.config.VCD.InternalIp, vcd.config.VCD.ExternalIp) + //task, err = edge.Remove1to1Mapping(vcd.config.VCD.InternalIp, vcd.config.VCD.ExternalIp) + // Cause Remove1to1Mapping isn't working correctly we will use new function + err = edge.RemoveNATRule(rule.ID) + check.Assert(err, IsNil) err = task.WaitTaskCompletion() check.Assert(err, IsNil) From 6bfe2dfa94b566db8391229d07ca5d1baa617ff9 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Wed, 17 Jul 2019 14:25:24 +0300 Subject: [PATCH 17/22] Add uni test Signed-off-by: Vaidotas Bauzys --- govcd/edgegateway_test.go | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/govcd/edgegateway_test.go b/govcd/edgegateway_test.go index 894191a92..b80695d0e 100644 --- a/govcd/edgegateway_test.go +++ b/govcd/edgegateway_test.go @@ -7,9 +7,11 @@ package govcd import ( - "github.com/vmware/go-vcloud-director/v2/types/v56" + "regexp" "strings" + "testing" + "github.com/vmware/go-vcloud-director/v2/types/v56" . "gopkg.in/check.v1" ) @@ -538,3 +540,23 @@ func (vcd *TestVCD) Test_UpdateNATRule(check *C) { check.Assert(len(edge.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService.NatRule), Equals, beforeChangeNatRulesNumber) } + +func TestGetPseudoUUID(t *testing.T) { + + var seen = make(map[string]int) + + reUuid := regexp.MustCompile(`^[A-F0-9]{8}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{12}$`) + for N := 0; N < 1000; N++ { + uuid, _ := getPseudoUuid() + if !reUuid.MatchString(uuid) { + t.Logf("string %s doesn't look like a UUID", uuid) + t.Fail() + } + previous, found := seen[uuid] + if found { + t.Logf("uuid %s already in the generated list at position %d", uuid, previous) + t.Fail() + } + seen[uuid] = N + } +} From b7d2fd23390dca2b4e935d261440320f3d4f5df9 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Thu, 18 Jul 2019 11:57:36 +0300 Subject: [PATCH 18/22] Changed fetch/read to Get Signed-off-by: Vaidotas Bauzys --- govcd/edgegateway.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/govcd/edgegateway.go b/govcd/edgegateway.go index c01bb7b2d..8263c82c7 100644 --- a/govcd/edgegateway.go +++ b/govcd/edgegateway.go @@ -269,7 +269,7 @@ func (eGW *EdgeGateway) RemoveNATRuleAsync(id string) (Task, error) { // Allows assigning a specific Org VDC or an external network. // When edge gateway is advanced vCD API uses element to map with NSX edge gateway ID. A known issue is // that updating rule using User interface resets and as result mapping is lost. -// Fetching using NatRule.ID won't be valid anymore. +// Getting using NatRule.ID won't be valid anymore. // Old functions AddNATPortMapping and AddNATMapping assigned rule only to first external network func (eGW *EdgeGateway) AddDNATRule(ruleDetails NatRule) (*types.NatRule, error) { mappingId, err := getPseudoUuid() @@ -317,10 +317,10 @@ func (eGW *EdgeGateway) AddDNATRule(ruleDetails NatRule) (*types.NatRule, error) // Old functions AddNATPortMapping and AddNATMapping aren't correct as assigned rule only to first external network func (eGW *EdgeGateway) AddSNATRule(networkHref, externalIP, internalIP, description string) (*types.NatRule, error) { - // As vCD API doesn't return rule ID we fetch it manually: + // As vCD API doesn't return rule ID we get it manually: // * create rule with description which value is our generated Id // * find rule which has description with our generated Id - // * read the real (vCD's) rule Id + // * get the real (vCD's) rule Id // * update description with real value and return nat rule mappingId, err := getPseudoUuid() @@ -387,7 +387,7 @@ func (eGW *EdgeGateway) UpdateNatRule(natRule *types.NatRule) (*types.NatRule, e return nil, fmt.Errorf("%s", combinedTaskErrorMessage(task.Task, err)) } - return eGW.ReadNatRule(natRule.ID) + return eGW.GetNatRule(natRule.ID) } // UpdateNatRuleAsync updates NAT rule and returns task or error. @@ -430,8 +430,8 @@ func (eGW *EdgeGateway) UpdateNatRuleAsync(natRule *types.NatRule) (Task, error) "application/vnd.vmware.admin.edgeGatewayServiceConfiguration+xml", "error reconfiguring Edge Gateway: %s", newRules) } -// ReadNatRule returns NAT rule or error. -func (eGW *EdgeGateway) ReadNatRule(id string) (*types.NatRule, error) { +// GetNatRule returns NAT rule or error. +func (eGW *EdgeGateway) GetNatRule(id string) (*types.NatRule, error) { err := eGW.Refresh() if err != nil { return nil, fmt.Errorf("error refreshing edge gateway: %#v", err) From 13a64fc1b7c5626b7354bb6796a3dfb62206e9d7 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Thu, 18 Jul 2019 15:07:00 +0300 Subject: [PATCH 19/22] Moved test function Signed-off-by: Vaidotas Bauzys --- govcd/edgegateway_test.go | 20 -------------------- govcd/edgegateway_unit_test.go | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 20 deletions(-) create mode 100644 govcd/edgegateway_unit_test.go diff --git a/govcd/edgegateway_test.go b/govcd/edgegateway_test.go index b80695d0e..9cf56c53c 100644 --- a/govcd/edgegateway_test.go +++ b/govcd/edgegateway_test.go @@ -540,23 +540,3 @@ func (vcd *TestVCD) Test_UpdateNATRule(check *C) { check.Assert(len(edge.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService.NatRule), Equals, beforeChangeNatRulesNumber) } - -func TestGetPseudoUUID(t *testing.T) { - - var seen = make(map[string]int) - - reUuid := regexp.MustCompile(`^[A-F0-9]{8}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{12}$`) - for N := 0; N < 1000; N++ { - uuid, _ := getPseudoUuid() - if !reUuid.MatchString(uuid) { - t.Logf("string %s doesn't look like a UUID", uuid) - t.Fail() - } - previous, found := seen[uuid] - if found { - t.Logf("uuid %s already in the generated list at position %d", uuid, previous) - t.Fail() - } - seen[uuid] = N - } -} diff --git a/govcd/edgegateway_unit_test.go b/govcd/edgegateway_unit_test.go new file mode 100644 index 000000000..adb3ee1a2 --- /dev/null +++ b/govcd/edgegateway_unit_test.go @@ -0,0 +1,32 @@ +// +build unit ALL + +/* +* Copyright 2019 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. + */ + +package govcd + +import ( + "regexp" + "testing" +) + +func TestGetPseudoUUID(t *testing.T) { + + var seen = make(map[string]int) + + reUuid := regexp.MustCompile(`^[A-F0-9]{8}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{12}$`) + for N := 0; N < 1000; N++ { + uuid, _ := getPseudoUuid() + if !reUuid.MatchString(uuid) { + t.Logf("string %s doesn't look like a UUID", uuid) + t.Fail() + } + previous, found := seen[uuid] + if found { + t.Logf("uuid %s already in the generated list at position %d", uuid, previous) + t.Fail() + } + seen[uuid] = N + } +} From a686dc8192721bf58655e35e6fb831f9c3a6965a Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Thu, 18 Jul 2019 17:21:17 +0300 Subject: [PATCH 20/22] Remove unused imports Signed-off-by: Vaidotas Bauzys --- govcd/edgegateway_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/govcd/edgegateway_test.go b/govcd/edgegateway_test.go index 9cf56c53c..e9c9b562d 100644 --- a/govcd/edgegateway_test.go +++ b/govcd/edgegateway_test.go @@ -7,9 +7,7 @@ package govcd import ( - "regexp" "strings" - "testing" "github.com/vmware/go-vcloud-director/v2/types/v56" . "gopkg.in/check.v1" From 222404dcbb4143b03d4a9ff23793a391d0db4ac1 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Fri, 19 Jul 2019 10:44:02 +0300 Subject: [PATCH 21/22] Add code improvements Signed-off-by: Vaidotas Bauzys --- govcd/edgegateway.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/govcd/edgegateway.go b/govcd/edgegateway.go index 8263c82c7..92c0de217 100644 --- a/govcd/edgegateway.go +++ b/govcd/edgegateway.go @@ -220,7 +220,7 @@ func (eGW *EdgeGateway) RemoveNATRule(id string) error { // Old functions RemoveNATPortMapping and RemoveNATMapping removed using rule details // and expected interface to be of external network type. func (eGW *EdgeGateway) RemoveNATRuleAsync(id string) (Task, error) { - if "" == id { + if id == "" { return Task{}, fmt.Errorf("provided id is empty") } @@ -392,7 +392,7 @@ func (eGW *EdgeGateway) UpdateNatRule(natRule *types.NatRule) (*types.NatRule, e // UpdateNatRuleAsync updates NAT rule and returns task or error. func (eGW *EdgeGateway) UpdateNatRuleAsync(natRule *types.NatRule) (Task, error) { - if "" != natRule.GatewayNatRule.Protocol && !isValidProtocol(natRule.GatewayNatRule.Protocol) { + if natRule.GatewayNatRule.Protocol != "" && !isValidProtocol(natRule.GatewayNatRule.Protocol) { return Task{}, fmt.Errorf("provided protocol is not one of TCP, UDP, TCPUDP, ICMP, ANY") } From 2100e4eb37481dda1148614c74c8b89dd9d9e75e Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Fri, 19 Jul 2019 13:28:56 +0300 Subject: [PATCH 22/22] Make variable public Signed-off-by: Vaidotas Bauzys --- govcd/edgegateway.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/govcd/edgegateway.go b/govcd/edgegateway.go index 92c0de217..1cdf2ca3e 100644 --- a/govcd/edgegateway.go +++ b/govcd/edgegateway.go @@ -42,7 +42,7 @@ func NewEdgeGateway(cli *Client) *EdgeGateway { // Struct which covers NAT rule fields type NatRule struct { - natType string + NatType string NetworkHref string ExternalIP string ExternalPort string @@ -279,7 +279,7 @@ func (eGW *EdgeGateway) AddDNATRule(ruleDetails NatRule) (*types.NatRule, error) originalDescription := ruleDetails.Description ruleDetails.Description = mappingId - ruleDetails.natType = "DNAT" + ruleDetails.NatType = "DNAT" task, err := eGW.AddNATRuleAsync(ruleDetails) if err != nil { return nil, fmt.Errorf("error creating DNAT rule: %#v", err) @@ -328,7 +328,7 @@ func (eGW *EdgeGateway) AddSNATRule(networkHref, externalIP, internalIP, descrip return nil, err } - task, err := eGW.AddNATRuleAsync(NatRule{NetworkHref: networkHref, natType: "SNAT", ExternalIP: externalIP, + task, err := eGW.AddNATRuleAsync(NatRule{NetworkHref: networkHref, NatType: "SNAT", ExternalIP: externalIP, ExternalPort: "any", InternalIP: internalIP, InternalPort: "any", IcmpSubType: "", Protocol: "any", Description: mappingId}) if err != nil { @@ -477,7 +477,7 @@ func (eGW *EdgeGateway) AddNATRuleAsync(ruleDetails NatRule) (Task, error) { //construct new rule natRule := &types.NatRule{ - RuleType: ruleDetails.natType, + RuleType: ruleDetails.NatType, IsEnabled: true, Description: ruleDetails.Description, GatewayNatRule: &types.GatewayNatRule{