From c26fb78f11ac75b82616cce75eba452afca274b6 Mon Sep 17 00:00:00 2001 From: Dainius S Date: Fri, 30 Aug 2019 12:09:08 +0300 Subject: [PATCH 01/23] WIP --- go.sum | 1 + govcd/nat.go | 120 +++++++++++++++++++++++++++++++++++++++++ govcd/nat_test.go | 32 +++++++++++ types/v56/constants.go | 6 +++ types/v56/types.go | 50 +++++++++++++++++ 5 files changed, 209 insertions(+) create mode 100644 govcd/nat.go create mode 100644 govcd/nat_test.go diff --git a/go.sum b/go.sum index 91cfce57f..af2c6a600 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,7 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/vmware/go-vcloud-director v2.0.0+incompatible h1:3B121XZVdEOxRhv5ARswKVxXt4KznAbun8GoXNbbZWs= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/govcd/nat.go b/govcd/nat.go new file mode 100644 index 000000000..05845dac6 --- /dev/null +++ b/govcd/nat.go @@ -0,0 +1,120 @@ +/* + * Copyright 2019 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. + */ + +package govcd + +import ( + "fmt" + "net/http" + + "github.com/vmware/go-vcloud-director/v2/types/v56" +) + +func (egw *EdgeGateway) CreateSnatRule(snatRuleConfig *types.EdgeSnatRule) (*types.EdgeSnatRule, error) { + if err := validateCreateSnatRule(snatRuleConfig, egw); err != nil { + return nil, err + } + + httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeCreateNatPath) + if err != nil { + return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) + } + // We expect to get http.StatusCreated or if not an error of type types.NSXError + resp, err := egw.client.ExecuteRequestWithCustomError(httpPath, http.MethodPost, types.AnyXMLMime, + "error creating SNAT rule: %s", snatRuleConfig, &types.NSXError{}) + if err != nil { + return nil, err + } + + // Location header should look similar to: + // [/network/edges/edge-3/loadbalancer/config/applicationrules/applicationRule-4] + lbAppRuleId, err := extractNsxObjectIdFromPath(resp.Header.Get("Location")) + if err != nil { + return nil, err + } + + readAppRule, err := egw.GetSnatRule(&types.EdgeSnatRule{ID: lbAppRuleId}) + if err != nil { + return nil, fmt.Errorf("unable to retrieve application rule with ID (%s) after creation: %s", + lbAppRuleId, err) + } + return readAppRule, nil +} + +func (egw *EdgeGateway) UpdateSnatRule(SnatRuleConfig *types.EdgeSnatRule) (*types.EdgeSnatRule, error) { + return nil, nil +} + +func (egw *EdgeGateway) GetSnatRule(snatRuleConfig *types.EdgeSnatRule) (*types.EdgeSnatRule, error) { + if err := validateGetSnatRule(snatRuleConfig, egw); err != nil { + return nil, err + } + + httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeNatPath) + if err != nil { + return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) + } + + // Anonymous struct to unwrap response + natRuleResponse := &struct { + EdgeSnatRules []*types.EdgeSnatRule `xml:"natRules"` + }{} + + // This query returns all application rules as the API does not have filtering options + _, err = egw.client.ExecuteRequest(httpPath, http.MethodGet, types.AnyXMLMime, + "unable to read SNAT rule: %s", nil, natRuleResponse) + if err != nil { + return nil, err + } + + // Search for nat rule by ID or by Name + for _, rule := range natRuleResponse.EdgeSnatRules { + // If ID was specified for lookup - look for the same ID + if rule.ID != "" && rule.ID == snatRuleConfig.ID { + return rule, nil + } + } + + // // If Name was specified for lookup - look for the same Name + // if lbAppRuleConfig.Name != "" && rule.Name == lbAppRuleConfig.Name { + // // We found it by name. Let's verify if search ID was specified and it matches the lookup object + // if lbAppRuleConfig.ID != "" && rule.ID != lbAppRuleConfig.ID { + // return nil, fmt.Errorf("load balancer application rule was found by name (%s)"+ + // ", but its ID (%s) does not match specified ID (%s)", + // rule.Name, rule.ID, lbAppRuleConfig.ID) + // } + // return rule, nil + // } + // } + + return nil, ErrorEntityNotFound +} + +func (egw *EdgeGateway) DeleteSnatRule(SnatRuleConfig *types.EdgeSnatRule) error { + return nil +} + +func validateCreateSnatRule(snatRuleConfig *types.EdgeSnatRule, egw *EdgeGateway) error { + if !egw.HasAdvancedNetworking() { + return fmt.Errorf("only advanced edge gateways support SNAT rules") + } + + // if snatRuleConfig.RuleType == "" { + // return fmt.Errorf("SnatRule") + // } + + return nil +} + +func validateGetSnatRule(snatRuleConfig *types.EdgeSnatRule, egw *EdgeGateway) error { + if !egw.HasAdvancedNetworking() { + return fmt.Errorf("only advanced edge gateways support SNAT rules") + } + + // if snatRuleConfig.RuleType == "" { + // return fmt.Errorf("SnatRule") + // } + + return nil +} diff --git a/govcd/nat_test.go b/govcd/nat_test.go new file mode 100644 index 000000000..fa770ebcd --- /dev/null +++ b/govcd/nat_test.go @@ -0,0 +1,32 @@ +// +build edge nat functional ALL + +/* + * Copyright 2019 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. + */ + +package govcd + +import ( + "github.com/vmware/go-vcloud-director/v2/types/v56" + . "gopkg.in/check.v1" +) + +func (vcd *TestVCD) Test_NatRule(check *C) { + if vcd.config.VCD.EdgeGateway == "" { + check.Skip("Skipping test because no edge gateway given") + } + edge, err := vcd.vdc.FindEdgeGateway(vcd.config.VCD.EdgeGateway) + check.Assert(err, IsNil) + check.Assert(edge.EdgeGateway.Name, Equals, vcd.config.VCD.EdgeGateway) + + natRule := &types.EdgeSnatRule{ + Name: "asd", + Action: "snat", + OriginalAddress: "10.10.10.15", + TranslatedAddress: "192.168.1.110", + } + + _, err = edge.CreateSnatRule(natRule) + check.Assert(err, IsNil) + +} diff --git a/types/v56/constants.go b/types/v56/constants.go index 36dc660a3..45e97a92b 100644 --- a/types/v56/constants.go +++ b/types/v56/constants.go @@ -163,6 +163,12 @@ const ( LbVirtualServerPath = "/loadbalancer/config/virtualservers/" ) +// Edge gateway NAT rule API endpoints +const ( + EdgeNatPath = "/nat/config" + EdgeCreateNatPath = "/nat/config/rules" +) + // Guest customization statuses. These are all known possible statuses const ( GuestCustStatusPending = "GC_PENDING" diff --git a/types/v56/types.go b/types/v56/types.go index 3daa509df..d017d3bfd 100644 --- a/types/v56/types.go +++ b/types/v56/types.go @@ -1819,6 +1819,56 @@ type LbVirtualServer struct { ApplicationRuleIds []string `xml:"applicationRuleId,omitempty"` } +// EdgeNatRule contains shared structure for SNAT and DNAT rule configuration using +// NSX-V proxied edge gateway endpoint +type EdgeNatRule struct { + XMLName xml.Name `xml:"natRule"` + ID string `xml:"ruleId,omitempty"` + Name string `xml:"name"` + RuleType string `xml:"ruleType"` + RuleTag string `xml:"ruleTag"` + Action string `xml:"action"` + Vnic string `xml:"vnic"` + OriginalAddress string `xml:"originalAddress"` + TranslatedAddress string `xml:"translatedAddress"` + LoggingEnabled string `xml:"loggingEnabled"` + Enabled string `xml:"enabled"` + Description string `xml:"description"` + Protocol string `xml:"protocol"` + OriginalPort string `xml:"originalPort"` + TranslatedPort string `xml:"translatedPort"` +} + +// EdgeSnatRule embeds EdgeNatRule and adds SNAT specific fields +type EdgeSnatRule struct { + XMLName xml.Name `xml:"natRule"` + ID string `xml:"ruleId,omitempty"` + Name string `xml:"name"` + RuleType string `xml:"ruleType"` + RuleTag string `xml:"ruleTag"` + Action string `xml:"action,omitempty"` + Vnic string `xml:"vnic,omitempty"` + OriginalAddress string `xml:"originalAddress,omitempty"` + TranslatedAddress string `xml:"translatedAddress,omitempty"` + LoggingEnabled string `xml:"loggingEnabled,omitempty"` + Enabled string `xml:"enabled,omitempty"` + Description string `xml:"description,omitempty"` + Protocol string `xml:"protocol,omitempty"` + OriginalPort string `xml:"originalPort,omitempty"` + TranslatedPort string `xml:"translatedPort,omitempty"` + + SnatMatchDestinationAddress string `xml:"dnatMatchDestinationAddress,omitempty"` + SnatMatchDestinationPort string `xml:"dnatMatchDestinationPort,omitempty"` +} + +// EdgeDnatRule embeds EdgeNatRule and adds DNAT specific fields +type EdgeDnatRule struct { + EdgeNatRule + + DnatMatchSourceAddress string `xml:"dnatMatchSourceAddress"` + DnatMatchSourcePort string `xml:"dnatMatchSourcePort"` +} + // VendorTemplate is information about a vendor service template. This is optional. // Type: VendorTemplateType // Namespace: http://www.vmware.com/vcloud/v1.5 From 7c2c2df2e438a89038a2a44d03bbe954ed614b74 Mon Sep 17 00:00:00 2001 From: Dainius S Date: Mon, 2 Sep 2019 15:33:28 +0300 Subject: [PATCH 02/23] Test Create/Get works --- govcd/nat.go | 52 ++++++++++++++++++++++++++++++++++++++++------ govcd/nat_test.go | 16 ++++++++++---- types/v56/types.go | 8 ++++--- 3 files changed, 63 insertions(+), 13 deletions(-) diff --git a/govcd/nat.go b/govcd/nat.go index 05845dac6..40c523bf7 100644 --- a/govcd/nat.go +++ b/govcd/nat.go @@ -5,24 +5,58 @@ package govcd import ( + "encoding/xml" "fmt" "net/http" "github.com/vmware/go-vcloud-director/v2/types/v56" ) + +type natRuleRequest struct { + XMLName xml.Name `xml:"natRules"` + EdgeSnatRules []*types.EdgeSnatRule +} + +type jonines struct { + XMLName xml.Name `xml:"nat"` + Version string `xml:"version"` + NatRule natRuleRequest +} + +type petrines struct { + XMLName xml.Name `xml:"nat"` + Version string `xml:"version"` + NatRules struct { + Rules []*types.EdgeSnatRule `xml:"natRule"` + } `xml:"natRules"` +} + func (egw *EdgeGateway) CreateSnatRule(snatRuleConfig *types.EdgeSnatRule) (*types.EdgeSnatRule, error) { if err := validateCreateSnatRule(snatRuleConfig, egw); err != nil { return nil, err } + natRuleRequest := &struct { + XMLName xml.Name `xml:"natRules"` + EdgeSnatRules []*types.EdgeSnatRule + }{} + // natRuleRequest := &jonines{} + + // natRuleRequest := &petrines{} + + natRules := []*types.EdgeSnatRule{} + natRules = append(natRules, snatRuleConfig) + + natRuleRequest.EdgeSnatRules = natRules + httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeCreateNatPath) if err != nil { return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) } // We expect to get http.StatusCreated or if not an error of type types.NSXError resp, err := egw.client.ExecuteRequestWithCustomError(httpPath, http.MethodPost, types.AnyXMLMime, - "error creating SNAT rule: %s", snatRuleConfig, &types.NSXError{}) + "error creating SNAT rule: %s", natRuleRequest, &types.NSXError{}) if err != nil { return nil, err } @@ -57,9 +91,11 @@ func (egw *EdgeGateway) GetSnatRule(snatRuleConfig *types.EdgeSnatRule) (*types. } // Anonymous struct to unwrap response - natRuleResponse := &struct { - EdgeSnatRules []*types.EdgeSnatRule `xml:"natRules"` - }{} + // natRuleResponse := &struct { + // EdgeSnatRules []*types.EdgeSnatRule `xml:"natRules"` + // }{} + + natRuleResponse := &petrines{} // This query returns all application rules as the API does not have filtering options _, err = egw.client.ExecuteRequest(httpPath, http.MethodGet, types.AnyXMLMime, @@ -68,9 +104,13 @@ func (egw *EdgeGateway) GetSnatRule(snatRuleConfig *types.EdgeSnatRule) (*types. return nil, err } + + fmt.Printf("whole struct %+#v\n", natRuleResponse) + // Search for nat rule by ID or by Name - for _, rule := range natRuleResponse.EdgeSnatRules { + for _, rule := range natRuleResponse.NatRules.Rules { // If ID was specified for lookup - look for the same ID + fmt.Printf("checking %+#v\n", rule) if rule.ID != "" && rule.ID == snatRuleConfig.ID { return rule, nil } @@ -91,7 +131,7 @@ func (egw *EdgeGateway) GetSnatRule(snatRuleConfig *types.EdgeSnatRule) (*types. return nil, ErrorEntityNotFound } -func (egw *EdgeGateway) DeleteSnatRule(SnatRuleConfig *types.EdgeSnatRule) error { +func (egw *EdgeGateway) DeleteSnatRule(snatRuleConfig *types.EdgeSnatRule) error { return nil } diff --git a/govcd/nat_test.go b/govcd/nat_test.go index fa770ebcd..30a158f56 100644 --- a/govcd/nat_test.go +++ b/govcd/nat_test.go @@ -20,10 +20,18 @@ func (vcd *TestVCD) Test_NatRule(check *C) { check.Assert(edge.EdgeGateway.Name, Equals, vcd.config.VCD.EdgeGateway) natRule := &types.EdgeSnatRule{ - Name: "asd", - Action: "snat", - OriginalAddress: "10.10.10.15", - TranslatedAddress: "192.168.1.110", + RuleType: "user", + // Name: "asd", + Action: "snat", + OriginalAddress: "10.10.10.15", + TranslatedAddress: "192.168.1.110", + // SnatMatchDestinationAddress: "any", + Enabled: "true", + // LoggingEnabled: "false", + // OriginalPort: "3380", + // TranslatedPort: "3380", + Protocol: "any", + Vnic: "0", } _, err = edge.CreateSnatRule(natRule) diff --git a/types/v56/types.go b/types/v56/types.go index d017d3bfd..bcb7d6630 100644 --- a/types/v56/types.go +++ b/types/v56/types.go @@ -1843,9 +1843,9 @@ type EdgeNatRule struct { type EdgeSnatRule struct { XMLName xml.Name `xml:"natRule"` ID string `xml:"ruleId,omitempty"` - Name string `xml:"name"` - RuleType string `xml:"ruleType"` - RuleTag string `xml:"ruleTag"` + Name string `xml:"name,omitempty"` + RuleType string `xml:"ruleType,omitempty"` + RuleTag string `xml:"ruleTag,omitempty"` Action string `xml:"action,omitempty"` Vnic string `xml:"vnic,omitempty"` OriginalAddress string `xml:"originalAddress,omitempty"` @@ -1861,6 +1861,8 @@ type EdgeSnatRule struct { SnatMatchDestinationPort string `xml:"dnatMatchDestinationPort,omitempty"` } +// type EdgeSnatRules []*types.EdgeSnatRule `xml:"natRules"` + // EdgeDnatRule embeds EdgeNatRule and adds DNAT specific fields type EdgeDnatRule struct { EdgeNatRule From e5f47e535a74476737164d78159ebc287ebf91b7 Mon Sep 17 00:00:00 2001 From: Dainius S Date: Tue, 3 Sep 2019 09:33:30 +0300 Subject: [PATCH 03/23] structure works --- govcd/nat.go | 47 ++++++++++++++-------------------------------- govcd/nat_test.go | 14 +++++++------- types/v56/types.go | 12 +++++++++--- 3 files changed, 30 insertions(+), 43 deletions(-) diff --git a/govcd/nat.go b/govcd/nat.go index 40c523bf7..74b7fa958 100644 --- a/govcd/nat.go +++ b/govcd/nat.go @@ -12,24 +12,11 @@ import ( "github.com/vmware/go-vcloud-director/v2/types/v56" ) - -type natRuleRequest struct { - XMLName xml.Name `xml:"natRules"` - EdgeSnatRules []*types.EdgeSnatRule -} - -type jonines struct { - XMLName xml.Name `xml:"nat"` - Version string `xml:"version"` - NatRule natRuleRequest -} - -type petrines struct { +// wrappedEdgeSnatRules is used to unwrap response when retrieving +type wrappedEdgeSnatRules struct { XMLName xml.Name `xml:"nat"` Version string `xml:"version"` - NatRules struct { - Rules []*types.EdgeSnatRule `xml:"natRule"` - } `xml:"natRules"` + NatR types.EdgeSnatRules `xml:"natRules"` } func (egw *EdgeGateway) CreateSnatRule(snatRuleConfig *types.EdgeSnatRule) (*types.EdgeSnatRule, error) { @@ -37,13 +24,7 @@ func (egw *EdgeGateway) CreateSnatRule(snatRuleConfig *types.EdgeSnatRule) (*typ return nil, err } - natRuleRequest := &struct { - XMLName xml.Name `xml:"natRules"` - EdgeSnatRules []*types.EdgeSnatRule - }{} - // natRuleRequest := &jonines{} - - // natRuleRequest := &petrines{} + natRuleRequest := &types.EdgeSnatRules{} natRules := []*types.EdgeSnatRule{} natRules = append(natRules, snatRuleConfig) @@ -90,12 +71,7 @@ func (egw *EdgeGateway) GetSnatRule(snatRuleConfig *types.EdgeSnatRule) (*types. return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) } - // Anonymous struct to unwrap response - // natRuleResponse := &struct { - // EdgeSnatRules []*types.EdgeSnatRule `xml:"natRules"` - // }{} - - natRuleResponse := &petrines{} + natRuleResponse := &wrappedEdgeSnatRules{} // This query returns all application rules as the API does not have filtering options _, err = egw.client.ExecuteRequest(httpPath, http.MethodGet, types.AnyXMLMime, @@ -108,7 +84,8 @@ func (egw *EdgeGateway) GetSnatRule(snatRuleConfig *types.EdgeSnatRule) (*types. fmt.Printf("whole struct %+#v\n", natRuleResponse) // Search for nat rule by ID or by Name - for _, rule := range natRuleResponse.NatRules.Rules { + // for _, rule := range natRuleResponse.NatRules.EdgeSnatRules { + for _, rule := range natRuleResponse.NatR.EdgeSnatRules { // If ID was specified for lookup - look for the same ID fmt.Printf("checking %+#v\n", rule) if rule.ID != "" && rule.ID == snatRuleConfig.ID { @@ -140,9 +117,13 @@ func validateCreateSnatRule(snatRuleConfig *types.EdgeSnatRule, egw *EdgeGateway return fmt.Errorf("only advanced edge gateways support SNAT rules") } - // if snatRuleConfig.RuleType == "" { - // return fmt.Errorf("SnatRule") - // } + if snatRuleConfig.Action == "" { + return fmt.Errorf("NAT rule must have an action") + } + + if snatRuleConfig.TranslatedAddress == "" { + return fmt.Errorf("NAT rule must translated address specified") + } return nil } diff --git a/govcd/nat_test.go b/govcd/nat_test.go index 30a158f56..6e7a17073 100644 --- a/govcd/nat_test.go +++ b/govcd/nat_test.go @@ -20,18 +20,18 @@ func (vcd *TestVCD) Test_NatRule(check *C) { check.Assert(edge.EdgeGateway.Name, Equals, vcd.config.VCD.EdgeGateway) natRule := &types.EdgeSnatRule{ - RuleType: "user", + // RuleType: "user", // Name: "asd", - Action: "snat", - OriginalAddress: "10.10.10.15", - TranslatedAddress: "192.168.1.110", + Action: "snat", // mandatory + // OriginalAddress: "10.10.10.15", + TranslatedAddress: "192.168.1.110", // mandatory // SnatMatchDestinationAddress: "any", - Enabled: "true", + Enabled: "true", // mandatory // LoggingEnabled: "false", // OriginalPort: "3380", // TranslatedPort: "3380", - Protocol: "any", - Vnic: "0", + // Protocol: "any", + // Vnic: "0", } _, err = edge.CreateSnatRule(natRule) diff --git a/types/v56/types.go b/types/v56/types.go index bcb7d6630..ef020a5ad 100644 --- a/types/v56/types.go +++ b/types/v56/types.go @@ -1854,14 +1854,20 @@ type EdgeSnatRule struct { Enabled string `xml:"enabled,omitempty"` Description string `xml:"description,omitempty"` Protocol string `xml:"protocol,omitempty"` - OriginalPort string `xml:"originalPort,omitempty"` - TranslatedPort string `xml:"translatedPort,omitempty"` + OriginalPort int `xml:"originalPort,omitempty"` + TranslatedPort int `xml:"translatedPort,omitempty"` SnatMatchDestinationAddress string `xml:"dnatMatchDestinationAddress,omitempty"` SnatMatchDestinationPort string `xml:"dnatMatchDestinationPort,omitempty"` } -// type EdgeSnatRules []*types.EdgeSnatRule `xml:"natRules"` +// EdgeSnatRules nests EdgeSnatRule as a convenience for mashalling +type EdgeSnatRules struct { + XMLName xml.Name `xml:"natRules"` + EdgeSnatRules []*EdgeSnatRule `xml:"natRule"` +} + + // EdgeDnatRule embeds EdgeNatRule and adds DNAT specific fields type EdgeDnatRule struct { From b183c6fd18a65c66ae451855f1bbced367173c0e Mon Sep 17 00:00:00 2001 From: Dainius S Date: Tue, 3 Sep 2019 09:43:36 +0300 Subject: [PATCH 04/23] fmt --- govcd/nat.go | 13 ++++--------- govcd/nat_test.go | 6 +++--- types/v56/types.go | 10 ++++------ 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/govcd/nat.go b/govcd/nat.go index 74b7fa958..85d118b0d 100644 --- a/govcd/nat.go +++ b/govcd/nat.go @@ -14,9 +14,9 @@ import ( // wrappedEdgeSnatRules is used to unwrap response when retrieving type wrappedEdgeSnatRules struct { - XMLName xml.Name `xml:"nat"` - Version string `xml:"version"` - NatR types.EdgeSnatRules `xml:"natRules"` + XMLName xml.Name `xml:"nat"` + Version string `xml:"version"` + NatR types.EdgeSnatRules `xml:"natRules"` } func (egw *EdgeGateway) CreateSnatRule(snatRuleConfig *types.EdgeSnatRule) (*types.EdgeSnatRule, error) { @@ -80,12 +80,11 @@ func (egw *EdgeGateway) GetSnatRule(snatRuleConfig *types.EdgeSnatRule) (*types. return nil, err } - fmt.Printf("whole struct %+#v\n", natRuleResponse) // Search for nat rule by ID or by Name // for _, rule := range natRuleResponse.NatRules.EdgeSnatRules { - for _, rule := range natRuleResponse.NatR.EdgeSnatRules { + for _, rule := range natRuleResponse.NatR.EdgeSnatRules { // If ID was specified for lookup - look for the same ID fmt.Printf("checking %+#v\n", rule) if rule.ID != "" && rule.ID == snatRuleConfig.ID { @@ -133,9 +132,5 @@ func validateGetSnatRule(snatRuleConfig *types.EdgeSnatRule, egw *EdgeGateway) e return fmt.Errorf("only advanced edge gateways support SNAT rules") } - // if snatRuleConfig.RuleType == "" { - // return fmt.Errorf("SnatRule") - // } - return nil } diff --git a/govcd/nat_test.go b/govcd/nat_test.go index 6e7a17073..26e6abeac 100644 --- a/govcd/nat_test.go +++ b/govcd/nat_test.go @@ -20,13 +20,13 @@ func (vcd *TestVCD) Test_NatRule(check *C) { check.Assert(edge.EdgeGateway.Name, Equals, vcd.config.VCD.EdgeGateway) natRule := &types.EdgeSnatRule{ + Action: "snat", + TranslatedAddress: "192.168.1.110", + Enabled: "true", // RuleType: "user", // Name: "asd", - Action: "snat", // mandatory // OriginalAddress: "10.10.10.15", - TranslatedAddress: "192.168.1.110", // mandatory // SnatMatchDestinationAddress: "any", - Enabled: "true", // mandatory // LoggingEnabled: "false", // OriginalPort: "3380", // TranslatedPort: "3380", diff --git a/types/v56/types.go b/types/v56/types.go index ef020a5ad..8b79cecff 100644 --- a/types/v56/types.go +++ b/types/v56/types.go @@ -1854,8 +1854,8 @@ type EdgeSnatRule struct { Enabled string `xml:"enabled,omitempty"` Description string `xml:"description,omitempty"` Protocol string `xml:"protocol,omitempty"` - OriginalPort int `xml:"originalPort,omitempty"` - TranslatedPort int `xml:"translatedPort,omitempty"` + OriginalPort int `xml:"originalPort,omitempty"` + TranslatedPort int `xml:"translatedPort,omitempty"` SnatMatchDestinationAddress string `xml:"dnatMatchDestinationAddress,omitempty"` SnatMatchDestinationPort string `xml:"dnatMatchDestinationPort,omitempty"` @@ -1863,12 +1863,10 @@ type EdgeSnatRule struct { // EdgeSnatRules nests EdgeSnatRule as a convenience for mashalling type EdgeSnatRules struct { - XMLName xml.Name `xml:"natRules"` - EdgeSnatRules []*EdgeSnatRule `xml:"natRule"` + XMLName xml.Name `xml:"natRules"` + EdgeSnatRules []*EdgeSnatRule `xml:"natRule"` } - - // EdgeDnatRule embeds EdgeNatRule and adds DNAT specific fields type EdgeDnatRule struct { EdgeNatRule From ecd1b1a2a6a1f5167a898ec9d26fbee8b74ed08a Mon Sep 17 00:00:00 2001 From: Dainius S Date: Tue, 3 Sep 2019 12:10:00 +0300 Subject: [PATCH 05/23] CRUD available --- govcd/nat.go | 113 ++++++++++++++++++++++++++++++++++++--------- govcd/nat_test.go | 21 +++++++-- types/v56/types.go | 37 ++++++--------- 3 files changed, 123 insertions(+), 48 deletions(-) diff --git a/govcd/nat.go b/govcd/nat.go index 85d118b0d..44907b80f 100644 --- a/govcd/nat.go +++ b/govcd/nat.go @@ -12,24 +12,29 @@ import ( "github.com/vmware/go-vcloud-director/v2/types/v56" ) -// wrappedEdgeSnatRules is used to unwrap response when retrieving -type wrappedEdgeSnatRules struct { - XMLName xml.Name `xml:"nat"` - Version string `xml:"version"` - NatR types.EdgeSnatRules `xml:"natRules"` +// requestEdgeSnatRules nests EdgeSnatRule as a convenience for unmarshalling POST requests +type requestEdgeSnatRules struct { + XMLName xml.Name `xml:"natRules"` + EdgeSnatRules []*types.EdgeSnatRule `xml:"natRule"` } +// responseEdgeSnatRules is used to unwrap response when retrieving +type responseEdgeSnatRules struct { + XMLName xml.Name `xml:"nat"` + Version string `xml:"version"` + NatRules requestEdgeSnatRules `xml:"natRules"` +} + +// CreateSnatRule func (egw *EdgeGateway) CreateSnatRule(snatRuleConfig *types.EdgeSnatRule) (*types.EdgeSnatRule, error) { if err := validateCreateSnatRule(snatRuleConfig, egw); err != nil { return nil, err } - natRuleRequest := &types.EdgeSnatRules{} - - natRules := []*types.EdgeSnatRule{} - natRules = append(natRules, snatRuleConfig) - - natRuleRequest.EdgeSnatRules = natRules + // Wrap the provided rule for POST request + natRuleRequest := requestEdgeSnatRules{ + EdgeSnatRules: []*types.EdgeSnatRule{snatRuleConfig}, + } httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeCreateNatPath) if err != nil { @@ -43,22 +48,44 @@ func (egw *EdgeGateway) CreateSnatRule(snatRuleConfig *types.EdgeSnatRule) (*typ } // Location header should look similar to: - // [/network/edges/edge-3/loadbalancer/config/applicationrules/applicationRule-4] - lbAppRuleId, err := extractNsxObjectIdFromPath(resp.Header.Get("Location")) + // [/network/edges/edge-1/nat/config/rules/197157] + snatRuleId, err := extractNsxObjectIdFromPath(resp.Header.Get("Location")) if err != nil { return nil, err } - readAppRule, err := egw.GetSnatRule(&types.EdgeSnatRule{ID: lbAppRuleId}) + readSnatRule, err := egw.GetSnatRule(&types.EdgeSnatRule{ID: snatRuleId}) if err != nil { - return nil, fmt.Errorf("unable to retrieve application rule with ID (%s) after creation: %s", - lbAppRuleId, err) + return nil, fmt.Errorf("unable to retrieve SNAT rule with ID (%s) after creation: %s", + snatRuleId, err) } - return readAppRule, nil + return readSnatRule, nil } -func (egw *EdgeGateway) UpdateSnatRule(SnatRuleConfig *types.EdgeSnatRule) (*types.EdgeSnatRule, error) { - return nil, nil +func (egw *EdgeGateway) UpdateSnatRule(snatRuleConfig *types.EdgeSnatRule) (*types.EdgeSnatRule, error) { + err := validateUpdateSnatRule(snatRuleConfig, egw) + if err != nil { + return nil, err + } + + httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeCreateNatPath + "/" + snatRuleConfig.ID) + if err != nil { + return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) + } + + // Result should be 204, if not we expect an error of type types.NSXError + _, err = egw.client.ExecuteRequestWithCustomError(httpPath, http.MethodPut, types.AnyXMLMime, + "error while updating NAT rule : %s", snatRuleConfig, &types.NSXError{}) + if err != nil { + return nil, err + } + + readSnatRule, err := egw.GetSnatRuleById(snatRuleConfig.ID) + if err != nil { + return nil, fmt.Errorf("unable to retrieve NAT rule with ID (%s) after update: %s", + readSnatRule.ID, err) + } + return readSnatRule, nil } func (egw *EdgeGateway) GetSnatRule(snatRuleConfig *types.EdgeSnatRule) (*types.EdgeSnatRule, error) { @@ -71,7 +98,7 @@ func (egw *EdgeGateway) GetSnatRule(snatRuleConfig *types.EdgeSnatRule) (*types. return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) } - natRuleResponse := &wrappedEdgeSnatRules{} + natRuleResponse := &responseEdgeSnatRules{} // This query returns all application rules as the API does not have filtering options _, err = egw.client.ExecuteRequest(httpPath, http.MethodGet, types.AnyXMLMime, @@ -80,13 +107,13 @@ func (egw *EdgeGateway) GetSnatRule(snatRuleConfig *types.EdgeSnatRule) (*types. return nil, err } - fmt.Printf("whole struct %+#v\n", natRuleResponse) + // fmt.Printf("whole struct %+#v\n", natRuleResponse) // Search for nat rule by ID or by Name // for _, rule := range natRuleResponse.NatRules.EdgeSnatRules { - for _, rule := range natRuleResponse.NatR.EdgeSnatRules { + for _, rule := range natRuleResponse.NatRules.EdgeSnatRules { // If ID was specified for lookup - look for the same ID - fmt.Printf("checking %+#v\n", rule) + // fmt.Printf("checking %+#v\n", rule) if rule.ID != "" && rule.ID == snatRuleConfig.ID { return rule, nil } @@ -107,10 +134,34 @@ func (egw *EdgeGateway) GetSnatRule(snatRuleConfig *types.EdgeSnatRule) (*types. return nil, ErrorEntityNotFound } +func (egw *EdgeGateway) GetSnatRuleById(id string) (*types.EdgeSnatRule, error) { + return egw.GetSnatRule(&types.EdgeSnatRule{ID: id}) +} + func (egw *EdgeGateway) DeleteSnatRule(snatRuleConfig *types.EdgeSnatRule) error { + err := validateDeleteSnatRule(snatRuleConfig, egw) + if err != nil { + return err + } + + httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeCreateNatPath + "/" + snatRuleConfig.ID) + if err != nil { + return fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) + } + + _, err = egw.client.ExecuteRequestWithCustomError(httpPath, http.MethodDelete, types.AnyXMLMime, + "unable to delete nat rule: %s", nil, &types.NSXError{}) + if err != nil { + return err + } + return nil } +func (egw *EdgeGateway) DeleteSnatRuleById(id string) error { + return egw.DeleteSnatRule(&types.EdgeSnatRule{ID: id}) +} + func validateCreateSnatRule(snatRuleConfig *types.EdgeSnatRule, egw *EdgeGateway) error { if !egw.HasAdvancedNetworking() { return fmt.Errorf("only advanced edge gateways support SNAT rules") @@ -127,10 +178,26 @@ func validateCreateSnatRule(snatRuleConfig *types.EdgeSnatRule, egw *EdgeGateway return nil } +func validateUpdateSnatRule(snatRuleConfig *types.EdgeSnatRule, egw *EdgeGateway) error { + if snatRuleConfig.ID == "" { + return fmt.Errorf("NAT rule must ID must be set for update") + } + + return validateCreateSnatRule(snatRuleConfig, egw) +} + func validateGetSnatRule(snatRuleConfig *types.EdgeSnatRule, egw *EdgeGateway) error { if !egw.HasAdvancedNetworking() { return fmt.Errorf("only advanced edge gateways support SNAT rules") } + if snatRuleConfig.ID == "" { + return fmt.Errorf("unable to retrieve SNAT rule without ID") + } + return nil } + +func validateDeleteSnatRule(snatRuleConfig *types.EdgeSnatRule, egw *EdgeGateway) error { + return validateGetSnatRule(snatRuleConfig, egw) +} diff --git a/govcd/nat_test.go b/govcd/nat_test.go index 26e6abeac..deb0e0e27 100644 --- a/govcd/nat_test.go +++ b/govcd/nat_test.go @@ -21,11 +21,11 @@ func (vcd *TestVCD) Test_NatRule(check *C) { natRule := &types.EdgeSnatRule{ Action: "snat", - TranslatedAddress: "192.168.1.110", + TranslatedAddress: vcd.config.VCD.ExternalIp, // Edge gateway address + OriginalAddress: vcd.config.VCD.InternalIp, Enabled: "true", // RuleType: "user", // Name: "asd", - // OriginalAddress: "10.10.10.15", // SnatMatchDestinationAddress: "any", // LoggingEnabled: "false", // OriginalPort: "3380", @@ -34,7 +34,22 @@ func (vcd *TestVCD) Test_NatRule(check *C) { // Vnic: "0", } - _, err = edge.CreateSnatRule(natRule) + createdSnatRule, err := edge.CreateSnatRule(natRule) + check.Assert(err, IsNil) + + gotSnatRule, err := edge.GetSnatRuleById(createdSnatRule.ID) + check.Assert(err, IsNil) + check.Assert(gotSnatRule.ID, Equals, createdSnatRule.ID) + + // Set ID and update nat rule with description + natRule.ID = gotSnatRule.ID + natRule.Description = "Description for SNAT rule" + updatedSnatRule, err := edge.UpdateSnatRule(natRule) + check.Assert(err, IsNil) + check.Assert(updatedSnatRule.Description, Equals, natRule.Description) + + + err = edge.DeleteSnatRuleById(gotSnatRule.ID) check.Assert(err, IsNil) } diff --git a/types/v56/types.go b/types/v56/types.go index 8b79cecff..11b1955c7 100644 --- a/types/v56/types.go +++ b/types/v56/types.go @@ -1824,26 +1824,25 @@ type LbVirtualServer struct { type EdgeNatRule struct { XMLName xml.Name `xml:"natRule"` ID string `xml:"ruleId,omitempty"` - Name string `xml:"name"` - RuleType string `xml:"ruleType"` - RuleTag string `xml:"ruleTag"` - Action string `xml:"action"` - Vnic string `xml:"vnic"` - OriginalAddress string `xml:"originalAddress"` - TranslatedAddress string `xml:"translatedAddress"` - LoggingEnabled string `xml:"loggingEnabled"` - Enabled string `xml:"enabled"` - Description string `xml:"description"` - Protocol string `xml:"protocol"` - OriginalPort string `xml:"originalPort"` - TranslatedPort string `xml:"translatedPort"` + Name string `xml:"name,omitempty"` + RuleType string `xml:"ruleType,omitempty"` + RuleTag string `xml:"ruleTag,omitempty"` + Action string `xml:"action,omitempty"` + Vnic string `xml:"vnic,omitempty"` + OriginalAddress string `xml:"originalAddress,omitempty"` + TranslatedAddress string `xml:"translatedAddress,omitempty"` + LoggingEnabled string `xml:"loggingEnabled,omitempty"` + Enabled string `xml:"enabled,omitempty"` + Description string `xml:"description,omitempty"` + Protocol string `xml:"protocol,omitempty"` + OriginalPort string `xml:"originalPort,omitempty"` + TranslatedPort string `xml:"translatedPort,omitempty"` } // EdgeSnatRule embeds EdgeNatRule and adds SNAT specific fields type EdgeSnatRule struct { XMLName xml.Name `xml:"natRule"` ID string `xml:"ruleId,omitempty"` - Name string `xml:"name,omitempty"` RuleType string `xml:"ruleType,omitempty"` RuleTag string `xml:"ruleTag,omitempty"` Action string `xml:"action,omitempty"` @@ -1854,19 +1853,13 @@ type EdgeSnatRule struct { Enabled string `xml:"enabled,omitempty"` Description string `xml:"description,omitempty"` Protocol string `xml:"protocol,omitempty"` - OriginalPort int `xml:"originalPort,omitempty"` - TranslatedPort int `xml:"translatedPort,omitempty"` + OriginalPort string `xml:"originalPort,omitempty"` + TranslatedPort string `xml:"translatedPort,omitempty"` SnatMatchDestinationAddress string `xml:"dnatMatchDestinationAddress,omitempty"` SnatMatchDestinationPort string `xml:"dnatMatchDestinationPort,omitempty"` } -// EdgeSnatRules nests EdgeSnatRule as a convenience for mashalling -type EdgeSnatRules struct { - XMLName xml.Name `xml:"natRules"` - EdgeSnatRules []*EdgeSnatRule `xml:"natRule"` -} - // EdgeDnatRule embeds EdgeNatRule and adds DNAT specific fields type EdgeDnatRule struct { EdgeNatRule From 460361f1ea88f7751c2c865a28441ac1ea781f3e Mon Sep 17 00:00:00 2001 From: Dainius S Date: Tue, 3 Sep 2019 12:59:19 +0300 Subject: [PATCH 06/23] Shared NAT rules --- govcd/nat.go | 102 ++++++++++++++++++++++----------------------- govcd/nat_test.go | 10 ++--- types/v56/types.go | 31 +++----------- 3 files changed, 61 insertions(+), 82 deletions(-) diff --git a/govcd/nat.go b/govcd/nat.go index 44907b80f..41343bc56 100644 --- a/govcd/nat.go +++ b/govcd/nat.go @@ -12,28 +12,28 @@ import ( "github.com/vmware/go-vcloud-director/v2/types/v56" ) -// requestEdgeSnatRules nests EdgeSnatRule as a convenience for unmarshalling POST requests -type requestEdgeSnatRules struct { +// requestEdgeNatRules nests EdgeNatRule as a convenience for unmarshalling POST requests +type requestEdgeNatRules struct { XMLName xml.Name `xml:"natRules"` - EdgeSnatRules []*types.EdgeSnatRule `xml:"natRule"` + EdgeNatRules []*types.EdgeNatRule `xml:"natRule"` } -// responseEdgeSnatRules is used to unwrap response when retrieving -type responseEdgeSnatRules struct { +// responseEdgeNatRules is used to unwrap response when retrieving +type responseEdgeNatRules struct { XMLName xml.Name `xml:"nat"` Version string `xml:"version"` - NatRules requestEdgeSnatRules `xml:"natRules"` + NatRules requestEdgeNatRules `xml:"natRules"` } -// CreateSnatRule -func (egw *EdgeGateway) CreateSnatRule(snatRuleConfig *types.EdgeSnatRule) (*types.EdgeSnatRule, error) { - if err := validateCreateSnatRule(snatRuleConfig, egw); err != nil { +// CreateNatRule +func (egw *EdgeGateway) CreateNsxvNatRule(natRuleConfig *types.EdgeNatRule) (*types.EdgeNatRule, error) { + if err := validateCreateNsxvNatRule(natRuleConfig, egw); err != nil { return nil, err } // Wrap the provided rule for POST request - natRuleRequest := requestEdgeSnatRules{ - EdgeSnatRules: []*types.EdgeSnatRule{snatRuleConfig}, + natRuleRequest := requestEdgeNatRules{ + EdgeNatRules: []*types.EdgeNatRule{natRuleConfig}, } httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeCreateNatPath) @@ -42,54 +42,54 @@ func (egw *EdgeGateway) CreateSnatRule(snatRuleConfig *types.EdgeSnatRule) (*typ } // We expect to get http.StatusCreated or if not an error of type types.NSXError resp, err := egw.client.ExecuteRequestWithCustomError(httpPath, http.MethodPost, types.AnyXMLMime, - "error creating SNAT rule: %s", natRuleRequest, &types.NSXError{}) + "error creating NAT rule: %s", natRuleRequest, &types.NSXError{}) if err != nil { return nil, err } // Location header should look similar to: // [/network/edges/edge-1/nat/config/rules/197157] - snatRuleId, err := extractNsxObjectIdFromPath(resp.Header.Get("Location")) + natRuleId, err := extractNsxObjectIdFromPath(resp.Header.Get("Location")) if err != nil { return nil, err } - readSnatRule, err := egw.GetSnatRule(&types.EdgeSnatRule{ID: snatRuleId}) + readNatRule, err := egw.GetNsxvNatRule(&types.EdgeNatRule{ID: natRuleId}) if err != nil { - return nil, fmt.Errorf("unable to retrieve SNAT rule with ID (%s) after creation: %s", - snatRuleId, err) + return nil, fmt.Errorf("unable to retrieve NAT rule with ID (%s) after creation: %s", + natRuleId, err) } - return readSnatRule, nil + return readNatRule, nil } -func (egw *EdgeGateway) UpdateSnatRule(snatRuleConfig *types.EdgeSnatRule) (*types.EdgeSnatRule, error) { - err := validateUpdateSnatRule(snatRuleConfig, egw) +func (egw *EdgeGateway) UpdateNsxvNatRule(natRuleConfig *types.EdgeNatRule) (*types.EdgeNatRule, error) { + err := validateUpdateNsxvNatRule(natRuleConfig, egw) if err != nil { return nil, err } - httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeCreateNatPath + "/" + snatRuleConfig.ID) + httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeCreateNatPath + "/" + natRuleConfig.ID) if err != nil { return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) } // Result should be 204, if not we expect an error of type types.NSXError _, err = egw.client.ExecuteRequestWithCustomError(httpPath, http.MethodPut, types.AnyXMLMime, - "error while updating NAT rule : %s", snatRuleConfig, &types.NSXError{}) + "error while updating NAT rule : %s", natRuleConfig, &types.NSXError{}) if err != nil { return nil, err } - readSnatRule, err := egw.GetSnatRuleById(snatRuleConfig.ID) + readNatRule, err := egw.GetNsxvNatRuleById(natRuleConfig.ID) if err != nil { return nil, fmt.Errorf("unable to retrieve NAT rule with ID (%s) after update: %s", - readSnatRule.ID, err) + readNatRule.ID, err) } - return readSnatRule, nil + return readNatRule, nil } -func (egw *EdgeGateway) GetSnatRule(snatRuleConfig *types.EdgeSnatRule) (*types.EdgeSnatRule, error) { - if err := validateGetSnatRule(snatRuleConfig, egw); err != nil { +func (egw *EdgeGateway) GetNsxvNatRule(natRuleConfig *types.EdgeNatRule) (*types.EdgeNatRule, error) { + if err := validateGetNsxvNatRule(natRuleConfig, egw); err != nil { return nil, err } @@ -98,11 +98,11 @@ func (egw *EdgeGateway) GetSnatRule(snatRuleConfig *types.EdgeSnatRule) (*types. return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) } - natRuleResponse := &responseEdgeSnatRules{} + natRuleResponse := &responseEdgeNatRules{} // This query returns all application rules as the API does not have filtering options _, err = egw.client.ExecuteRequest(httpPath, http.MethodGet, types.AnyXMLMime, - "unable to read SNAT rule: %s", nil, natRuleResponse) + "unable to read NAT rule: %s", nil, natRuleResponse) if err != nil { return nil, err } @@ -110,11 +110,11 @@ func (egw *EdgeGateway) GetSnatRule(snatRuleConfig *types.EdgeSnatRule) (*types. // fmt.Printf("whole struct %+#v\n", natRuleResponse) // Search for nat rule by ID or by Name - // for _, rule := range natRuleResponse.NatRules.EdgeSnatRules { - for _, rule := range natRuleResponse.NatRules.EdgeSnatRules { + // for _, rule := range natRuleResponse.NatRules.EdgeNatRules { + for _, rule := range natRuleResponse.NatRules.EdgeNatRules { // If ID was specified for lookup - look for the same ID // fmt.Printf("checking %+#v\n", rule) - if rule.ID != "" && rule.ID == snatRuleConfig.ID { + if rule.ID != "" && rule.ID == natRuleConfig.ID { return rule, nil } } @@ -134,17 +134,17 @@ func (egw *EdgeGateway) GetSnatRule(snatRuleConfig *types.EdgeSnatRule) (*types. return nil, ErrorEntityNotFound } -func (egw *EdgeGateway) GetSnatRuleById(id string) (*types.EdgeSnatRule, error) { - return egw.GetSnatRule(&types.EdgeSnatRule{ID: id}) +func (egw *EdgeGateway) GetNsxvNatRuleById(id string) (*types.EdgeNatRule, error) { + return egw.GetNsxvNatRule(&types.EdgeNatRule{ID: id}) } -func (egw *EdgeGateway) DeleteSnatRule(snatRuleConfig *types.EdgeSnatRule) error { - err := validateDeleteSnatRule(snatRuleConfig, egw) +func (egw *EdgeGateway) DeleteNsxvNatRule(natRuleConfig *types.EdgeNatRule) error { + err := validateDeleteNsxvNatRule(natRuleConfig, egw) if err != nil { return err } - httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeCreateNatPath + "/" + snatRuleConfig.ID) + httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeCreateNatPath + "/" + natRuleConfig.ID) if err != nil { return fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) } @@ -158,46 +158,46 @@ func (egw *EdgeGateway) DeleteSnatRule(snatRuleConfig *types.EdgeSnatRule) error return nil } -func (egw *EdgeGateway) DeleteSnatRuleById(id string) error { - return egw.DeleteSnatRule(&types.EdgeSnatRule{ID: id}) +func (egw *EdgeGateway) DeleteNsxvNatRuleById(id string) error { + return egw.DeleteNsxvNatRule(&types.EdgeNatRule{ID: id}) } -func validateCreateSnatRule(snatRuleConfig *types.EdgeSnatRule, egw *EdgeGateway) error { +func validateCreateNsxvNatRule(natRuleConfig *types.EdgeNatRule, egw *EdgeGateway) error { if !egw.HasAdvancedNetworking() { - return fmt.Errorf("only advanced edge gateways support SNAT rules") + return fmt.Errorf("only advanced edge gateways support NAT rules") } - if snatRuleConfig.Action == "" { + if natRuleConfig.Action == "" { return fmt.Errorf("NAT rule must have an action") } - if snatRuleConfig.TranslatedAddress == "" { + if natRuleConfig.TranslatedAddress == "" { return fmt.Errorf("NAT rule must translated address specified") } return nil } -func validateUpdateSnatRule(snatRuleConfig *types.EdgeSnatRule, egw *EdgeGateway) error { - if snatRuleConfig.ID == "" { +func validateUpdateNsxvNatRule(natRuleConfig *types.EdgeNatRule, egw *EdgeGateway) error { + if natRuleConfig.ID == "" { return fmt.Errorf("NAT rule must ID must be set for update") } - return validateCreateSnatRule(snatRuleConfig, egw) + return validateCreateNsxvNatRule(natRuleConfig, egw) } -func validateGetSnatRule(snatRuleConfig *types.EdgeSnatRule, egw *EdgeGateway) error { +func validateGetNsxvNatRule(natRuleConfig *types.EdgeNatRule, egw *EdgeGateway) error { if !egw.HasAdvancedNetworking() { - return fmt.Errorf("only advanced edge gateways support SNAT rules") + return fmt.Errorf("only advanced edge gateways support NAT rules") } - if snatRuleConfig.ID == "" { - return fmt.Errorf("unable to retrieve SNAT rule without ID") + if natRuleConfig.ID == "" { + return fmt.Errorf("unable to retrieve NAT rule without ID") } return nil } -func validateDeleteSnatRule(snatRuleConfig *types.EdgeSnatRule, egw *EdgeGateway) error { - return validateGetSnatRule(snatRuleConfig, egw) +func validateDeleteNsxvNatRule(natRuleConfig *types.EdgeNatRule, egw *EdgeGateway) error { + return validateGetNsxvNatRule(natRuleConfig, egw) } diff --git a/govcd/nat_test.go b/govcd/nat_test.go index deb0e0e27..2e18cceaf 100644 --- a/govcd/nat_test.go +++ b/govcd/nat_test.go @@ -19,7 +19,7 @@ func (vcd *TestVCD) Test_NatRule(check *C) { check.Assert(err, IsNil) check.Assert(edge.EdgeGateway.Name, Equals, vcd.config.VCD.EdgeGateway) - natRule := &types.EdgeSnatRule{ + natRule := &types.EdgeNatRule{ Action: "snat", TranslatedAddress: vcd.config.VCD.ExternalIp, // Edge gateway address OriginalAddress: vcd.config.VCD.InternalIp, @@ -34,22 +34,22 @@ func (vcd *TestVCD) Test_NatRule(check *C) { // Vnic: "0", } - createdSnatRule, err := edge.CreateSnatRule(natRule) + createdSnatRule, err := edge.CreateNsxvNatRule(natRule) check.Assert(err, IsNil) - gotSnatRule, err := edge.GetSnatRuleById(createdSnatRule.ID) + gotSnatRule, err := edge.GetNsxvNatRuleById(createdSnatRule.ID) check.Assert(err, IsNil) check.Assert(gotSnatRule.ID, Equals, createdSnatRule.ID) // Set ID and update nat rule with description natRule.ID = gotSnatRule.ID natRule.Description = "Description for SNAT rule" - updatedSnatRule, err := edge.UpdateSnatRule(natRule) + updatedSnatRule, err := edge.UpdateNsxvNatRule(natRule) check.Assert(err, IsNil) check.Assert(updatedSnatRule.Description, Equals, natRule.Description) - err = edge.DeleteSnatRuleById(gotSnatRule.ID) + err = edge.DeleteNsxvNatRuleById(gotSnatRule.ID) check.Assert(err, IsNil) } diff --git a/types/v56/types.go b/types/v56/types.go index 11b1955c7..e43d0060c 100644 --- a/types/v56/types.go +++ b/types/v56/types.go @@ -1821,26 +1821,8 @@ type LbVirtualServer struct { // EdgeNatRule contains shared structure for SNAT and DNAT rule configuration using // NSX-V proxied edge gateway endpoint +// // https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide type EdgeNatRule struct { - XMLName xml.Name `xml:"natRule"` - ID string `xml:"ruleId,omitempty"` - Name string `xml:"name,omitempty"` - RuleType string `xml:"ruleType,omitempty"` - RuleTag string `xml:"ruleTag,omitempty"` - Action string `xml:"action,omitempty"` - Vnic string `xml:"vnic,omitempty"` - OriginalAddress string `xml:"originalAddress,omitempty"` - TranslatedAddress string `xml:"translatedAddress,omitempty"` - LoggingEnabled string `xml:"loggingEnabled,omitempty"` - Enabled string `xml:"enabled,omitempty"` - Description string `xml:"description,omitempty"` - Protocol string `xml:"protocol,omitempty"` - OriginalPort string `xml:"originalPort,omitempty"` - TranslatedPort string `xml:"translatedPort,omitempty"` -} - -// EdgeSnatRule embeds EdgeNatRule and adds SNAT specific fields -type EdgeSnatRule struct { XMLName xml.Name `xml:"natRule"` ID string `xml:"ruleId,omitempty"` RuleType string `xml:"ruleType,omitempty"` @@ -1856,16 +1838,13 @@ type EdgeSnatRule struct { OriginalPort string `xml:"originalPort,omitempty"` TranslatedPort string `xml:"translatedPort,omitempty"` + // Optional SNAT related fields SnatMatchDestinationAddress string `xml:"dnatMatchDestinationAddress,omitempty"` SnatMatchDestinationPort string `xml:"dnatMatchDestinationPort,omitempty"` -} - -// EdgeDnatRule embeds EdgeNatRule and adds DNAT specific fields -type EdgeDnatRule struct { - EdgeNatRule - DnatMatchSourceAddress string `xml:"dnatMatchSourceAddress"` - DnatMatchSourcePort string `xml:"dnatMatchSourcePort"` + // Optional DNAT related fields + DnatMatchSourceAddress string `xml:"dnatMatchSourceAddress,omitempty"` + DnatMatchSourcePort string `xml:"dnatMatchSourcePort,omitempty"` } // VendorTemplate is information about a vendor service template. This is optional. From 121082c1fe0e86bd0ae40d4ea8c18929a4f208a0 Mon Sep 17 00:00:00 2001 From: Dainius S Date: Thu, 5 Sep 2019 16:49:33 +0300 Subject: [PATCH 07/23] Remove omitempty from booleans --- types/v56/types.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/types/v56/types.go b/types/v56/types.go index c7ebb71e3..9934388e5 100644 --- a/types/v56/types.go +++ b/types/v56/types.go @@ -1830,12 +1830,13 @@ type EdgeNatRule struct { Vnic string `xml:"vnic,omitempty"` OriginalAddress string `xml:"originalAddress,omitempty"` TranslatedAddress string `xml:"translatedAddress,omitempty"` - LoggingEnabled string `xml:"loggingEnabled,omitempty"` - Enabled string `xml:"enabled,omitempty"` + LoggingEnabled bool `xml:"loggingEnabled"` + Enabled bool `xml:"enabled"` Description string `xml:"description,omitempty"` Protocol string `xml:"protocol,omitempty"` OriginalPort string `xml:"originalPort,omitempty"` TranslatedPort string `xml:"translatedPort,omitempty"` + IcmpType string `xml:"icmpType,omitempty"` // Optional SNAT related fields SnatMatchDestinationAddress string `xml:"dnatMatchDestinationAddress,omitempty"` From 41827c863963ed8adbc70f905936ee5c9a0dde2c Mon Sep 17 00:00:00 2001 From: Dainius S Date: Mon, 9 Sep 2019 08:16:48 +0300 Subject: [PATCH 08/23] Add edgegateway.GetVnics --- govcd/edgegateway.go | 19 +++++++++++++++ govcd/edgegateway_test.go | 24 +++++++++++++++++++ govcd/nat.go | 10 ++++---- govcd/nat_test.go | 3 +-- types/v56/constants.go | 3 ++- types/v56/types.go | 49 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 100 insertions(+), 8 deletions(-) diff --git a/govcd/edgegateway.go b/govcd/edgegateway.go index cbfff4d72..d8da623c1 100644 --- a/govcd/edgegateway.go +++ b/govcd/edgegateway.go @@ -1167,3 +1167,22 @@ func validateUpdateLBGeneralParams(logLevel string) error { return nil } + +// GetVnics retrieves a structure of type EdgeGatewayVnics which contains network interfaces +// available in Edge Gateway +func (egw *EdgeGateway) GetVnics() (*types.EdgeGatewayVnics, error) { + httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeVnicConfig) + if err != nil { + return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) + } + + vnicConfig := &types.EdgeGatewayVnics{} + _, err = egw.client.ExecuteRequest(httpPath, http.MethodGet, types.AnyXMLMime, + "unable to edge gateway vnic configuration: %s", nil, vnicConfig) + + if err != nil { + return nil, err + } + + return vnicConfig, nil +} diff --git a/govcd/edgegateway_test.go b/govcd/edgegateway_test.go index c9e938f07..5950950fc 100644 --- a/govcd/edgegateway_test.go +++ b/govcd/edgegateway_test.go @@ -582,3 +582,27 @@ func (vcd *TestVCD) TestEdgeGateway_UpdateLBGeneralParams(check *C) { // Validate load balancer configuration against initially cached version testCheckLoadBalancerConfig(beforeLb, beforeLbXml, edge, check) } + +func (vcd *TestVCD) TestEdgeGateway_GetVnics(check *C) { + if vcd.config.VCD.EdgeGateway == "" { + check.Skip("Skipping test because no edge gatway given") + } + edge, err := vcd.vdc.FindEdgeGateway(vcd.config.VCD.EdgeGateway) + check.Assert(err, IsNil) + + if !edge.HasAdvancedNetworking() { + check.Skip("Skipping test because the edge gateway does not have advanced networking enabled") + } + + vnics, err := edge.GetVnics() + check.Assert(err, IsNil) + check.Assert(len(vnics.Vnic) > 1, Equals, true) + check.Assert(vnics.Vnic[0].Name, Equals, vcd.config.VCD.ExternalNetwork) + check.Assert(vnics.Vnic[0].PortgroupName, Equals, vcd.config.VCD.ExternalNetwork) + check.Assert(vnics.Vnic[0].AddressGroups.AddressGroup.PrimaryAddress, Equals, vcd.config.VCD.ExternalIp) + check.Assert(vnics.Vnic[0].Type, Equals, "uplink") + + check.Assert(vnics.Vnic[1].Type, Equals, "internal") + check.Assert(vnics.Vnic[1].PortgroupName, Equals, vcd.config.VCD.Network.Net1) + +} diff --git a/govcd/nat.go b/govcd/nat.go index 41343bc56..9b4c9c595 100644 --- a/govcd/nat.go +++ b/govcd/nat.go @@ -14,14 +14,14 @@ import ( // requestEdgeNatRules nests EdgeNatRule as a convenience for unmarshalling POST requests type requestEdgeNatRules struct { - XMLName xml.Name `xml:"natRules"` + XMLName xml.Name `xml:"natRules"` EdgeNatRules []*types.EdgeNatRule `xml:"natRule"` } // responseEdgeNatRules is used to unwrap response when retrieving type responseEdgeNatRules struct { - XMLName xml.Name `xml:"nat"` - Version string `xml:"version"` + XMLName xml.Name `xml:"nat"` + Version string `xml:"version"` NatRules requestEdgeNatRules `xml:"natRules"` } @@ -67,7 +67,7 @@ func (egw *EdgeGateway) UpdateNsxvNatRule(natRuleConfig *types.EdgeNatRule) (*ty if err != nil { return nil, err } - + httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeCreateNatPath + "/" + natRuleConfig.ID) if err != nil { return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) @@ -83,7 +83,7 @@ func (egw *EdgeGateway) UpdateNsxvNatRule(natRuleConfig *types.EdgeNatRule) (*ty readNatRule, err := egw.GetNsxvNatRuleById(natRuleConfig.ID) if err != nil { return nil, fmt.Errorf("unable to retrieve NAT rule with ID (%s) after update: %s", - readNatRule.ID, err) + readNatRule.ID, err) } return readNatRule, nil } diff --git a/govcd/nat_test.go b/govcd/nat_test.go index 2e18cceaf..2115ccb7a 100644 --- a/govcd/nat_test.go +++ b/govcd/nat_test.go @@ -23,7 +23,7 @@ func (vcd *TestVCD) Test_NatRule(check *C) { Action: "snat", TranslatedAddress: vcd.config.VCD.ExternalIp, // Edge gateway address OriginalAddress: vcd.config.VCD.InternalIp, - Enabled: "true", + Enabled: true, // RuleType: "user", // Name: "asd", // SnatMatchDestinationAddress: "any", @@ -48,7 +48,6 @@ func (vcd *TestVCD) Test_NatRule(check *C) { check.Assert(err, IsNil) check.Assert(updatedSnatRule.Description, Equals, natRule.Description) - err = edge.DeleteNsxvNatRuleById(gotSnatRule.ID) check.Assert(err, IsNil) diff --git a/types/v56/constants.go b/types/v56/constants.go index 45e97a92b..16dea1845 100644 --- a/types/v56/constants.go +++ b/types/v56/constants.go @@ -163,10 +163,11 @@ const ( LbVirtualServerPath = "/loadbalancer/config/virtualservers/" ) -// Edge gateway NAT rule API endpoints +// Edge gateway API endpoints const ( EdgeNatPath = "/nat/config" EdgeCreateNatPath = "/nat/config/rules" + EdgeVnicConfig = "/vnics" ) // Guest customization statuses. These are all known possible statuses diff --git a/types/v56/types.go b/types/v56/types.go index 9934388e5..b89c78557 100644 --- a/types/v56/types.go +++ b/types/v56/types.go @@ -2700,3 +2700,52 @@ type AdminCatalogRecord struct { Link *Link `xml:"Link,omitempty"` Vdc *Metadata `xml:"Metadata,omitempty"` } + +// EdgeGatewayVnics is a data structure holding information of vNic configuration in NSX-V edge +// gateway +type EdgeGatewayVnics struct { + XMLName xml.Name `xml:"vnics"` + Vnic []struct { + Label string `xml:"label"` + Name string `xml:"name"` + AddressGroups struct { + AddressGroup struct { + PrimaryAddress string `xml:"primaryAddress"` + SecondaryAddresses struct { + IpAddress []string `xml:"ipAddress"` + } `xml:"secondaryAddresses"` + SubnetMask string `xml:"subnetMask"` + SubnetPrefixLength string `xml:"subnetPrefixLength"` + } `xml:"addressGroup"` + } `xml:"addressGroups"` + Mtu string `xml:"mtu"` + Type string `xml:"type"` + IsConnected string `xml:"isConnected"` + Index string `xml:"index"` + PortgroupId string `xml:"portgroupId"` + PortgroupName string `xml:"portgroupName"` + EnableProxyArp string `xml:"enableProxyArp"` + EnableSendRedirects string `xml:"enableSendRedirects"` + SubInterfaces struct { + SubInterface []struct { + IsConnected string `xml:"isConnected"` + Label string `xml:"label"` + Name string `xml:"name"` + Index string `xml:"index"` + TunnelId string `xml:"tunnelId"` + LogicalSwitchId string `xml:"logicalSwitchId"` + LogicalSwitchName string `xml:"logicalSwitchName"` + EnableSendRedirects string `xml:"enableSendRedirects"` + Mtu string `xml:"mtu"` + AddressGroups struct { + AddressGroup struct { + Text string `xml:",chardata"` + PrimaryAddress string `xml:"primaryAddress"` + SubnetMask string `xml:"subnetMask"` + SubnetPrefixLength string `xml:"subnetPrefixLength"` + } `xml:"addressGroup"` + } `xml:"addressGroups"` + } `xml:"subInterface"` + } `xml:"subInterfaces"` + } `xml:"vnic"` +} From ffa68c47f4882280fe43104321c984b30210fe4e Mon Sep 17 00:00:00 2001 From: Dainius S Date: Mon, 9 Sep 2019 12:10:28 +0300 Subject: [PATCH 09/23] GetVnicIndexFromNetworkNameType() --- govcd/edgegateway.go | 60 ++++++++ govcd/edgegateway_unit_test.go | 267 +++++++++++++++++++++++++++++++++ types/v56/constants.go | 8 + types/v56/types.go | 4 +- 4 files changed, 337 insertions(+), 2 deletions(-) diff --git a/govcd/edgegateway.go b/govcd/edgegateway.go index d8da623c1..4a1b66bca 100644 --- a/govcd/edgegateway.go +++ b/govcd/edgegateway.go @@ -1171,6 +1171,10 @@ func validateUpdateLBGeneralParams(logLevel string) error { // GetVnics retrieves a structure of type EdgeGatewayVnics which contains network interfaces // available in Edge Gateway func (egw *EdgeGateway) GetVnics() (*types.EdgeGatewayVnics, error) { + if !egw.HasAdvancedNetworking() { + return nil, fmt.Errorf("only advanced edge gateway supports vNics") + } + httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeVnicConfig) if err != nil { return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) @@ -1186,3 +1190,59 @@ func (egw *EdgeGateway) GetVnics() (*types.EdgeGatewayVnics, error) { return vnicConfig, nil } + +// GetVnicIndexFromNetworkNameType returns *int of vNic index for specified network name and network type +// networkType one of: 'internal', 'uplink', 'trunk', 'subinterface' +// networkName cannot be empty +func (egw *EdgeGateway) GetVnicIndexFromNetworkNameType(networkName, networkType string) (*int, error) { + vnics, err := egw.GetVnics() + if err != nil { + return nil, fmt.Errorf("cannot retrieve vNic configuration: %s", err) + } + return getVnicIndexFromNetworkNameType(networkName, networkType, vnics) +} + +// getVnicIndexFromNetworkNameType is wrapped and used by public function GetVnicIndexFromNetworkNameType +func getVnicIndexFromNetworkNameType(networkName, networkType string, vnics *types.EdgeGatewayVnics) (*int, error) { + if networkName == "" { + return nil, fmt.Errorf("network name cannot be empty") + } + if networkType != types.EdgeGatewayVnicTypeUplink && + networkType != types.EdgeGatewayVnicTypeInternal && + networkType != types.EdgeGatewayVnicTypeTrunk && + networkType != types.EdgeGatewayVnicTypeSubinterface { + return nil, fmt.Errorf("networkType must be one of 'uplink', 'internal', 'trunk', 'subinterface'") + } + + var foundIndex *int + foundCount := 0 + + for _, vnic := range vnics.Vnic { + // Look for matching portgroup name and network type + if networkType != types.EdgeGatewayVnicTypeSubinterface && vnic.PortgroupName == networkName && vnic.Type == networkType { + foundIndex = vnic.Index + foundCount++ + } + + // if looking for subinterface - check if they are defined and search for logicalSwitchName + if networkType == types.EdgeGatewayVnicTypeSubinterface && len(vnic.SubInterfaces.SubInterface) > 0 { + for _, subInterface := range vnic.SubInterfaces.SubInterface { + if subInterface.LogicalSwitchName == networkName { + foundIndex = subInterface.Index + foundCount++ + } + } + } + } + + if foundCount > 1 { + return nil, fmt.Errorf("more than one (%d) networks of type '%s' with name '%s' found", + foundCount, networkType, networkName) + } + + if foundCount == 0 { + return nil, ErrorEntityNotFound + } + + return foundIndex, nil +} diff --git a/govcd/edgegateway_unit_test.go b/govcd/edgegateway_unit_test.go index adb3ee1a2..f155aebe5 100644 --- a/govcd/edgegateway_unit_test.go +++ b/govcd/edgegateway_unit_test.go @@ -7,8 +7,13 @@ package govcd import ( + "encoding/xml" + "fmt" "regexp" + "strings" "testing" + + "github.com/vmware/go-vcloud-director/v2/types/v56" ) func TestGetPseudoUUID(t *testing.T) { @@ -30,3 +35,265 @@ func TestGetPseudoUUID(t *testing.T) { seen[uuid] = N } } + +func Test_getVnicIndexFromNetworkNameType(t *testing.T) { + // sample body for unit testing. vNic4 interface is made to contain duplicate network + // name on purpose (not a real respone from API) + sampleBody := []byte(` + + + + my-ext-network + + + 192.168.1.110 + + 192.168.1.118 + 192.168.1.115 + 192.168.1.116 + 192.168.1.117 + + 255.255.255.0 + 24 + + + 1500 + uplink + true + 0 + f2547dd9-e0f7-4d81-97c1-dd33e5e0fbbf + my-ext-network + false + true + + + + vnic1 + + + 10.10.10.5 + 255.255.255.0 + 24 + + + 1500 + internal + true + 1 + 95bffe8e-7e67-452d-abf2-535ac298db2b + my-vdc-int-net + false + true + + + + vdcf9daf2da-b4f9-4921-a2f4-d77a943a381c + + 1600 + trunk + + + true + + vnic1-subinterface + 10 + 1 + 9a222ba9-23bc-41bf-b10f-0168f87b80ab + with-subinterfaces + true + 1500 + + + 3.3.3.1 + 255.255.255.0 + 24 + + + + + true + + vnic2-subinterface + 11 + 2 + aecdf098-13ea-4ddf-9c4b-6a06372aa163 + subinterface2 + true + 1500 + + + 7.7.7.1 + 255.255.255.0 + 24 + + + + + true + + vnic3-subinterface + 12 + 3 + c489d85d-df17-409c-810e-12af5aa5c1c2 + subinterface-subnet-clash + true + 1500 + + + 4.3.3.1 + 255.255.255.0 + 24 + + + + + true + 2 + dvportgroup-20228 + dvs.VCDVS-Trunk-Portgroup-vdcf9daf2da-b4f9-4921-a2f4-d77a943a381c + false + true + + + + vnic3 + + + 13.13.1.1 + 255.255.255.0 + 24 + + + 1500 + internal + true + 3 + 96a68fd4-c21a-41d6-98a4-fbf32c96480f + my-vdc-int-net2 + false + true + + + + vnic4 + + + 13.13.1.1 + 255.255.255.0 + 24 + + + 1500 + internal + true + 3 + 96a68fd4-c21a-41d6-98a4-fbf32c96480f + my-vdc-int-net2 + false + true + + + + vnic5 + + 1500 + internal + false + 5 + false + true + + + + vnic6 + + 1500 + internal + false + 6 + false + true + + + + vnic7 + + 1500 + internal + false + 7 + false + true + + + + vnic8 + + 1500 + internal + false + 8 + false + true + + + + vnic9 + + 1500 + internal + false + 9 + false + true + +`) + vnicObject := &types.EdgeGatewayVnics{} + + _ = xml.Unmarshal(sampleBody, vnicObject) + + tests := []struct { + name string + networkName string + networkType string + expectedVnicIndex *int + hasError bool + expectedError error + }{ + {"ExtNetwork", "my-ext-network", types.EdgeGatewayVnicTypeUplink, takeAddress(0), false, nil}, + {"OrgNetwork", "my-vdc-int-net", types.EdgeGatewayVnicTypeInternal, takeAddress(1), false, nil}, + {"WithSubinterfaces", "with-subinterfaces", types.EdgeGatewayVnicTypeSubinterface, takeAddress(10), false, nil}, + {"WithSubinterfaces2", "subinterface2", types.EdgeGatewayVnicTypeSubinterface, takeAddress(11), false, nil}, + {"Trunk", "dvs.VCDVS-Trunk-Portgroup-vdcf9daf2da-b4f9-4921-a2f4-d77a943a381c", types.EdgeGatewayVnicTypeTrunk, takeAddress(2), false, nil}, + {"NonExistingUplink", "invalid-network-name", types.EdgeGatewayVnicTypeUplink, nil, true, ErrorEntityNotFound}, + {"NonExistingInternal", "invalid-network-name", types.EdgeGatewayVnicTypeInternal, nil, true, ErrorEntityNotFound}, + {"NonExistingSubinterface", "invalid-network-name", types.EdgeGatewayVnicTypeSubinterface, nil, true, ErrorEntityNotFound}, + {"NonExistingTrunk", "invalid-network-name", types.EdgeGatewayVnicTypeTrunk, nil, true, ErrorEntityNotFound}, + {"MoreThanOne", "my-vdc-int-net2", types.EdgeGatewayVnicTypeInternal, nil, true, + fmt.Errorf("more than one (2) networks of type 'internal' with name 'my-vdc-int-net2' found")}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + vnicIndex, err := getVnicIndexFromNetworkNameType(tt.networkName, tt.networkType, vnicObject) + + if !tt.hasError && err != nil { + t.Errorf("error was not expected: %s", err) + } + + if tt.hasError && !strings.Contains(err.Error(), tt.expectedError.Error()) { + t.Errorf("Got unexpected error: %s, expected: %s", err, tt.expectedError) + } + + if vnicIndex != nil && tt.expectedVnicIndex != nil && *vnicIndex != *tt.expectedVnicIndex { + t.Errorf("Got unexpected vNic name: %d, expected: %d", vnicIndex, tt.expectedVnicIndex) + } + + }) + } + +} + +// takeAddress is a helper which can gives address of `int` +func takeAddress(x int) *int { + return &x +} diff --git a/types/v56/constants.go b/types/v56/constants.go index 16dea1845..8eb73e736 100644 --- a/types/v56/constants.go +++ b/types/v56/constants.go @@ -178,3 +178,11 @@ const ( GuestCustStatusFailed = "GC_FAILED" GuestCustStatusRebootPending = "REBOOT_PENDING" ) + +// Edge gateway vNic types +const ( + EdgeGatewayVnicTypeUplink = "uplink" + EdgeGatewayVnicTypeInternal = "internal" + EdgeGatewayVnicTypeTrunk = "trunk" + EdgeGatewayVnicTypeSubinterface = "subinterface" +) diff --git a/types/v56/types.go b/types/v56/types.go index b89c78557..fc5226056 100644 --- a/types/v56/types.go +++ b/types/v56/types.go @@ -2721,7 +2721,7 @@ type EdgeGatewayVnics struct { Mtu string `xml:"mtu"` Type string `xml:"type"` IsConnected string `xml:"isConnected"` - Index string `xml:"index"` + Index *int `xml:"index"` PortgroupId string `xml:"portgroupId"` PortgroupName string `xml:"portgroupName"` EnableProxyArp string `xml:"enableProxyArp"` @@ -2731,7 +2731,7 @@ type EdgeGatewayVnics struct { IsConnected string `xml:"isConnected"` Label string `xml:"label"` Name string `xml:"name"` - Index string `xml:"index"` + Index *int `xml:"index"` TunnelId string `xml:"tunnelId"` LogicalSwitchId string `xml:"logicalSwitchId"` LogicalSwitchName string `xml:"logicalSwitchName"` From 42abe901ed6f1d971deb911de38b819963603b4f Mon Sep 17 00:00:00 2001 From: Dainius S Date: Mon, 9 Sep 2019 15:36:31 +0300 Subject: [PATCH 10/23] WIP --- govcd/edgegateway.go | 48 +++++++ govcd/edgegateway_unit_test.go | 246 ++++++++++++++++++++++++++++++++- govcd/vapp_test.go | 14 ++ types/v56/types.go | 2 +- 4 files changed, 308 insertions(+), 2 deletions(-) diff --git a/govcd/edgegateway.go b/govcd/edgegateway.go index 4a1b66bca..65610e72b 100644 --- a/govcd/edgegateway.go +++ b/govcd/edgegateway.go @@ -1202,6 +1202,16 @@ func (egw *EdgeGateway) GetVnicIndexFromNetworkNameType(networkName, networkType return getVnicIndexFromNetworkNameType(networkName, networkType, vnics) } +// GetNetworkNameTypeFromVnicIndex returns network name and network type for given vNic index +// returned networkType can be one of: 'internal', 'uplink', 'trunk', 'subinterface' +func (egw *EdgeGateway) GetNetworkNameTypeFromVnicIndex(vNicIndex int) (string, string, error) { + vnics, err := egw.GetVnics() + if err != nil { + return "", "", fmt.Errorf("cannot retrieve vNic configuration: %s", err) + } + return getNetworkNameTypeFromVnicIndex(vNicIndex, vnics) +} + // getVnicIndexFromNetworkNameType is wrapped and used by public function GetVnicIndexFromNetworkNameType func getVnicIndexFromNetworkNameType(networkName, networkType string, vnics *types.EdgeGatewayVnics) (*int, error) { if networkName == "" { @@ -1246,3 +1256,41 @@ func getVnicIndexFromNetworkNameType(networkName, networkType string, vnics *typ return foundIndex, nil } + +func getNetworkNameTypeFromVnicIndex(vNicIndex int, vnics *types.EdgeGatewayVnics) (string, string, error) { + if vNicIndex < 0 { + return "", "", fmt.Errorf("vNic index cannot be negative") + } + + foundCount := 0 + var networkName, networkType string + + for _, vnic := range vnics.Vnic { + if vnic.Index != nil && *vnic.Index == vNicIndex { + foundCount++ + networkName = vnic.PortgroupName + networkType = vnic.Type + } + + // Search inside "subinterface tree" + if vnic.Type == types.EdgeGatewayVnicTypeTrunk && len(vnic.SubInterfaces.SubInterface) > 0 { + for _, subInterface := range vnic.SubInterfaces.SubInterface { + if subInterface.Index != nil && *subInterface.Index == vNicIndex { + foundCount++ + networkName = subInterface.LogicalSwitchName + networkType = types.EdgeGatewayVnicTypeSubinterface + } + } + } + } + + if foundCount > 1 { + return "", "", fmt.Errorf("more than one networks found for vNic %d", vNicIndex) + } + + if foundCount == 0 { + return "", "", ErrorEntityNotFound + } + + return networkName, networkType, nil +} diff --git a/govcd/edgegateway_unit_test.go b/govcd/edgegateway_unit_test.go index f155aebe5..55be6a0c1 100644 --- a/govcd/edgegateway_unit_test.go +++ b/govcd/edgegateway_unit_test.go @@ -249,7 +249,6 @@ func Test_getVnicIndexFromNetworkNameType(t *testing.T) { `) vnicObject := &types.EdgeGatewayVnics{} - _ = xml.Unmarshal(sampleBody, vnicObject) tests := []struct { @@ -293,6 +292,251 @@ func Test_getVnicIndexFromNetworkNameType(t *testing.T) { } +func Test_getNetworkNameTypeFromVnicIndex(t *testing.T) { + // sample body for unit testing. vNic4 interface is made to contain duplicate network + // name on purpose (not a real respone from API) + sampleBody := []byte(` + + + + my-ext-network + + + 192.168.1.110 + + 192.168.1.118 + 192.168.1.115 + 192.168.1.116 + 192.168.1.117 + + 255.255.255.0 + 24 + + + 1500 + uplink + true + 0 + f2547dd9-e0f7-4d81-97c1-dd33e5e0fbbf + my-ext-network + false + true + + + + vnic1 + + + 10.10.10.5 + 255.255.255.0 + 24 + + + 1500 + internal + true + 1 + 95bffe8e-7e67-452d-abf2-535ac298db2b + my-vdc-int-net + false + true + + + + vdcf9daf2da-b4f9-4921-a2f4-d77a943a381c + + 1600 + trunk + + + true + + vnic1-subinterface + 10 + 1 + 9a222ba9-23bc-41bf-b10f-0168f87b80ab + with-subinterfaces + true + 1500 + + + 3.3.3.1 + 255.255.255.0 + 24 + + + + + true + + vnic2-subinterface + 11 + 2 + aecdf098-13ea-4ddf-9c4b-6a06372aa163 + subinterface2 + true + 1500 + + + 7.7.7.1 + 255.255.255.0 + 24 + + + + + true + + vnic3-subinterface + 12 + 3 + c489d85d-df17-409c-810e-12af5aa5c1c2 + subinterface-subnet-clash + true + 1500 + + + 4.3.3.1 + 255.255.255.0 + 24 + + + + + true + 2 + dvportgroup-20228 + dvs.VCDVS-Trunk-Portgroup-vdcf9daf2da-b4f9-4921-a2f4-d77a943a381c + false + true + + + + vnic3 + + + 13.13.1.1 + 255.255.255.0 + 24 + + + 1500 + internal + true + 3 + 96a68fd4-c21a-41d6-98a4-fbf32c96480f + my-vdc-int-net2 + false + true + + + + vnic4 + + + 13.13.1.1 + 255.255.255.0 + 24 + + + 1500 + internal + true + 3 + 96a68fd4-c21a-41d6-98a4-fbf32c96480f + my-vdc-int-net2 + false + true + + + + vnic5 + + 1500 + internal + false + 5 + false + true + + + + vnic6 + + 1500 + internal + false + 6 + false + true + + + + vnic7 + + 1500 + internal + false + 7 + false + true + + + + vnic8 + + 1500 + internal + false + 8 + false + true + + + + vnic9 + + 1500 + internal + false + 9 + false + true + +`) + vnicObject := &types.EdgeGatewayVnics{} + _ = xml.Unmarshal(sampleBody, vnicObject) + + tests := []struct { + name string + vnicIndex int + expectednetworkName string + expectednetworkType string + hasError bool + expectedError error + }{ + {"0", 0, "my-ext-network", "uplink", false, nil}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + vnicIndex, err := getVnicIndexFromNetworkNameType(tt.networkName, tt.networkType, vnicObject) + + if !tt.hasError && err != nil { + t.Errorf("error was not expected: %s", err) + } + + if tt.hasError && !strings.Contains(err.Error(), tt.expectedError.Error()) { + t.Errorf("Got unexpected error: %s, expected: %s", err, tt.expectedError) + } + + if vnicIndex != nil && tt.expectedVnicIndex != nil && *vnicIndex != *tt.expectedVnicIndex { + t.Errorf("Got unexpected vNic name: %d, expected: %d", vnicIndex, tt.expectedVnicIndex) + } + + }) + } +} + // takeAddress is a helper which can gives address of `int` func takeAddress(x int) *int { return &x diff --git a/govcd/vapp_test.go b/govcd/vapp_test.go index 67fe0c677..555b27e34 100644 --- a/govcd/vapp_test.go +++ b/govcd/vapp_test.go @@ -9,6 +9,7 @@ package govcd import ( "fmt" "regexp" + "sort" "github.com/vmware/go-vcloud-director/v2/types/v56" . "gopkg.in/check.v1" @@ -574,8 +575,21 @@ func (vcd *TestVCD) Test_AddNewVMMultiNIC(check *C) { } func verifyNetworkConnectionSection(check *C, actual, desired *types.NetworkConnectionSection) { + check.Assert(len(actual.NetworkConnection), Equals, len(desired.NetworkConnection)) check.Assert(actual.PrimaryNetworkConnectionIndex, Equals, desired.PrimaryNetworkConnectionIndex) + + // sort both objects by index before comparison + sort.SliceStable(actual.NetworkConnection, func(i, j int) bool { + return actual.NetworkConnection[i].NetworkConnectionIndex < + actual.NetworkConnection[j].NetworkConnectionIndex + }) + + sort.SliceStable(desired.NetworkConnection, func(i, j int) bool { + return desired.NetworkConnection[i].NetworkConnectionIndex < + desired.NetworkConnection[j].NetworkConnectionIndex + }) + for index := range actual.NetworkConnection { actualNic := actual.NetworkConnection[index] desiredNic := desired.NetworkConnection[index] diff --git a/types/v56/types.go b/types/v56/types.go index fc5226056..42fec8065 100644 --- a/types/v56/types.go +++ b/types/v56/types.go @@ -1827,7 +1827,7 @@ type EdgeNatRule struct { RuleType string `xml:"ruleType,omitempty"` RuleTag string `xml:"ruleTag,omitempty"` Action string `xml:"action,omitempty"` - Vnic string `xml:"vnic,omitempty"` + Vnic *int `xml:"vnic,omitempty"` OriginalAddress string `xml:"originalAddress,omitempty"` TranslatedAddress string `xml:"translatedAddress,omitempty"` LoggingEnabled bool `xml:"loggingEnabled"` From 4c3305d3b2c9dbc245b606bc539b6f8042832578 Mon Sep 17 00:00:00 2001 From: Dainius S Date: Tue, 10 Sep 2019 09:19:04 +0300 Subject: [PATCH 11/23] WIP WIP WIP --- govcd/edgegateway_unit_test.go | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/govcd/edgegateway_unit_test.go b/govcd/edgegateway_unit_test.go index 55be6a0c1..d4bf47f96 100644 --- a/govcd/edgegateway_unit_test.go +++ b/govcd/edgegateway_unit_test.go @@ -38,7 +38,7 @@ func TestGetPseudoUUID(t *testing.T) { func Test_getVnicIndexFromNetworkNameType(t *testing.T) { // sample body for unit testing. vNic4 interface is made to contain duplicate network - // name on purpose (not a real respone from API) + // name on purpose (not a real response from API) sampleBody := []byte(` @@ -293,8 +293,7 @@ func Test_getVnicIndexFromNetworkNameType(t *testing.T) { } func Test_getNetworkNameTypeFromVnicIndex(t *testing.T) { - // sample body for unit testing. vNic4 interface is made to contain duplicate network - // name on purpose (not a real respone from API) + // sample body for unit testing. The vNic 8 record is duplicated on purpose for testing. sampleBody := []byte(` @@ -492,6 +491,17 @@ func Test_getNetworkNameTypeFromVnicIndex(t *testing.T) { false true + + + vnic8 + + 1500 + internal + false + 8 + false + true + vnic9 @@ -515,11 +525,16 @@ func Test_getNetworkNameTypeFromVnicIndex(t *testing.T) { hasError bool expectedError error }{ - {"0", 0, "my-ext-network", "uplink", false, nil}, + {"ExternalUplink", 0, "my-ext-network", "uplink", false, nil}, + {"Internal", 1, "my-vdc-int-net", "internal", false, nil}, + {"Trunk", 2, "dvs.VCDVS-Trunk-Portgroup-vdcf9daf2da-b4f9-4921-a2f4-d77a943a381c", "trunk", false, nil}, + {"Subinterface", 10, "with-subinterfaces", "subinterface", false, nil}, + {"NonExistent", 219, "", "", true, ErrorEntityNotFound}, + {"DuplicateRecords", 8, "", "", true, fmt.Errorf("more than one networks found for vNic 8")}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - vnicIndex, err := getVnicIndexFromNetworkNameType(tt.networkName, tt.networkType, vnicObject) + networkName, networkType, err := getNetworkNameTypeFromVnicIndex(tt.vnicIndex, vnicObject) if !tt.hasError && err != nil { t.Errorf("error was not expected: %s", err) @@ -529,8 +544,12 @@ func Test_getNetworkNameTypeFromVnicIndex(t *testing.T) { t.Errorf("Got unexpected error: %s, expected: %s", err, tt.expectedError) } - if vnicIndex != nil && tt.expectedVnicIndex != nil && *vnicIndex != *tt.expectedVnicIndex { - t.Errorf("Got unexpected vNic name: %d, expected: %d", vnicIndex, tt.expectedVnicIndex) + if networkName != tt.expectednetworkName { + t.Errorf("Got unexpected network name: %s, expected: %s", networkName, tt.expectednetworkName) + } + + if networkType != tt.expectednetworkType { + t.Errorf("Got unexpected vNic name: %s, expected: %s", networkType, tt.expectednetworkType) } }) From c05cdd94f5ee80832d1fa04f09f9eb6bb7fcb38d Mon Sep 17 00:00:00 2001 From: Dainius S Date: Tue, 10 Sep 2019 17:23:28 +0300 Subject: [PATCH 12/23] Add function docs --- govcd/edgegateway.go | 1 + govcd/nat_test.go | 8 -------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/govcd/edgegateway.go b/govcd/edgegateway.go index 65610e72b..f2093593a 100644 --- a/govcd/edgegateway.go +++ b/govcd/edgegateway.go @@ -1257,6 +1257,7 @@ func getVnicIndexFromNetworkNameType(networkName, networkType string, vnics *typ return foundIndex, nil } +// getNetworkNameTypeFromVnicIndex is wrapped and used by public function GetNetworkNameTypeFromVnicIndex func getNetworkNameTypeFromVnicIndex(vNicIndex int, vnics *types.EdgeGatewayVnics) (string, string, error) { if vNicIndex < 0 { return "", "", fmt.Errorf("vNic index cannot be negative") diff --git a/govcd/nat_test.go b/govcd/nat_test.go index 2115ccb7a..356e8b1e2 100644 --- a/govcd/nat_test.go +++ b/govcd/nat_test.go @@ -24,14 +24,6 @@ func (vcd *TestVCD) Test_NatRule(check *C) { TranslatedAddress: vcd.config.VCD.ExternalIp, // Edge gateway address OriginalAddress: vcd.config.VCD.InternalIp, Enabled: true, - // RuleType: "user", - // Name: "asd", - // SnatMatchDestinationAddress: "any", - // LoggingEnabled: "false", - // OriginalPort: "3380", - // TranslatedPort: "3380", - // Protocol: "any", - // Vnic: "0", } createdSnatRule, err := edge.CreateNsxvNatRule(natRule) From 74b10527fe5a7c5676dd3aa814170909998a10f5 Mon Sep 17 00:00:00 2001 From: Dainius S Date: Wed, 11 Sep 2019 10:34:56 +0300 Subject: [PATCH 13/23] Before landing --- govcd/api_vcd_test.go | 27 +++++++++ govcd/edgegateway.go | 8 +-- govcd/edgegateway_test.go | 2 +- govcd/nat.go | 31 ++++------ govcd/nat_test.go | 116 ++++++++++++++++++++++++++++++++++---- types/v56/types.go | 8 --- 6 files changed, 149 insertions(+), 43 deletions(-) diff --git a/govcd/api_vcd_test.go b/govcd/api_vcd_test.go index 625406960..884e3c3c4 100644 --- a/govcd/api_vcd_test.go +++ b/govcd/api_vcd_test.go @@ -73,6 +73,8 @@ const ( TestLbAppRule = "TestLbAppRule" TestLbVirtualServer = "TestLbVirtualServer" TestLb = "TestLb" + TestNsxvSnatRule = "TestNsxvSnatRule" + TestNsxvDnatRule = "TestNsxvDnatRule" ) const ( @@ -963,6 +965,31 @@ func (vcd *TestVCD) removeLeftoverEntities(entity CleanupEntity) { } return + case "nsxvNatRule": + if entity.Parent == "" { + vcd.infoCleanup("removeLeftoverEntries: [ERROR] No parent specified '%s'\n", entity.Name) + return + } + + orgName, vdcName, edgeName := splitParent(entity.Parent, "|") + + _, _, edge, err := getOrgVdcEdgeByNames(vcd, orgName, vdcName, edgeName) + if err != nil { + vcd.infoCleanup("removeLeftoverEntries: [ERROR] %s \n", err) + } + + err = edge.DeleteNsxvNatRuleById(entity.Name) + if IsNotFound(err) { + vcd.infoCleanup(notFoundMsg, entity.EntityType, entity.Name) + return + } + if err != nil { + vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err) + } + + vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy) + return + default: // If we reach this point, we are trying to clean up an entity that // we aren't prepared for yet. diff --git a/govcd/edgegateway.go b/govcd/edgegateway.go index f2093593a..69f0a34ee 100644 --- a/govcd/edgegateway.go +++ b/govcd/edgegateway.go @@ -1168,9 +1168,9 @@ func validateUpdateLBGeneralParams(logLevel string) error { return nil } -// GetVnics retrieves a structure of type EdgeGatewayVnics which contains network interfaces +// getVnics retrieves a structure of type EdgeGatewayVnics which contains network interfaces // available in Edge Gateway -func (egw *EdgeGateway) GetVnics() (*types.EdgeGatewayVnics, error) { +func (egw *EdgeGateway) getVnics() (*types.EdgeGatewayVnics, error) { if !egw.HasAdvancedNetworking() { return nil, fmt.Errorf("only advanced edge gateway supports vNics") } @@ -1195,7 +1195,7 @@ func (egw *EdgeGateway) GetVnics() (*types.EdgeGatewayVnics, error) { // networkType one of: 'internal', 'uplink', 'trunk', 'subinterface' // networkName cannot be empty func (egw *EdgeGateway) GetVnicIndexFromNetworkNameType(networkName, networkType string) (*int, error) { - vnics, err := egw.GetVnics() + vnics, err := egw.getVnics() if err != nil { return nil, fmt.Errorf("cannot retrieve vNic configuration: %s", err) } @@ -1205,7 +1205,7 @@ func (egw *EdgeGateway) GetVnicIndexFromNetworkNameType(networkName, networkType // GetNetworkNameTypeFromVnicIndex returns network name and network type for given vNic index // returned networkType can be one of: 'internal', 'uplink', 'trunk', 'subinterface' func (egw *EdgeGateway) GetNetworkNameTypeFromVnicIndex(vNicIndex int) (string, string, error) { - vnics, err := egw.GetVnics() + vnics, err := egw.getVnics() if err != nil { return "", "", fmt.Errorf("cannot retrieve vNic configuration: %s", err) } diff --git a/govcd/edgegateway_test.go b/govcd/edgegateway_test.go index 5950950fc..cf1ab4f83 100644 --- a/govcd/edgegateway_test.go +++ b/govcd/edgegateway_test.go @@ -594,7 +594,7 @@ func (vcd *TestVCD) TestEdgeGateway_GetVnics(check *C) { check.Skip("Skipping test because the edge gateway does not have advanced networking enabled") } - vnics, err := edge.GetVnics() + vnics, err := edge.getVnics() check.Assert(err, IsNil) check.Assert(len(vnics.Vnic) > 1, Equals, true) check.Assert(vnics.Vnic[0].Name, Equals, vcd.config.VCD.ExternalNetwork) diff --git a/govcd/nat.go b/govcd/nat.go index 9b4c9c595..2812eb614 100644 --- a/govcd/nat.go +++ b/govcd/nat.go @@ -8,6 +8,7 @@ import ( "encoding/xml" "fmt" "net/http" + "time" "github.com/vmware/go-vcloud-director/v2/types/v56" ) @@ -54,6 +55,8 @@ func (egw *EdgeGateway) CreateNsxvNatRule(natRuleConfig *types.EdgeNatRule) (*ty return nil, err } + time.Sleep(5 * time.Second) + readNatRule, err := egw.GetNsxvNatRule(&types.EdgeNatRule{ID: natRuleId}) if err != nil { return nil, fmt.Errorf("unable to retrieve NAT rule with ID (%s) after creation: %s", @@ -107,30 +110,12 @@ func (egw *EdgeGateway) GetNsxvNatRule(natRuleConfig *types.EdgeNatRule) (*types return nil, err } - // fmt.Printf("whole struct %+#v\n", natRuleResponse) - - // Search for nat rule by ID or by Name - // for _, rule := range natRuleResponse.NatRules.EdgeNatRules { for _, rule := range natRuleResponse.NatRules.EdgeNatRules { - // If ID was specified for lookup - look for the same ID - // fmt.Printf("checking %+#v\n", rule) if rule.ID != "" && rule.ID == natRuleConfig.ID { return rule, nil } } - // // If Name was specified for lookup - look for the same Name - // if lbAppRuleConfig.Name != "" && rule.Name == lbAppRuleConfig.Name { - // // We found it by name. Let's verify if search ID was specified and it matches the lookup object - // if lbAppRuleConfig.ID != "" && rule.ID != lbAppRuleConfig.ID { - // return nil, fmt.Errorf("load balancer application rule was found by name (%s)"+ - // ", but its ID (%s) does not match specified ID (%s)", - // rule.Name, rule.ID, lbAppRuleConfig.ID) - // } - // return rule, nil - // } - // } - return nil, ErrorEntityNotFound } @@ -138,7 +123,7 @@ func (egw *EdgeGateway) GetNsxvNatRuleById(id string) (*types.EdgeNatRule, error return egw.GetNsxvNatRule(&types.EdgeNatRule{ID: id}) } -func (egw *EdgeGateway) DeleteNsxvNatRule(natRuleConfig *types.EdgeNatRule) error { +func (egw *EdgeGateway) deleteNsxvNatRule(natRuleConfig *types.EdgeNatRule) error { err := validateDeleteNsxvNatRule(natRuleConfig, egw) if err != nil { return err @@ -149,6 +134,12 @@ func (egw *EdgeGateway) DeleteNsxvNatRule(natRuleConfig *types.EdgeNatRule) erro return fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) } + // check if the rule exists and pass back the error at it may be 'ErrorEntityNotFound' + _, err = egw.GetNsxvNatRuleById(natRuleConfig.ID) + if err != nil { + return err + } + _, err = egw.client.ExecuteRequestWithCustomError(httpPath, http.MethodDelete, types.AnyXMLMime, "unable to delete nat rule: %s", nil, &types.NSXError{}) if err != nil { @@ -159,7 +150,7 @@ func (egw *EdgeGateway) DeleteNsxvNatRule(natRuleConfig *types.EdgeNatRule) erro } func (egw *EdgeGateway) DeleteNsxvNatRuleById(id string) error { - return egw.DeleteNsxvNatRule(&types.EdgeNatRule{ID: id}) + return egw.deleteNsxvNatRule(&types.EdgeNatRule{ID: id}) } func validateCreateNsxvNatRule(natRuleConfig *types.EdgeNatRule, egw *EdgeGateway) error { diff --git a/govcd/nat_test.go b/govcd/nat_test.go index 356e8b1e2..fe94ba23b 100644 --- a/govcd/nat_test.go +++ b/govcd/nat_test.go @@ -7,11 +7,13 @@ package govcd import ( + "fmt" + "github.com/vmware/go-vcloud-director/v2/types/v56" . "gopkg.in/check.v1" ) -func (vcd *TestVCD) Test_NatRule(check *C) { +func (vcd *TestVCD) Test_NsxvSnatRule(check *C) { if vcd.config.VCD.EdgeGateway == "" { check.Skip("Skipping test because no edge gateway given") } @@ -19,28 +21,122 @@ func (vcd *TestVCD) Test_NatRule(check *C) { check.Assert(err, IsNil) check.Assert(edge.EdgeGateway.Name, Equals, vcd.config.VCD.EdgeGateway) + vnicIndex, err := edge.GetVnicIndexFromNetworkNameType(vcd.config.VCD.Network.Net1, "internal") + check.Assert(err, IsNil) + natRule := &types.EdgeNatRule{ Action: "snat", - TranslatedAddress: vcd.config.VCD.ExternalIp, // Edge gateway address + Vnic: vnicIndex, OriginalAddress: vcd.config.VCD.InternalIp, + TranslatedAddress: vcd.config.VCD.ExternalIp, Enabled: true, + LoggingEnabled: true, + Description: "my description", + } + if testVerbose { + fmt.Printf("# %s %s %s -> %s\n", natRule.Action, natRule.Protocol, natRule.OriginalAddress, + natRule.TranslatedAddress) + } + testNsxvNat(natRule, vcd, check, edge) +} +func (vcd *TestVCD) Test_NsxvDnatRule(check *C) { + if vcd.config.VCD.EdgeGateway == "" { + check.Skip("Skipping test because no edge gateway given") } + edge, err := vcd.vdc.FindEdgeGateway(vcd.config.VCD.EdgeGateway) + check.Assert(err, IsNil) + check.Assert(edge.EdgeGateway.Name, Equals, vcd.config.VCD.EdgeGateway) - createdSnatRule, err := edge.CreateNsxvNatRule(natRule) + vnicIndex, err := edge.GetVnicIndexFromNetworkNameType(vcd.config.VCD.ExternalNetwork, "uplink") check.Assert(err, IsNil) - gotSnatRule, err := edge.GetNsxvNatRuleById(createdSnatRule.ID) + natRule := &types.EdgeNatRule{ + Action: "dnat", + Vnic: vnicIndex, + Protocol: "tcp", + OriginalAddress: vcd.config.VCD.ExternalIp, + OriginalPort: "443", + TranslatedAddress: vcd.config.VCD.InternalIp, + TranslatedPort: "8443", + Enabled: true, + LoggingEnabled: true, + Description: "my description", + } + if testVerbose { + fmt.Printf("# %s %s %s:%s -> %s:%s\n", natRule.Action, natRule.Protocol, natRule.OriginalAddress, + natRule.OriginalPort, natRule.TranslatedAddress, natRule.TranslatedPort) + } + + testNsxvNat(natRule, vcd, check, edge) + + natRule = &types.EdgeNatRule{ + Action: "dnat", + Vnic: vnicIndex, + Protocol: "icmp", + IcmpType: "router-advertisement", + OriginalAddress: vcd.config.VCD.ExternalIp, + TranslatedAddress: vcd.config.VCD.InternalIp, + Enabled: true, + LoggingEnabled: true, + Description: "my description", + } + if testVerbose { + fmt.Printf("# %s %s:%s %s -> %s\n", natRule.Action, natRule.Protocol, natRule.IcmpType, + natRule.OriginalAddress, natRule.TranslatedAddress) + } + testNsxvNat(natRule, vcd, check, edge) + + natRule = &types.EdgeNatRule{ + Action: "dnat", + Vnic: vnicIndex, + Protocol: "any", + OriginalAddress: vcd.config.VCD.ExternalIp, + TranslatedAddress: vcd.config.VCD.InternalIp, + Enabled: true, + LoggingEnabled: true, + Description: "my description", + } + if testVerbose { + fmt.Printf("# %s %s %s -> %s\n", natRule.Action, natRule.Protocol, natRule.OriginalAddress, + natRule.TranslatedAddress) + } + testNsxvNat(natRule, vcd, check, edge) +} + +// testNsxvNat is a helper to test multiple configurations of NAT rules. It does the following +// 1. Creates NAT rule with provided config +// 2. Checks that it can be retrieve and verifies if IDs match +// 3. Tries to update description field and validates that nothing else except description changes +// 4. Deletes the rule +// 5. Validates that the rule was deleted +func testNsxvNat(natRule *types.EdgeNatRule, vcd *TestVCD, check *C, edge EdgeGateway) { + createdNatRule, err := edge.CreateNsxvNatRule(natRule) check.Assert(err, IsNil) - check.Assert(gotSnatRule.ID, Equals, createdSnatRule.ID) + + parentEntity := vcd.org.Org.Name + "|" + vcd.vdc.Vdc.Name + "|" + vcd.config.VCD.EdgeGateway + AddToCleanupList(createdNatRule.ID, "nsxvNatRule", parentEntity, check.TestName()) + + gotNatRule, err := edge.GetNsxvNatRuleById(createdNatRule.ID) + check.Assert(err, IsNil) + check.Assert(gotNatRule, DeepEquals, createdNatRule) + check.Assert(gotNatRule.ID, Equals, createdNatRule.ID) // Set ID and update nat rule with description - natRule.ID = gotSnatRule.ID - natRule.Description = "Description for SNAT rule" - updatedSnatRule, err := edge.UpdateNsxvNatRule(natRule) + natRule.ID = gotNatRule.ID + natRule.Description = "Description for NAT rule" + updatedNatRule, err := edge.UpdateNsxvNatRule(natRule) check.Assert(err, IsNil) - check.Assert(updatedSnatRule.Description, Equals, natRule.Description) - err = edge.DeleteNsxvNatRuleById(gotSnatRule.ID) + check.Assert(updatedNatRule.Description, Equals, natRule.Description) + + // Check if the objects are deeply equal (except updated 'Description' field) + createdNatRule.Description = natRule.Description + check.Assert(updatedNatRule, DeepEquals, createdNatRule) + + err = edge.DeleteNsxvNatRuleById(gotNatRule.ID) check.Assert(err, IsNil) + // Ensure the rule does not exist anymore + _, err = edge.GetNsxvNatRuleById(createdNatRule.ID) + check.Assert(err, Equals, ErrorEntityNotFound) } diff --git a/types/v56/types.go b/types/v56/types.go index 42fec8065..2a5b9e418 100644 --- a/types/v56/types.go +++ b/types/v56/types.go @@ -1837,14 +1837,6 @@ type EdgeNatRule struct { OriginalPort string `xml:"originalPort,omitempty"` TranslatedPort string `xml:"translatedPort,omitempty"` IcmpType string `xml:"icmpType,omitempty"` - - // Optional SNAT related fields - SnatMatchDestinationAddress string `xml:"dnatMatchDestinationAddress,omitempty"` - SnatMatchDestinationPort string `xml:"dnatMatchDestinationPort,omitempty"` - - // Optional DNAT related fields - DnatMatchSourceAddress string `xml:"dnatMatchSourceAddress,omitempty"` - DnatMatchSourcePort string `xml:"dnatMatchSourcePort,omitempty"` } // VendorTemplate is information about a vendor service template. This is optional. From d643393e25ff0c8e8cf397f2e4d3fa8f630bd476 Mon Sep 17 00:00:00 2001 From: Dainius S Date: Wed, 11 Sep 2019 10:40:30 +0300 Subject: [PATCH 14/23] Function comments --- govcd/nat.go | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/govcd/nat.go b/govcd/nat.go index 2812eb614..598778a48 100644 --- a/govcd/nat.go +++ b/govcd/nat.go @@ -26,7 +26,8 @@ type responseEdgeNatRules struct { NatRules requestEdgeNatRules `xml:"natRules"` } -// CreateNatRule +// CreateNsxvNatRule creates NAT rule using proxied NSX-V API. It is a synchronuous operation. +// It returns an object with all fields populated (including ID) func (egw *EdgeGateway) CreateNsxvNatRule(natRuleConfig *types.EdgeNatRule) (*types.EdgeNatRule, error) { if err := validateCreateNsxvNatRule(natRuleConfig, egw); err != nil { return nil, err @@ -57,7 +58,7 @@ func (egw *EdgeGateway) CreateNsxvNatRule(natRuleConfig *types.EdgeNatRule) (*ty time.Sleep(5 * time.Second) - readNatRule, err := egw.GetNsxvNatRule(&types.EdgeNatRule{ID: natRuleId}) + readNatRule, err := egw.getNsxvNatRule(&types.EdgeNatRule{ID: natRuleId}) if err != nil { return nil, fmt.Errorf("unable to retrieve NAT rule with ID (%s) after creation: %s", natRuleId, err) @@ -65,6 +66,8 @@ func (egw *EdgeGateway) CreateNsxvNatRule(natRuleConfig *types.EdgeNatRule) (*ty return readNatRule, nil } +// UpdateNsxvNatRule updates types.EdgeNatRule with all fields using proxied NSX-V API. ID is +// mandatory to perform the update. func (egw *EdgeGateway) UpdateNsxvNatRule(natRuleConfig *types.EdgeNatRule) (*types.EdgeNatRule, error) { err := validateUpdateNsxvNatRule(natRuleConfig, egw) if err != nil { @@ -91,7 +94,7 @@ func (egw *EdgeGateway) UpdateNsxvNatRule(natRuleConfig *types.EdgeNatRule) (*ty return readNatRule, nil } -func (egw *EdgeGateway) GetNsxvNatRule(natRuleConfig *types.EdgeNatRule) (*types.EdgeNatRule, error) { +func (egw *EdgeGateway) getNsxvNatRule(natRuleConfig *types.EdgeNatRule) (*types.EdgeNatRule, error) { if err := validateGetNsxvNatRule(natRuleConfig, egw); err != nil { return nil, err } @@ -119,8 +122,11 @@ func (egw *EdgeGateway) GetNsxvNatRule(natRuleConfig *types.EdgeNatRule) (*types return nil, ErrorEntityNotFound } +// GetNsxvNatRuleById retrieves types.EdgeNatRule by NAT rule ID as shown in the UI using proxied +// NSX-V API. +// It returns and error `ErrorEntityNotFound` if the NAT rule is now found. func (egw *EdgeGateway) GetNsxvNatRuleById(id string) (*types.EdgeNatRule, error) { - return egw.GetNsxvNatRule(&types.EdgeNatRule{ID: id}) + return egw.getNsxvNatRule(&types.EdgeNatRule{ID: id}) } func (egw *EdgeGateway) deleteNsxvNatRule(natRuleConfig *types.EdgeNatRule) error { @@ -149,6 +155,9 @@ func (egw *EdgeGateway) deleteNsxvNatRule(natRuleConfig *types.EdgeNatRule) erro return nil } +// DeleteNsxvNatRuleById deletes types.EdgeNatRule by NAT rule ID as shown in the UI using proxied +// NSX-V API. +// It returns and error `ErrorEntityNotFound` if the NAT rule is now found. func (egw *EdgeGateway) DeleteNsxvNatRuleById(id string) error { return egw.deleteNsxvNatRule(&types.EdgeNatRule{ID: id}) } From 5c501af92ac919d875d32bc421d84948d3fcb7d5 Mon Sep 17 00:00:00 2001 From: Dainius S Date: Wed, 11 Sep 2019 10:49:38 +0300 Subject: [PATCH 15/23] changelog added --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 762e9308e..4b9437bb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,8 @@ guest properties [#235](https://github.com/vmware/go-vcloud-director/pull/235) * Added methods `vapp.SetProductSectionList` and `vapp.GetProductSectionList` allowing to manipulate vApp guest properties [#235](https://github.com/vmware/go-vcloud-director/pull/235) * Added method GetStorageProfileByHref +* Added methods `CreateNsxvNatRule()`, `UpdateNsxvNatRule()`, `GetNsxvNatRuleById()`, `DeleteNsxvNatRuleById()` [#xx]() +* Added methods `GetVnicIndexFromNetworkNameType()` and `GetNetworkNameTypeFromVnicIndex()` [#xx]() IMPROVEMENTS: From ec9a63c0e0e6862004fb2a249469f14ebfb7dda7 Mon Sep 17 00:00:00 2001 From: Dainius S Date: Wed, 11 Sep 2019 10:50:16 +0300 Subject: [PATCH 16/23] Remove errorneus patch --- govcd/vapp_test.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/govcd/vapp_test.go b/govcd/vapp_test.go index cf95d8cfb..6b182a55a 100644 --- a/govcd/vapp_test.go +++ b/govcd/vapp_test.go @@ -9,7 +9,6 @@ package govcd import ( "fmt" "regexp" - "sort" "github.com/vmware/go-vcloud-director/v2/types/v56" . "gopkg.in/check.v1" @@ -579,17 +578,6 @@ func verifyNetworkConnectionSection(check *C, actual, desired *types.NetworkConn check.Assert(len(actual.NetworkConnection), Equals, len(desired.NetworkConnection)) check.Assert(actual.PrimaryNetworkConnectionIndex, Equals, desired.PrimaryNetworkConnectionIndex) - // sort both objects by index before comparison - sort.SliceStable(actual.NetworkConnection, func(i, j int) bool { - return actual.NetworkConnection[i].NetworkConnectionIndex < - actual.NetworkConnection[j].NetworkConnectionIndex - }) - - sort.SliceStable(desired.NetworkConnection, func(i, j int) bool { - return desired.NetworkConnection[i].NetworkConnectionIndex < - desired.NetworkConnection[j].NetworkConnectionIndex - }) - for index := range actual.NetworkConnection { actualNic := actual.NetworkConnection[index] desiredNic := desired.NetworkConnection[index] From d7482c8d367f33d19505764bfa4d429983adfaf8 Mon Sep 17 00:00:00 2001 From: Dainius S Date: Wed, 11 Sep 2019 10:58:11 +0300 Subject: [PATCH 17/23] Insert correct PR numbers and links to changelog --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b9437bb5..9b1bf3ae6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,8 +28,9 @@ guest properties [#235](https://github.com/vmware/go-vcloud-director/pull/235) * Added methods `vapp.SetProductSectionList` and `vapp.GetProductSectionList` allowing to manipulate vApp guest properties [#235](https://github.com/vmware/go-vcloud-director/pull/235) * Added method GetStorageProfileByHref -* Added methods `CreateNsxvNatRule()`, `UpdateNsxvNatRule()`, `GetNsxvNatRuleById()`, `DeleteNsxvNatRuleById()` [#xx]() -* Added methods `GetVnicIndexFromNetworkNameType()` and `GetNetworkNameTypeFromVnicIndex()` [#xx]() +* Added methods `CreateNsxvNatRule()`, `UpdateNsxvNatRule()`, `GetNsxvNatRuleById()`, `DeleteNsxvNatRuleById()` +which use the proxied NSX-V API for handling NAT rules [#241](https://github.com/vmware/go-vcloud-director/pull/241) +* Added methods `GetVnicIndexFromNetworkNameType()` and `GetNetworkNameTypeFromVnicIndex()` [#241](https://github.com/vmware/go-vcloud-director/pull/241) IMPROVEMENTS: From 7f3995d9171c2b909c9450fd89d14fc8fae367dd Mon Sep 17 00:00:00 2001 From: Dainius S Date: Wed, 11 Sep 2019 11:55:29 +0300 Subject: [PATCH 18/23] Address comment --- govcd/edgegateway.go | 20 ++++++++++---------- govcd/edgegateway_unit_test.go | 4 ++-- govcd/nat_test.go | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/govcd/edgegateway.go b/govcd/edgegateway.go index 69f0a34ee..80d8df3ec 100644 --- a/govcd/edgegateway.go +++ b/govcd/edgegateway.go @@ -1191,29 +1191,29 @@ func (egw *EdgeGateway) getVnics() (*types.EdgeGatewayVnics, error) { return vnicConfig, nil } -// GetVnicIndexFromNetworkNameType returns *int of vNic index for specified network name and network type +// GetVnicIndexByNetworkNameAndType returns *int of vNic index for specified network name and network type // networkType one of: 'internal', 'uplink', 'trunk', 'subinterface' // networkName cannot be empty -func (egw *EdgeGateway) GetVnicIndexFromNetworkNameType(networkName, networkType string) (*int, error) { +func (egw *EdgeGateway) GetVnicIndexByNetworkNameAndType(networkName, networkType string) (*int, error) { vnics, err := egw.getVnics() if err != nil { return nil, fmt.Errorf("cannot retrieve vNic configuration: %s", err) } - return getVnicIndexFromNetworkNameType(networkName, networkType, vnics) + return GetVnicIndexByNetworkNameAndType(networkName, networkType, vnics) } -// GetNetworkNameTypeFromVnicIndex returns network name and network type for given vNic index +// GetNetworkNameAndTypeByVnicIndex returns network name and network type for given vNic index // returned networkType can be one of: 'internal', 'uplink', 'trunk', 'subinterface' -func (egw *EdgeGateway) GetNetworkNameTypeFromVnicIndex(vNicIndex int) (string, string, error) { +func (egw *EdgeGateway) GetNetworkNameAndTypeByVnicIndex(vNicIndex int) (string, string, error) { vnics, err := egw.getVnics() if err != nil { return "", "", fmt.Errorf("cannot retrieve vNic configuration: %s", err) } - return getNetworkNameTypeFromVnicIndex(vNicIndex, vnics) + return GetNetworkNameAndTypeByVnicIndex(vNicIndex, vnics) } -// getVnicIndexFromNetworkNameType is wrapped and used by public function GetVnicIndexFromNetworkNameType -func getVnicIndexFromNetworkNameType(networkName, networkType string, vnics *types.EdgeGatewayVnics) (*int, error) { +// GetVnicIndexByNetworkNameAndType is wrapped and used by public function GetVnicIndexByNetworkNameAndType +func GetVnicIndexByNetworkNameAndType(networkName, networkType string, vnics *types.EdgeGatewayVnics) (*int, error) { if networkName == "" { return nil, fmt.Errorf("network name cannot be empty") } @@ -1257,8 +1257,8 @@ func getVnicIndexFromNetworkNameType(networkName, networkType string, vnics *typ return foundIndex, nil } -// getNetworkNameTypeFromVnicIndex is wrapped and used by public function GetNetworkNameTypeFromVnicIndex -func getNetworkNameTypeFromVnicIndex(vNicIndex int, vnics *types.EdgeGatewayVnics) (string, string, error) { +// GetNetworkNameAndTypeByVnicIndex is wrapped and used by public function GetNetworkNameAndTypeByVnicIndex +func GetNetworkNameAndTypeByVnicIndex(vNicIndex int, vnics *types.EdgeGatewayVnics) (string, string, error) { if vNicIndex < 0 { return "", "", fmt.Errorf("vNic index cannot be negative") } diff --git a/govcd/edgegateway_unit_test.go b/govcd/edgegateway_unit_test.go index d4bf47f96..681ec9678 100644 --- a/govcd/edgegateway_unit_test.go +++ b/govcd/edgegateway_unit_test.go @@ -273,7 +273,7 @@ func Test_getVnicIndexFromNetworkNameType(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - vnicIndex, err := getVnicIndexFromNetworkNameType(tt.networkName, tt.networkType, vnicObject) + vnicIndex, err := GetVnicIndexByNetworkNameAndType(tt.networkName, tt.networkType, vnicObject) if !tt.hasError && err != nil { t.Errorf("error was not expected: %s", err) @@ -534,7 +534,7 @@ func Test_getNetworkNameTypeFromVnicIndex(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - networkName, networkType, err := getNetworkNameTypeFromVnicIndex(tt.vnicIndex, vnicObject) + networkName, networkType, err := GetNetworkNameAndTypeByVnicIndex(tt.vnicIndex, vnicObject) if !tt.hasError && err != nil { t.Errorf("error was not expected: %s", err) diff --git a/govcd/nat_test.go b/govcd/nat_test.go index fe94ba23b..dbc39e5e8 100644 --- a/govcd/nat_test.go +++ b/govcd/nat_test.go @@ -21,7 +21,7 @@ func (vcd *TestVCD) Test_NsxvSnatRule(check *C) { check.Assert(err, IsNil) check.Assert(edge.EdgeGateway.Name, Equals, vcd.config.VCD.EdgeGateway) - vnicIndex, err := edge.GetVnicIndexFromNetworkNameType(vcd.config.VCD.Network.Net1, "internal") + vnicIndex, err := edge.GetVnicIndexByNetworkNameAndType(vcd.config.VCD.Network.Net1, "internal") check.Assert(err, IsNil) natRule := &types.EdgeNatRule{ @@ -47,7 +47,7 @@ func (vcd *TestVCD) Test_NsxvDnatRule(check *C) { check.Assert(err, IsNil) check.Assert(edge.EdgeGateway.Name, Equals, vcd.config.VCD.EdgeGateway) - vnicIndex, err := edge.GetVnicIndexFromNetworkNameType(vcd.config.VCD.ExternalNetwork, "uplink") + vnicIndex, err := edge.GetVnicIndexByNetworkNameAndType(vcd.config.VCD.ExternalNetwork, "uplink") check.Assert(err, IsNil) natRule := &types.EdgeNatRule{ From cb14b009662fcb98952b87247a56798b4b1c0ebd Mon Sep 17 00:00:00 2001 From: Dainius S Date: Wed, 11 Sep 2019 11:57:34 +0300 Subject: [PATCH 19/23] Fix changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b1bf3ae6..d0900a4fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,7 +30,7 @@ vApp guest properties [#235](https://github.com/vmware/go-vcloud-director/pull/2 * Added method GetStorageProfileByHref * Added methods `CreateNsxvNatRule()`, `UpdateNsxvNatRule()`, `GetNsxvNatRuleById()`, `DeleteNsxvNatRuleById()` which use the proxied NSX-V API for handling NAT rules [#241](https://github.com/vmware/go-vcloud-director/pull/241) -* Added methods `GetVnicIndexFromNetworkNameType()` and `GetNetworkNameTypeFromVnicIndex()` [#241](https://github.com/vmware/go-vcloud-director/pull/241) +* Added methods `GetVnicIndexByNetworkNameAndType()` and `GetNetworkNameAndTypeByVnicIndex()` [#241](https://github.com/vmware/go-vcloud-director/pull/241) IMPROVEMENTS: From 8df73c8c93a05ccd7eb376acf9fec517513fde5e Mon Sep 17 00:00:00 2001 From: Dainius S Date: Mon, 16 Sep 2019 14:03:36 +0300 Subject: [PATCH 20/23] Fix and tune test --- go.mod | 2 ++ go.sum | 1 - govcd/edgegateway_test.go | 27 ++++++++++++++++++++------- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 1c75c93e1..75b7f110b 100644 --- a/go.mod +++ b/go.mod @@ -6,3 +6,5 @@ require ( gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 gopkg.in/yaml.v2 v2.2.2 ) + +go 1.13 diff --git a/go.sum b/go.sum index af2c6a600..91cfce57f 100644 --- a/go.sum +++ b/go.sum @@ -5,7 +5,6 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/vmware/go-vcloud-director v2.0.0+incompatible h1:3B121XZVdEOxRhv5ARswKVxXt4KznAbun8GoXNbbZWs= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/govcd/edgegateway_test.go b/govcd/edgegateway_test.go index ee19a6ad4..070fd25b3 100644 --- a/govcd/edgegateway_test.go +++ b/govcd/edgegateway_test.go @@ -596,13 +596,26 @@ func (vcd *TestVCD) TestEdgeGateway_GetVnics(check *C) { vnics, err := edge.getVnics() check.Assert(err, IsNil) - check.Assert(len(vnics.Vnic) > 1, Equals, true) - check.Assert(vnics.Vnic[0].Name, Equals, vcd.config.VCD.ExternalNetwork) - check.Assert(vnics.Vnic[0].PortgroupName, Equals, vcd.config.VCD.ExternalNetwork) - check.Assert(vnics.Vnic[0].AddressGroups.AddressGroup.PrimaryAddress, Equals, vcd.config.VCD.ExternalIp) - check.Assert(vnics.Vnic[0].Type, Equals, "uplink") - check.Assert(vnics.Vnic[1].Type, Equals, "internal") - check.Assert(vnics.Vnic[1].PortgroupName, Equals, vcd.config.VCD.Network.Net1) + foundExtNet := false + foundOrgNet := false + + check.Assert(len(vnics.Vnic) > 1, Equals, true) + // Look for both - external and Org networks in returned edge gateway vNics + for _, vnic := range vnics.Vnic { + // Look for external network attached to Edge gateway + if vnic.PortgroupName == vcd.config.VCD.ExternalNetwork { + check.Assert(vnic.AddressGroups.AddressGroup.PrimaryAddress, Equals, vcd.config.VCD.ExternalIp) + check.Assert(vnic.Type, Equals, "uplink") + foundExtNet = true + } + // Look for org network 1 attached + if vnic.PortgroupName == vcd.config.VCD.Network.Net1 { + check.Assert(vnic.Type, Equals, "internal") + foundOrgNet = true + } + } + check.Assert(foundExtNet, Equals, true) + check.Assert(foundOrgNet, Equals, true) } From 4dd675ae89d3e9309865982a4c0cddfc379d83a3 Mon Sep 17 00:00:00 2001 From: Dainius S Date: Tue, 17 Sep 2019 10:06:36 +0300 Subject: [PATCH 21/23] Address comments --- govcd/nat.go | 43 +++++++++++------------------ govcd/nat_test.go | 2 ++ types/v56/types.go | 69 +++++++++++++++++++++++----------------------- 3 files changed, 52 insertions(+), 62 deletions(-) diff --git a/govcd/nat.go b/govcd/nat.go index 598778a48..669a5d757 100644 --- a/govcd/nat.go +++ b/govcd/nat.go @@ -8,7 +8,6 @@ import ( "encoding/xml" "fmt" "net/http" - "time" "github.com/vmware/go-vcloud-director/v2/types/v56" ) @@ -56,9 +55,7 @@ func (egw *EdgeGateway) CreateNsxvNatRule(natRuleConfig *types.EdgeNatRule) (*ty return nil, err } - time.Sleep(5 * time.Second) - - readNatRule, err := egw.getNsxvNatRule(&types.EdgeNatRule{ID: natRuleId}) + readNatRule, err := egw.GetNsxvNatRuleById(natRuleId) if err != nil { return nil, fmt.Errorf("unable to retrieve NAT rule with ID (%s) after creation: %s", natRuleId, err) @@ -94,8 +91,11 @@ func (egw *EdgeGateway) UpdateNsxvNatRule(natRuleConfig *types.EdgeNatRule) (*ty return readNatRule, nil } -func (egw *EdgeGateway) getNsxvNatRule(natRuleConfig *types.EdgeNatRule) (*types.EdgeNatRule, error) { - if err := validateGetNsxvNatRule(natRuleConfig, egw); err != nil { +// GetNsxvNatRuleById retrieves types.EdgeNatRule by NAT rule ID as shown in the UI using proxied +// NSX-V API. +// It returns and error `ErrorEntityNotFound` if the NAT rule is now found. +func (egw *EdgeGateway) GetNsxvNatRuleById(id string) (*types.EdgeNatRule, error) { + if err := validateGetNsxvNatRule(id, egw); err != nil { return nil, err } @@ -114,7 +114,7 @@ func (egw *EdgeGateway) getNsxvNatRule(natRuleConfig *types.EdgeNatRule) (*types } for _, rule := range natRuleResponse.NatRules.EdgeNatRules { - if rule.ID != "" && rule.ID == natRuleConfig.ID { + if rule.ID != "" && rule.ID == id { return rule, nil } } @@ -122,26 +122,22 @@ func (egw *EdgeGateway) getNsxvNatRule(natRuleConfig *types.EdgeNatRule) (*types return nil, ErrorEntityNotFound } -// GetNsxvNatRuleById retrieves types.EdgeNatRule by NAT rule ID as shown in the UI using proxied +// DeleteNsxvNatRuleById deletes types.EdgeNatRule by NAT rule ID as shown in the UI using proxied // NSX-V API. // It returns and error `ErrorEntityNotFound` if the NAT rule is now found. -func (egw *EdgeGateway) GetNsxvNatRuleById(id string) (*types.EdgeNatRule, error) { - return egw.getNsxvNatRule(&types.EdgeNatRule{ID: id}) -} - -func (egw *EdgeGateway) deleteNsxvNatRule(natRuleConfig *types.EdgeNatRule) error { - err := validateDeleteNsxvNatRule(natRuleConfig, egw) +func (egw *EdgeGateway) DeleteNsxvNatRuleById(id string) error { + err := validateDeleteNsxvNatRule(id, egw) if err != nil { return err } - httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeCreateNatPath + "/" + natRuleConfig.ID) + httpPath, err := egw.buildProxiedEdgeEndpointURL(types.EdgeCreateNatPath + "/" + id) if err != nil { return fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) } // check if the rule exists and pass back the error at it may be 'ErrorEntityNotFound' - _, err = egw.GetNsxvNatRuleById(natRuleConfig.ID) + _, err = egw.GetNsxvNatRuleById(id) if err != nil { return err } @@ -155,13 +151,6 @@ func (egw *EdgeGateway) deleteNsxvNatRule(natRuleConfig *types.EdgeNatRule) erro return nil } -// DeleteNsxvNatRuleById deletes types.EdgeNatRule by NAT rule ID as shown in the UI using proxied -// NSX-V API. -// It returns and error `ErrorEntityNotFound` if the NAT rule is now found. -func (egw *EdgeGateway) DeleteNsxvNatRuleById(id string) error { - return egw.deleteNsxvNatRule(&types.EdgeNatRule{ID: id}) -} - func validateCreateNsxvNatRule(natRuleConfig *types.EdgeNatRule, egw *EdgeGateway) error { if !egw.HasAdvancedNetworking() { return fmt.Errorf("only advanced edge gateways support NAT rules") @@ -186,18 +175,18 @@ func validateUpdateNsxvNatRule(natRuleConfig *types.EdgeNatRule, egw *EdgeGatewa return validateCreateNsxvNatRule(natRuleConfig, egw) } -func validateGetNsxvNatRule(natRuleConfig *types.EdgeNatRule, egw *EdgeGateway) error { +func validateGetNsxvNatRule(id string, egw *EdgeGateway) error { if !egw.HasAdvancedNetworking() { return fmt.Errorf("only advanced edge gateways support NAT rules") } - if natRuleConfig.ID == "" { + if id == "" { return fmt.Errorf("unable to retrieve NAT rule without ID") } return nil } -func validateDeleteNsxvNatRule(natRuleConfig *types.EdgeNatRule, egw *EdgeGateway) error { - return validateGetNsxvNatRule(natRuleConfig, egw) +func validateDeleteNsxvNatRule(id string, egw *EdgeGateway) error { + return validateGetNsxvNatRule(id, egw) } diff --git a/govcd/nat_test.go b/govcd/nat_test.go index dbc39e5e8..516677cf9 100644 --- a/govcd/nat_test.go +++ b/govcd/nat_test.go @@ -118,6 +118,7 @@ func testNsxvNat(natRule *types.EdgeNatRule, vcd *TestVCD, check *C, edge EdgeGa gotNatRule, err := edge.GetNsxvNatRuleById(createdNatRule.ID) check.Assert(err, IsNil) + check.Assert(gotNatRule, NotNil) check.Assert(gotNatRule, DeepEquals, createdNatRule) check.Assert(gotNatRule.ID, Equals, createdNatRule.ID) @@ -126,6 +127,7 @@ func testNsxvNat(natRule *types.EdgeNatRule, vcd *TestVCD, check *C, edge EdgeGa natRule.Description = "Description for NAT rule" updatedNatRule, err := edge.UpdateNsxvNatRule(natRule) check.Assert(err, IsNil) + check.Assert(updatedNatRule, NotNil) check.Assert(updatedNatRule.Description, Equals, natRule.Description) diff --git a/types/v56/types.go b/types/v56/types.go index 6875b0c49..175f9b1e9 100644 --- a/types/v56/types.go +++ b/types/v56/types.go @@ -1826,10 +1826,10 @@ type EdgeNatRule struct { ID string `xml:"ruleId,omitempty"` RuleType string `xml:"ruleType,omitempty"` RuleTag string `xml:"ruleTag,omitempty"` - Action string `xml:"action,omitempty"` + Action string `xml:"action"` Vnic *int `xml:"vnic,omitempty"` - OriginalAddress string `xml:"originalAddress,omitempty"` - TranslatedAddress string `xml:"translatedAddress,omitempty"` + OriginalAddress string `xml:"originalAddress"` + TranslatedAddress string `xml:"translatedAddress"` LoggingEnabled bool `xml:"loggingEnabled"` Enabled bool `xml:"enabled"` Description string `xml:"description,omitempty"` @@ -2702,42 +2702,41 @@ type EdgeGatewayVnics struct { Name string `xml:"name"` AddressGroups struct { AddressGroup struct { - PrimaryAddress string `xml:"primaryAddress"` + PrimaryAddress string `xml:"primaryAddress,omitempty"` SecondaryAddresses struct { - IpAddress []string `xml:"ipAddress"` - } `xml:"secondaryAddresses"` - SubnetMask string `xml:"subnetMask"` - SubnetPrefixLength string `xml:"subnetPrefixLength"` - } `xml:"addressGroup"` - } `xml:"addressGroups"` - Mtu string `xml:"mtu"` - Type string `xml:"type"` - IsConnected string `xml:"isConnected"` + IpAddress []string `xml:"ipAddress,omitempty"` + } `xml:"secondaryAddresses,omitempty"` + SubnetMask string `xml:"subnetMask,omitempty"` + SubnetPrefixLength string `xml:"subnetPrefixLength,omitempty"` + } `xml:"addressGroup,omitempty"` + } `xml:"addressGroups,omitempty"` + Mtu string `xml:"mtu,omitempty"` + Type string `xml:"type,omitempty"` + IsConnected string `xml:"isConnected,omitempty"` Index *int `xml:"index"` - PortgroupId string `xml:"portgroupId"` - PortgroupName string `xml:"portgroupName"` - EnableProxyArp string `xml:"enableProxyArp"` - EnableSendRedirects string `xml:"enableSendRedirects"` + PortgroupId string `xml:"portgroupId,omitempty"` + PortgroupName string `xml:"portgroupName,omitempty"` + EnableProxyArp string `xml:"enableProxyArp,omitempty"` + EnableSendRedirects string `xml:"enableSendRedirects,omitempty"` SubInterfaces struct { SubInterface []struct { - IsConnected string `xml:"isConnected"` - Label string `xml:"label"` - Name string `xml:"name"` - Index *int `xml:"index"` - TunnelId string `xml:"tunnelId"` - LogicalSwitchId string `xml:"logicalSwitchId"` - LogicalSwitchName string `xml:"logicalSwitchName"` - EnableSendRedirects string `xml:"enableSendRedirects"` - Mtu string `xml:"mtu"` + IsConnected string `xml:"isConnected,omitempty"` + Label string `xml:"label,omitempty"` + Name string `xml:"name,omitempty"` + Index *int `xml:"index,omitempty"` + TunnelId string `xml:"tunnelId,omitempty"` + LogicalSwitchId string `xml:"logicalSwitchId,omitempty"` + LogicalSwitchName string `xml:"logicalSwitchName,omitempty"` + EnableSendRedirects string `xml:"enableSendRedirects,omitempty"` + Mtu string `xml:"mtu,omitempty"` AddressGroups struct { AddressGroup struct { - Text string `xml:",chardata"` - PrimaryAddress string `xml:"primaryAddress"` - SubnetMask string `xml:"subnetMask"` - SubnetPrefixLength string `xml:"subnetPrefixLength"` - } `xml:"addressGroup"` - } `xml:"addressGroups"` - } `xml:"subInterface"` - } `xml:"subInterfaces"` - } `xml:"vnic"` + PrimaryAddress string `xml:"primaryAddress,omitempty"` + SubnetMask string `xml:"subnetMask,omitempty"` + SubnetPrefixLength string `xml:"subnetPrefixLength,omitempty"` + } `xml:"addressGroup,omitempty"` + } `xml:"addressGroups,omitempty"` + } `xml:"subInterface,omitempty"` + } `xml:"subInterfaces,omitempty"` + } `xml:"vnic,omitempty"` } From ab2012ad7a9ab42bd5a5567d4366d981e5c29954 Mon Sep 17 00:00:00 2001 From: Dainius S Date: Tue, 17 Sep 2019 11:10:05 +0300 Subject: [PATCH 22/23] Add fix for LB test --- govcd/lb_test.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/govcd/lb_test.go b/govcd/lb_test.go index b2d5d22d9..74592930d 100644 --- a/govcd/lb_test.go +++ b/govcd/lb_test.go @@ -69,8 +69,11 @@ func (vcd *TestVCD) Test_LB(check *C) { // Wait until vApp becomes configurable initialVappStatus, err := vapp.GetStatus() check.Assert(err, IsNil) - err = vapp.BlockWhileStatus(initialVappStatus, vapp.client.MaxRetryTimeout) - check.Assert(err, IsNil) + if initialVappStatus != "RESOLVED" { // RESOLVED vApp is ready to accept operations + err = vapp.BlockWhileStatus(initialVappStatus, vapp.client.MaxRetryTimeout) + check.Assert(err, IsNil) + } + fmt.Printf(". Done\n") fmt.Printf("# Attaching vDC network '%s' to vApp '%s'", vcd.config.VCD.Network.Net1, TestLb) From 3cf53075c652440723e3bd5be3a034794379b240 Mon Sep 17 00:00:00 2001 From: Dainius S Date: Tue, 17 Sep 2019 11:32:55 +0300 Subject: [PATCH 23/23] Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d0900a4fc..6835afda6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,7 +29,7 @@ guest properties [#235](https://github.com/vmware/go-vcloud-director/pull/235) vApp guest properties [#235](https://github.com/vmware/go-vcloud-director/pull/235) * Added method GetStorageProfileByHref * Added methods `CreateNsxvNatRule()`, `UpdateNsxvNatRule()`, `GetNsxvNatRuleById()`, `DeleteNsxvNatRuleById()` -which use the proxied NSX-V API for handling NAT rules [#241](https://github.com/vmware/go-vcloud-director/pull/241) +which use the proxied NSX-V API of advanced edge gateway for handling NAT rules [#241](https://github.com/vmware/go-vcloud-director/pull/241) * Added methods `GetVnicIndexByNetworkNameAndType()` and `GetNetworkNameAndTypeByVnicIndex()` [#241](https://github.com/vmware/go-vcloud-director/pull/241) IMPROVEMENTS: