diff --git a/.changes/v2.15.0/440-improvements.md b/.changes/v2.15.0/440-improvements.md new file mode 100644 index 000000000..7f53e7b74 --- /dev/null +++ b/.changes/v2.15.0/440-improvements.md @@ -0,0 +1,6 @@ +* NSX-T Edge Gateway now supports VDC Groups by switching from `OrgVdc` to `OwnerRef` field. + Additional methods `NsxtEdgeGateway.MoveToVdcOrVdcGroup()`, + `Org.GetNsxtEdgeGatewayByNameAndOwnerId()`, `VdcGroup.GetNsxtEdgeGatewayByName()`, + `VdcGroup.GetAllNsxtEdgeGateways()`, `org.GetVdcGroupById` [GH-440] +* Additional helper functions `OwnerIsVdcGroup()`, `OwnerIsVdc()`, `VdcGroup.GetCapabilities()`, + `VdcGroup.IsNsxt()` [GH-440] \ No newline at end of file diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index fc1fb6ec4..d41c226f8 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,3 +1,3 @@ # These owners will be the default owners for everything in the repo. Unless a later match takes # precedence all these users will be requested for review when someone opens a pull request. -* @lvirbalas @dataclouder @Didainius @vbauzysvmware +* @lvirbalas @dataclouder @Didainius @vbauzysvmware @mikeletux @adambarreiro diff --git a/govcd/api_vcd.go b/govcd/api_vcd.go index 971638747..0c0ffb1d9 100644 --- a/govcd/api_vcd.go +++ b/govcd/api_vcd.go @@ -7,13 +7,14 @@ package govcd import ( "crypto/tls" "fmt" - semver "github.com/hashicorp/go-version" "net/http" "net/url" "os" "strings" "time" + semver "github.com/hashicorp/go-version" + "github.com/vmware/go-vcloud-director/v2/types/v56" "github.com/vmware/go-vcloud-director/v2/util" ) diff --git a/govcd/graph_plan.png b/govcd/graph_plan.png deleted file mode 100644 index e69de29bb..000000000 diff --git a/govcd/nsxt_edgegateway.go b/govcd/nsxt_edgegateway.go index 000c5f2d6..1dd7d8616 100644 --- a/govcd/nsxt_edgegateway.go +++ b/govcd/nsxt_edgegateway.go @@ -17,26 +17,26 @@ type NsxtEdgeGateway struct { client *Client } -// GetNsxtEdgeGatewayById allows to retrieve NSX-T edge gateway by ID for Org admins +// GetNsxtEdgeGatewayById allows retrieving NSX-T edge gateway by ID for Org admins func (adminOrg *AdminOrg) GetNsxtEdgeGatewayById(id string) (*NsxtEdgeGateway, error) { return getNsxtEdgeGatewayById(adminOrg.client, id, nil) } -// GetNsxtEdgeGatewayById allows to retrieve NSX-T edge gateway by ID for Org users +// GetNsxtEdgeGatewayById allows retrieving NSX-T edge gateway by ID for Org users func (org *Org) GetNsxtEdgeGatewayById(id string) (*NsxtEdgeGateway, error) { return getNsxtEdgeGatewayById(org.client, id, nil) } -// GetNsxtEdgeGatewayById allows to retrieve NSX-T edge gateway by ID for specific VDC +// GetNsxtEdgeGatewayById allows retrieving NSX-T edge gateway by ID for specific VDC func (vdc *Vdc) GetNsxtEdgeGatewayById(id string) (*NsxtEdgeGateway, error) { params := url.Values{} - filterParams := queryParameterFilterAnd("orgVdc.id=="+vdc.Vdc.ID, params) + filterParams := queryParameterFilterAnd("ownerRef.id=="+vdc.Vdc.ID, params) egw, err := getNsxtEdgeGatewayById(vdc.client, id, filterParams) if err != nil { return nil, err } - if egw.EdgeGateway.OrgVdc.ID != vdc.Vdc.ID { + if egw.EdgeGateway.OwnerRef.ID != vdc.Vdc.ID { return nil, fmt.Errorf("%s: no NSX-T Edge Gateway with ID '%s' found in VDC '%s'", ErrorEntityNotFound, id, vdc.Vdc.ID) } @@ -44,7 +44,7 @@ func (vdc *Vdc) GetNsxtEdgeGatewayById(id string) (*NsxtEdgeGateway, error) { return egw, nil } -// GetNsxtEdgeGatewayByName allows to retrieve NSX-T edge gateway by Name for Org admins +// GetNsxtEdgeGatewayByName allows retrieving NSX-T edge gateway by Name for Org admins func (adminOrg *AdminOrg) GetNsxtEdgeGatewayByName(name string) (*NsxtEdgeGateway, error) { queryParameters := url.Values{} queryParameters.Add("filter", "name=="+name) @@ -59,7 +59,7 @@ func (adminOrg *AdminOrg) GetNsxtEdgeGatewayByName(name string) (*NsxtEdgeGatewa return returnSingleNsxtEdgeGateway(name, onlyNsxtEdges) } -// GetNsxtEdgeGatewayByName allows to retrieve NSX-T edge gateway by Name for Org admins +// GetNsxtEdgeGatewayByName allows retrieving NSX-T edge gateway by Name for Org admins func (org *Org) GetNsxtEdgeGatewayByName(name string) (*NsxtEdgeGateway, error) { queryParameters := url.Values{} queryParameters.Add("filter", "name=="+name) @@ -74,6 +74,26 @@ func (org *Org) GetNsxtEdgeGatewayByName(name string) (*NsxtEdgeGateway, error) return returnSingleNsxtEdgeGateway(name, onlyNsxtEdges) } +// GetNsxtEdgeGatewayByNameAndOwnerId looks up NSX-T Edge Gateway by name and its owner ID (owner +// can be VDC or VDC Group). +func (org *Org) GetNsxtEdgeGatewayByNameAndOwnerId(edgeGatewayName, ownerId string) (*NsxtEdgeGateway, error) { + if edgeGatewayName == "" || ownerId == "" { + return nil, fmt.Errorf("'edgeGatewayName' and 'ownerId' must both be specified") + } + + queryParameters := url.Values{} + queryParameters.Add("filter", fmt.Sprintf("ownerRef.id==%s;name==%s", ownerId, edgeGatewayName)) + + allEdges, err := org.GetAllNsxtEdgeGateways(queryParameters) + if err != nil { + return nil, fmt.Errorf("unable to retrieve Edge Gateway by name '%s': %s", edgeGatewayName, err) + } + + onlyNsxtEdges := filterOnlyNsxtEdges(allEdges) + + return returnSingleNsxtEdgeGateway(edgeGatewayName, onlyNsxtEdges) +} + // GetNsxtEdgeGatewayByName allows to retrieve NSX-T edge gateway by Name for specific VDC func (vdc *Vdc) GetNsxtEdgeGatewayByName(name string) (*NsxtEdgeGateway, error) { queryParameters := url.Values{} @@ -87,6 +107,23 @@ func (vdc *Vdc) GetNsxtEdgeGatewayByName(name string) (*NsxtEdgeGateway, error) return returnSingleNsxtEdgeGateway(name, allEdges) } +// GetNsxtEdgeGatewayByName allows to retrieve NSX-T edge gateway by Name for specific VDC Group +func (vdcGroup *VdcGroup) GetNsxtEdgeGatewayByName(name string) (*NsxtEdgeGateway, error) { + if name == "" { + return nil, fmt.Errorf("'name' must be specified") + } + + queryParameters := url.Values{} + queryParameters.Add("filter", "name=="+name) + + allEdges, err := vdcGroup.GetAllNsxtEdgeGateways(queryParameters) + if err != nil { + return nil, fmt.Errorf("unable to retrieve Edge Gateway by name '%s': %s", name, err) + } + + return returnSingleNsxtEdgeGateway(name, allEdges) +} + // GetAllNsxtEdgeGateways allows to retrieve all NSX-T edge gateways for Org Admins func (adminOrg *AdminOrg) GetAllNsxtEdgeGateways(queryParameters url.Values) ([]*NsxtEdgeGateway, error) { return getAllNsxtEdgeGateways(adminOrg.client, queryParameters) @@ -99,10 +136,16 @@ func (org *Org) GetAllNsxtEdgeGateways(queryParameters url.Values) ([]*NsxtEdgeG // GetAllNsxtEdgeGateways allows to retrieve all NSX-T edge gateways for specific VDC func (vdc *Vdc) GetAllNsxtEdgeGateways(queryParameters url.Values) ([]*NsxtEdgeGateway, error) { - filteredQueryParams := queryParameterFilterAnd("orgVdc.id=="+vdc.Vdc.ID, queryParameters) + filteredQueryParams := queryParameterFilterAnd("ownerRef.id=="+vdc.Vdc.ID, queryParameters) return getAllNsxtEdgeGateways(vdc.client, filteredQueryParams) } +// GetAllNsxtEdgeGateways allows to retrieve all NSX-T edge gateways for specific VDC +func (vdcGroup *VdcGroup) GetAllNsxtEdgeGateways(queryParameters url.Values) ([]*NsxtEdgeGateway, error) { + filteredQueryParams := queryParameterFilterAnd("ownerRef.id=="+vdcGroup.VdcGroup.Id, queryParameters) + return getAllNsxtEdgeGateways(vdcGroup.client, filteredQueryParams) +} + // CreateNsxtEdgeGateway allows to create NSX-T edge gateway for Org admins func (adminOrg *AdminOrg) CreateNsxtEdgeGateway(edgeGatewayConfig *types.OpenAPIEdgeGateway) (*NsxtEdgeGateway, error) { if !adminOrg.client.IsSysAdmin { @@ -110,7 +153,7 @@ func (adminOrg *AdminOrg) CreateNsxtEdgeGateway(edgeGatewayConfig *types.OpenAPI } endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGateways - minimumApiVersion, err := adminOrg.client.checkOpenApiEndpointCompatibility(endpoint) + minimumApiVersion, err := adminOrg.client.getOpenApiHighestElevatedVersion(endpoint) if err != nil { return nil, err } @@ -133,14 +176,14 @@ func (adminOrg *AdminOrg) CreateNsxtEdgeGateway(edgeGatewayConfig *types.OpenAPI return returnEgw, nil } -// Update allows to update NSX-T edge gateway for Org admins +// Update allows updating NSX-T edge gateway for Org admins func (egw *NsxtEdgeGateway) Update(edgeGatewayConfig *types.OpenAPIEdgeGateway) (*NsxtEdgeGateway, error) { if !egw.client.IsSysAdmin { return nil, fmt.Errorf("only System Administrator can update Edge Gateway") } endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGateways - minimumApiVersion, err := egw.client.checkOpenApiEndpointCompatibility(endpoint) + minimumApiVersion, err := egw.client.getOpenApiHighestElevatedVersion(endpoint) if err != nil { return nil, err } @@ -167,14 +210,14 @@ func (egw *NsxtEdgeGateway) Update(edgeGatewayConfig *types.OpenAPIEdgeGateway) return returnEgw, nil } -// Delete allows to delete NSX-T edge gateway for sysadmins +// Delete allows deleting NSX-T edge gateway for sysadmins func (egw *NsxtEdgeGateway) Delete() error { if !egw.client.IsSysAdmin { return fmt.Errorf("only Provider can delete Edge Gateway") } endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGateways - minimumApiVersion, err := egw.client.checkOpenApiEndpointCompatibility(endpoint) + minimumApiVersion, err := egw.client.getOpenApiHighestElevatedVersion(endpoint) if err != nil { return err } @@ -197,13 +240,29 @@ func (egw *NsxtEdgeGateway) Delete() error { return nil } +// MoveToVdcOrVdcGroup moves NSX-T Edge Gateway to another VDC. This can cover such scenarios: +// * Move from VDC to VDC Group +// * Move from VDC Group to VDC (which is part of that VDC Group) +// +// This function is just an Update operation with OwnerRef changed to vdcGroupId, but it is more +// convenient to use it. +// Note. NSX-T Edge Gateway cannot be moved directly from one VDC to another +func (egw *NsxtEdgeGateway) MoveToVdcOrVdcGroup(vdcOrVdcGroupId string) (*NsxtEdgeGateway, error) { + edgeGatewayConfig := egw.EdgeGateway + edgeGatewayConfig.OwnerRef = &types.OpenApiReference{ID: vdcOrVdcGroupId} + // Explicitly unset VDC field because using it fails + edgeGatewayConfig.OrgVdc = nil + + return egw.Update(edgeGatewayConfig) +} + // getNsxtEdgeGatewayById is a private parent for wrapped functions: // func (adminOrg *AdminOrg) GetNsxtEdgeGatewayByName(id string) (*NsxtEdgeGateway, error) // func (org *Org) GetNsxtEdgeGatewayByName(id string) (*NsxtEdgeGateway, error) // func (vdc *Vdc) GetNsxtEdgeGatewayById(id string) (*NsxtEdgeGateway, error) func getNsxtEdgeGatewayById(client *Client, id string, queryParameters url.Values) (*NsxtEdgeGateway, error) { endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGateways - minimumApiVersion, err := client.checkOpenApiEndpointCompatibility(endpoint) + minimumApiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) if err != nil { return nil, err } @@ -255,7 +314,7 @@ func returnSingleNsxtEdgeGateway(name string, allEdges []*NsxtEdgeGateway) (*Nsx // func (vdc *Vdc) GetAllNsxtEdgeGateways(queryParameters url.Values) ([]*NsxtEdgeGateway, error) func getAllNsxtEdgeGateways(client *Client, queryParameters url.Values) ([]*NsxtEdgeGateway, error) { endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGateways - minimumApiVersion, err := client.checkOpenApiEndpointCompatibility(endpoint) + minimumApiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) if err != nil { return nil, err } diff --git a/govcd/nsxt_edgegateway_test.go b/govcd/nsxt_edgegateway_test.go index 06e95aab0..aaee24a17 100644 --- a/govcd/nsxt_edgegateway_test.go +++ b/govcd/nsxt_edgegateway_test.go @@ -17,20 +17,25 @@ func (vcd *TestVCD) Test_NsxtEdgeCreate(check *C) { adminOrg, err := vcd.client.GetAdminOrgByName(vcd.config.VCD.Org) check.Assert(err, IsNil) + check.Assert(adminOrg, NotNil) org, err := vcd.client.GetOrgByName(vcd.config.VCD.Org) check.Assert(err, IsNil) + check.Assert(org, NotNil) nsxvVdc, err := adminOrg.GetVDCByName(vcd.config.VCD.Vdc, false) check.Assert(err, IsNil) + check.Assert(nsxvVdc, NotNil) nsxtVdc, err := adminOrg.GetVDCByName(vcd.config.VCD.Nsxt.Vdc, false) if ContainsNotFound(err) { check.Skip(fmt.Sprintf("No NSX-T VDC (%s) found - skipping test", vcd.config.VCD.Nsxt.Vdc)) } check.Assert(err, IsNil) + check.Assert(nsxtVdc, NotNil) nsxtExternalNetwork, err := GetExternalNetworkV2ByName(vcd.client, vcd.config.VCD.Nsxt.ExternalNetwork) check.Assert(err, IsNil) + check.Assert(nsxtExternalNetwork, NotNil) egwDefinition := &types.OpenAPIEdgeGateway{ Name: "nsx-t-edge", @@ -71,16 +76,22 @@ func (vcd *TestVCD) Test_NsxtEdgeCreate(check *C) { // Lookup using different available methods e1, err := adminOrg.GetNsxtEdgeGatewayByName(updatedEdge.EdgeGateway.Name) check.Assert(err, IsNil) + check.Assert(e1, NotNil) e2, err := org.GetNsxtEdgeGatewayByName(updatedEdge.EdgeGateway.Name) check.Assert(err, IsNil) + check.Assert(e2, NotNil) e3, err := nsxtVdc.GetNsxtEdgeGatewayByName(updatedEdge.EdgeGateway.Name) check.Assert(err, IsNil) + check.Assert(e3, NotNil) e4, err := adminOrg.GetNsxtEdgeGatewayById(updatedEdge.EdgeGateway.ID) check.Assert(err, IsNil) + check.Assert(e4, NotNil) e5, err := org.GetNsxtEdgeGatewayById(updatedEdge.EdgeGateway.ID) check.Assert(err, IsNil) + check.Assert(e5, NotNil) e6, err := nsxtVdc.GetNsxtEdgeGatewayById(updatedEdge.EdgeGateway.ID) check.Assert(err, IsNil) + check.Assert(e6, NotNil) // Try to search for NSX-T edge gateway in NSX-V VDC and expect it to be not found expectNil, err := nsxvVdc.GetNsxtEdgeGatewayByName(updatedEdge.EdgeGateway.Name) @@ -100,3 +111,91 @@ func (vcd *TestVCD) Test_NsxtEdgeCreate(check *C) { err = updatedEdge.Delete() check.Assert(err, IsNil) } + +func (vcd *TestVCD) Test_NsxtEdgeVdcGroup(check *C) { + skipNoNsxtConfiguration(vcd, check) + skipOpenApiEndpointTest(vcd, check, types.OpenApiPathVersion1_0_0+types.OpenApiEndpointEdgeGateways) + + adminOrg, err := vcd.client.GetAdminOrgByName(vcd.config.VCD.Org) + check.Assert(err, IsNil) + check.Assert(adminOrg, NotNil) + + org, err := vcd.client.GetOrgByName(vcd.config.VCD.Org) + check.Assert(err, IsNil) + check.Assert(org, NotNil) + + nsxtExternalNetwork, err := GetExternalNetworkV2ByName(vcd.client, vcd.config.VCD.Nsxt.ExternalNetwork) + check.Assert(err, IsNil) + check.Assert(nsxtExternalNetwork, NotNil) + + vdc, vdcGroup := test_CreateVdcGroup(check, adminOrg, vcd) + + egwDefinition := &types.OpenAPIEdgeGateway{ + Name: "nsx-t-edge", + Description: "nsx-t-edge-description", + OwnerRef: &types.OpenApiReference{ + ID: vdc.Vdc.ID, + }, + EdgeGatewayUplinks: []types.EdgeGatewayUplinks{{ + UplinkID: nsxtExternalNetwork.ExternalNetwork.ID, + Subnets: types.OpenAPIEdgeGatewaySubnets{Values: []types.OpenAPIEdgeGatewaySubnetValue{{ + Gateway: "1.1.1.1", + PrefixLength: 24, + Enabled: true, + }}}, + Connected: true, + Dedicated: false, + }}, + } + + // Create Edge Gateway in VDC + createdEdge, err := adminOrg.CreateNsxtEdgeGateway(egwDefinition) + check.Assert(err, IsNil) + check.Assert(createdEdge.EdgeGateway.OwnerRef.ID, Matches, `^urn:vcloud:vdc:.*`) + openApiEndpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEdgeGateways + createdEdge.EdgeGateway.ID + PrependToCleanupListOpenApi(createdEdge.EdgeGateway.Name, check.TestName(), openApiEndpoint) + + check.Assert(createdEdge.EdgeGateway.Name, Equals, egwDefinition.Name) + check.Assert(createdEdge.EdgeGateway.OwnerRef.ID, Equals, egwDefinition.OwnerRef.ID) + + // Move Edge Gateway to VDC Group + movedGateway, err := createdEdge.MoveToVdcOrVdcGroup(vdcGroup.VdcGroup.Id) + check.Assert(err, IsNil) + check.Assert(movedGateway.EdgeGateway.OwnerRef.ID, Equals, vdcGroup.VdcGroup.Id) + check.Assert(movedGateway.EdgeGateway.OwnerRef.ID, Matches, `^urn:vcloud:vdcGroup:.*`) + + // Get by name and owner ID + edgeByNameAndOwnerId, err := org.GetNsxtEdgeGatewayByNameAndOwnerId(createdEdge.EdgeGateway.Name, vdcGroup.VdcGroup.Id) + check.Assert(err, IsNil) + + // Check lookup of Edge Gateways in VDC Groups + edgeInVdcGroup, err := vdcGroup.GetNsxtEdgeGatewayByName(createdEdge.EdgeGateway.Name) + check.Assert(err, IsNil) + + // Ensure both methods for retrieval get the same object + check.Assert(edgeByNameAndOwnerId.EdgeGateway, DeepEquals, edgeInVdcGroup.EdgeGateway) + + // Masking known variables that have change for deep check + edgeInVdcGroup.EdgeGateway.OwnerRef.Name = "" + check.Assert(edgeInVdcGroup.EdgeGateway, DeepEquals, createdEdge.EdgeGateway) + + // Move Edge Gateway back to VDC from VDC Group + egwDefinition.OwnerRef.ID = vdc.Vdc.ID + egwDefinition.ID = movedGateway.EdgeGateway.ID + + movedBackToVdcEdge, err := movedGateway.Update(egwDefinition) + check.Assert(err, IsNil) + check.Assert(movedBackToVdcEdge.EdgeGateway.OwnerRef.ID, Matches, `^urn:vcloud:vdc:.*`) + + // Ignore known to differ fields, but check that whole Edge Gateway structure remains the same + // as it is important to perform update operations without impacting configuration itself + + // Fields to ignore on both sides + createdEdge.EdgeGateway.OrgVdc = movedBackToVdcEdge.EdgeGateway.OrgVdc + createdEdge.EdgeGateway.OwnerRef = movedBackToVdcEdge.EdgeGateway.OwnerRef + check.Assert(movedBackToVdcEdge.EdgeGateway, DeepEquals, createdEdge.EdgeGateway) + + // Remove Edge Gateway + err = movedBackToVdcEdge.Delete() + check.Assert(err, IsNil) +} diff --git a/govcd/openapi_endpoints.go b/govcd/openapi_endpoints.go index 31dfadd2b..1f61ab4a2 100644 --- a/govcd/openapi_endpoints.go +++ b/govcd/openapi_endpoints.go @@ -110,11 +110,11 @@ func (client *Client) checkOpenApiEndpointCompatibility(endpoint string) (string return minimumApiVersion, nil } -// getOpenApiHighestElevatedVersion returns highest supported API version for particular endpoint +// getOpenApiHighestElevatedVersion returns the highest supported API version for particular endpoint // These API versions must be defined in endpointElevatedApiVersions. If none are there - it will return minimum // supported API versions just like client.checkOpenApiEndpointCompatibility(). // -// The advantage of this functions is that it provides a controlled API elevation instead of just picking the highest +// The advantage of this function is that it provides a controlled API elevation instead of just picking the highest // which could be risky and untested (especially if new API version is released after release of package consuming this // SDK) func (client *Client) getOpenApiHighestElevatedVersion(endpoint string) (string, error) { diff --git a/govcd/vdc_group.go b/govcd/vdc_group.go index 52a84366e..57b57ece0 100644 --- a/govcd/vdc_group.go +++ b/govcd/vdc_group.go @@ -6,8 +6,10 @@ package govcd import ( "fmt" - "github.com/vmware/go-vcloud-director/v2/types/v56" "net/url" + "strings" + + "github.com/vmware/go-vcloud-director/v2/types/v56" ) // VdcGroup is a structure defining a VdcGroup in Organization @@ -315,6 +317,43 @@ func (adminOrg *AdminOrg) GetVdcGroupById(id string) (*VdcGroup, error) { return vdcGroup, nil } +// GetVdcGroupById Returns VDC group using provided ID +func (org *Org) GetVdcGroupById(id string) (*VdcGroup, error) { + if id == "" { + return nil, fmt.Errorf("empty VDC group ID") + } + + tenantContext, err := org.getTenantContext() + if err != nil { + return nil, err + } + + endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointVdcGroups + minimumApiVersion, err := org.client.checkOpenApiEndpointCompatibility(endpoint) + if err != nil { + return nil, err + } + + urlRef, err := org.client.OpenApiBuildEndpoint(endpoint, id) + if err != nil { + return nil, err + } + + vdcGroup := &VdcGroup{ + VdcGroup: &types.VdcGroup{}, + client: org.client, + Href: urlRef.String(), + parent: org, + } + + err = org.client.OpenApiGetItem(minimumApiVersion, urlRef, nil, vdcGroup.VdcGroup, getTenantContextHeader(tenantContext)) + if err != nil { + return nil, err + } + + return vdcGroup, nil +} + // Update updates existing Vdc group. Allows changing only name and description and participating VCDs // Not restrictive update method also available - GenericUpdate func (vdcGroup *VdcGroup) Update(name, description string, participatingOrgVddIs []string) (*VdcGroup, error) { @@ -500,3 +539,79 @@ func (vdcGroup *VdcGroup) DisableDefaultPolicy() (*VdcGroup, error) { dfwPolicies.DefaultPolicy.Enabled = takeBoolPointer(false) return vdcGroup.UpdateDefaultDfwPolicies(*dfwPolicies.DefaultPolicy) } + +func getOwnerTypeFromUrn(urn string) (string, error) { + if !isUrn(urn) { + return "", fmt.Errorf("supplied ID is not URN: %s", urn) + } + + ss := strings.Split(urn, ":") + return ss[2], nil +} + +// OwnerIsVdcGroup evaluates given URN and returns true if it is a VDC Group +func OwnerIsVdcGroup(urn string) bool { + ownerType, err := getOwnerTypeFromUrn(urn) + if err != nil { + return false + } + + if strings.EqualFold(ownerType, types.UrnTypeVdcGroup) { + return true + } + + return false +} + +// OwnerIsVdc evaluates a given URN and returns true if it is a VDC +func OwnerIsVdc(urn string) bool { + ownerType, err := getOwnerTypeFromUrn(urn) + if err != nil { + return false + } + + if strings.EqualFold(ownerType, types.UrnTypeVdc) { + return true + } + + return false +} + +// GetCapabilities allows to retrieve a list of VDC capabilities. It has a list of values. Some particularly useful are: +// * networkProvider - overlay stack responsible for providing network functionality. (NSX_V or NSX_T) +// * crossVdc - supports cross vDC network creation +func (vdcGroup *VdcGroup) GetCapabilities() ([]types.VdcCapability, error) { + if vdcGroup.VdcGroup.Id == "" { + return nil, fmt.Errorf("VDC ID must be set to get capabilities") + } + + endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointVdcCapabilities + minimumApiVersion, err := vdcGroup.client.checkOpenApiEndpointCompatibility(endpoint) + if err != nil { + return nil, err + } + + urlRef, err := vdcGroup.client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, url.QueryEscape(vdcGroup.VdcGroup.Id))) + if err != nil { + return nil, err + } + + capabilities := make([]types.VdcCapability, 0) + err = vdcGroup.client.OpenApiGetAllItems(minimumApiVersion, urlRef, nil, &capabilities, nil) + if err != nil { + return nil, err + } + return capabilities, nil +} + +// IsNsxt is a convenience function to check if VDC is backed by NSX-T pVdc +// If error occurs - it returns false +func (vdcGroup *VdcGroup) IsNsxt() bool { + vdcCapabilities, err := vdcGroup.GetCapabilities() + if err != nil { + return false + } + + networkProviderCapability := getCapabilityValue(vdcCapabilities, "networkProvider") + return networkProviderCapability == types.VdcCapabilityNetworkProviderNsxt +} diff --git a/govcd/vdc_group_common_test.go b/govcd/vdc_group_common_test.go new file mode 100644 index 000000000..163ed771a --- /dev/null +++ b/govcd/vdc_group_common_test.go @@ -0,0 +1,142 @@ +//go:build network || functional || openapi || vdcGroup || nsxt || gateway || ALL +// +build network functional openapi vdcGroup nsxt gateway ALL + +/* + * Copyright 2022 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. + */ + +package govcd + +import ( + "fmt" + "net/url" + + "github.com/vmware/go-vcloud-director/v2/types/v56" + . "gopkg.in/check.v1" +) + +func test_CreateVdcGroup(check *C, adminOrg *AdminOrg, vcd *TestVCD) (*Vdc, *VdcGroup) { + createdVdc := createNewVdc(vcd, check, check.TestName()) + + createdVdcAsCandidate, err := adminOrg.GetAllNsxtVdcGroupCandidates(createdVdc.vdcId(), + map[string][]string{"filter": []string{fmt.Sprintf("name==%s", url.QueryEscape(createdVdc.vdcName()))}}) + check.Assert(err, IsNil) + check.Assert(createdVdcAsCandidate, NotNil) + check.Assert(len(createdVdcAsCandidate) == 1, Equals, true) + + existingVdcAsCandidate, err := adminOrg.GetAllNsxtVdcGroupCandidates(createdVdc.vdcId(), + map[string][]string{"filter": []string{fmt.Sprintf("name==%s", url.QueryEscape(vcd.nsxtVdc.vdcName()))}}) + check.Assert(err, IsNil) + check.Assert(existingVdcAsCandidate, NotNil) + check.Assert(len(existingVdcAsCandidate) == 1, Equals, true) + + vdcGroupConfig := &types.VdcGroup{ + Name: check.TestName() + "Group", + OrgId: adminOrg.orgId(), + ParticipatingOrgVdcs: []types.ParticipatingOrgVdcs{ + types.ParticipatingOrgVdcs{ + VdcRef: types.OpenApiReference{ + ID: createdVdc.vdcId(), + }, + SiteRef: (createdVdcAsCandidate)[0].SiteRef, + OrgRef: (createdVdcAsCandidate)[0].OrgRef, + }, + types.ParticipatingOrgVdcs{ + VdcRef: types.OpenApiReference{ + ID: vcd.nsxtVdc.vdcId(), + }, + SiteRef: (existingVdcAsCandidate)[0].SiteRef, + OrgRef: (existingVdcAsCandidate)[0].OrgRef, + }, + }, + LocalEgress: false, + UniversalNetworkingEnabled: false, + NetworkProviderType: "NSX_T", + Type: "LOCAL", + //DfwEnabled: true, // ignored by API + } + + vdcGroup, err := adminOrg.CreateVdcGroup(vdcGroupConfig) + check.Assert(err, IsNil) + check.Assert(vdcGroup, NotNil) + check.Assert(vdcGroup.IsNsxt(), Equals, true) + + openApiEndpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointVdcGroups + vdcGroup.VdcGroup.Id + PrependToCleanupListOpenApi(vdcGroup.VdcGroup.Name, check.TestName(), openApiEndpoint) + + return createdVdc, vdcGroup +} + +func createNewVdc(vcd *TestVCD, check *C, vdcName string) *Vdc { + adminOrg, err := vcd.client.GetAdminOrgByName(vcd.org.Org.Name) + check.Assert(err, IsNil) + check.Assert(adminOrg, NotNil) + + pVdcs, err := QueryProviderVdcByName(vcd.client, vcd.config.VCD.NsxtProviderVdc.Name) + check.Assert(err, IsNil) + + if len(pVdcs) == 0 { + check.Skip(fmt.Sprintf("No NSX-T Provider VDC found with name '%s'", vcd.config.VCD.NsxtProviderVdc.Name)) + } + providerVdcHref := pVdcs[0].HREF + pvdcStorageProfile, err := vcd.client.QueryProviderVdcStorageProfileByName(vcd.config.VCD.NsxtProviderVdc.StorageProfile, providerVdcHref) + check.Assert(err, IsNil) + check.Assert(pvdcStorageProfile, NotNil) + providerVdcStorageProfileHref := pvdcStorageProfile.HREF + + networkPools, err := QueryNetworkPoolByName(vcd.client, vcd.config.VCD.NsxtProviderVdc.NetworkPool) + check.Assert(err, IsNil) + if len(networkPools) == 0 { + check.Skip(fmt.Sprintf("No network pool found with name '%s'", vcd.config.VCD.NsxtProviderVdc.NetworkPool)) + } + + networkPoolHref := networkPools[0].HREF + trueValue := true + vdcConfiguration := &types.VdcConfiguration{ + Name: vdcName, + Xmlns: types.XMLNamespaceVCloud, + AllocationModel: "Flex", + ComputeCapacity: []*types.ComputeCapacity{ + &types.ComputeCapacity{ + CPU: &types.CapacityWithUsage{ + Units: "MHz", + Allocated: 1024, + Limit: 1024, + }, + Memory: &types.CapacityWithUsage{ + Allocated: 1024, + Limit: 1024, + Units: "MB", + }, + }, + }, + VdcStorageProfile: []*types.VdcStorageProfileConfiguration{&types.VdcStorageProfileConfiguration{ + Enabled: takeBoolPointer(true), + Units: "MB", + Limit: 1024, + Default: true, + ProviderVdcStorageProfile: &types.Reference{ + HREF: providerVdcStorageProfileHref, + }, + }, + }, + NetworkPoolReference: &types.Reference{ + HREF: networkPoolHref, + }, + ProviderVdcReference: &types.Reference{ + HREF: providerVdcHref, + }, + IsEnabled: true, + IsThinProvision: true, + UsesFastProvisioning: true, + IsElastic: &trueValue, + IncludeMemoryOverhead: &trueValue, + } + + vdc, err := adminOrg.CreateOrgVdc(vdcConfiguration) + check.Assert(err, IsNil) + check.Assert(vdc, NotNil) + + AddToCleanupList(vdcConfiguration.Name, "vdc", vcd.org.Org.Name, check.TestName()) + return vdc +} diff --git a/govcd/vdc_group_test.go b/govcd/vdc_group_test.go index 44fc74d71..c59ebdcf4 100644 --- a/govcd/vdc_group_test.go +++ b/govcd/vdc_group_test.go @@ -1,5 +1,5 @@ -//go:build functional || openapi || vdcGroup || ALL -// +build functional openapi vdcGroup ALL +//go:build functional || openapi || vdcGroup || nsxt || ALL +// +build functional openapi vdcGroup nsxt ALL /* * Copyright 2021 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. @@ -9,7 +9,6 @@ package govcd import ( "fmt" - "net/url" "strings" "github.com/vmware/go-vcloud-director/v2/types/v56" @@ -34,130 +33,6 @@ func (vcd *TestVCD) Test_CreateVdcGroup(check *C) { test_CreateVdcGroup(check, adminOrg, vcd) } -func test_CreateVdcGroup(check *C, adminOrg *AdminOrg, vcd *TestVCD) { - createdVdc := createNewVdc(vcd, check, check.TestName()) - - createdVdcAsCandidate, err := adminOrg.GetAllNsxtVdcGroupCandidates(createdVdc.vdcId(), - map[string][]string{"filter": []string{fmt.Sprintf("name==%s", url.QueryEscape(createdVdc.vdcName()))}}) - check.Assert(err, IsNil) - check.Assert(createdVdcAsCandidate, NotNil) - check.Assert(len(createdVdcAsCandidate) == 1, Equals, true) - - existingVdcAsCandidate, err := adminOrg.GetAllNsxtVdcGroupCandidates(createdVdc.vdcId(), - map[string][]string{"filter": []string{fmt.Sprintf("name==%s", url.QueryEscape(vcd.nsxtVdc.vdcName()))}}) - check.Assert(err, IsNil) - check.Assert(existingVdcAsCandidate, NotNil) - check.Assert(len(existingVdcAsCandidate) == 1, Equals, true) - - vdcGroupConfig := &types.VdcGroup{ - Name: check.TestName() + "Group", - OrgId: adminOrg.orgId(), - ParticipatingOrgVdcs: []types.ParticipatingOrgVdcs{ - types.ParticipatingOrgVdcs{ - VdcRef: types.OpenApiReference{ - ID: createdVdc.vdcId(), - }, - SiteRef: (createdVdcAsCandidate)[0].SiteRef, - OrgRef: (createdVdcAsCandidate)[0].OrgRef, - }, - types.ParticipatingOrgVdcs{ - VdcRef: types.OpenApiReference{ - ID: vcd.nsxtVdc.vdcId(), - }, - SiteRef: (existingVdcAsCandidate)[0].SiteRef, - OrgRef: (existingVdcAsCandidate)[0].OrgRef, - }, - }, - LocalEgress: false, - UniversalNetworkingEnabled: false, - NetworkProviderType: "NSX_T", - Type: "LOCAL", - //DfwEnabled: true, // ignored by API - } - - vdcGroup, err := adminOrg.CreateVdcGroup(vdcGroupConfig) - check.Assert(err, IsNil) - check.Assert(vdcGroup, NotNil) - - openApiEndpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointVdcGroups + vdcGroup.VdcGroup.Id - PrependToCleanupListOpenApi(vdcGroup.VdcGroup.Name, check.TestName(), openApiEndpoint) -} - -func createNewVdc(vcd *TestVCD, check *C, vdcName string) *Vdc { - adminOrg, err := vcd.client.GetAdminOrgByName(vcd.org.Org.Name) - check.Assert(err, IsNil) - check.Assert(adminOrg, NotNil) - - pVdcs, err := QueryProviderVdcByName(vcd.client, vcd.config.VCD.NsxtProviderVdc.Name) - check.Assert(err, IsNil) - - if len(pVdcs) == 0 { - check.Skip(fmt.Sprintf("No NSX-T Provider VDC found with name '%s'", vcd.config.VCD.NsxtProviderVdc.Name)) - } - providerVdcHref := pVdcs[0].HREF - - pvdcStorageProfile, err := vcd.client.QueryProviderVdcStorageProfileByName(vcd.config.VCD.NsxtProviderVdc.StorageProfile, providerVdcHref) - - check.Assert(err, IsNil) - providerVdcStorageProfileHref := pvdcStorageProfile.HREF - - networkPools, err := QueryNetworkPoolByName(vcd.client, vcd.config.VCD.NsxtProviderVdc.NetworkPool) - check.Assert(err, IsNil) - if len(networkPools) == 0 { - check.Skip(fmt.Sprintf("No network pool found with name '%s'", vcd.config.VCD.NsxtProviderVdc.NetworkPool)) - } - - networkPoolHref := networkPools[0].HREF - trueValue := true - vdcConfiguration := &types.VdcConfiguration{ - Name: vdcName, - Xmlns: types.XMLNamespaceVCloud, - AllocationModel: "Flex", - ComputeCapacity: []*types.ComputeCapacity{ - &types.ComputeCapacity{ - CPU: &types.CapacityWithUsage{ - Units: "MHz", - Allocated: 1024, - Limit: 1024, - }, - Memory: &types.CapacityWithUsage{ - Allocated: 1024, - Limit: 1024, - Units: "MB", - }, - }, - }, - VdcStorageProfile: []*types.VdcStorageProfileConfiguration{&types.VdcStorageProfileConfiguration{ - Enabled: takeBoolPointer(true), - Units: "MB", - Limit: 1024, - Default: true, - ProviderVdcStorageProfile: &types.Reference{ - HREF: providerVdcStorageProfileHref, - }, - }, - }, - NetworkPoolReference: &types.Reference{ - HREF: networkPoolHref, - }, - ProviderVdcReference: &types.Reference{ - HREF: providerVdcHref, - }, - IsEnabled: true, - IsThinProvision: true, - UsesFastProvisioning: true, - IsElastic: &trueValue, - IncludeMemoryOverhead: &trueValue, - } - - vdc, err := adminOrg.CreateOrgVdc(vdcConfiguration) - check.Assert(vdc, NotNil) - check.Assert(err, IsNil) - - AddToCleanupList(vdcConfiguration.Name, "vdc", vcd.org.Org.Name, check.TestName()) - return vdc -} - // tests creation of NSX-T VDCs group func (vcd *TestVCD) Test_NsxtVdcGroup(check *C) { fmt.Printf("Running: %s\n", check.TestName()) diff --git a/types/v56/constants.go b/types/v56/constants.go index 989d1494e..e8f7c710c 100644 --- a/types/v56/constants.go +++ b/types/v56/constants.go @@ -481,6 +481,13 @@ const ( NsxtAlbCloudBackingTypeNsxtAlb = "NSXALB_NSXT" ) +const ( + // UrnTypeVdcGroup is the third segment of URN for VDC Group + UrnTypeVdcGroup = "vdcGroup" + // UrnTypeVdc is the third segment of URN for VDC + UrnTypeVdc = "vdc" +) + // Metadata type constants const ( MetadataStringValue string = "MetadataStringValue"