Skip to content

Commit

Permalink
Regions, supervisors and supervisor zones (#718)
Browse files Browse the repository at this point in the history
  • Loading branch information
Didainius authored Nov 7, 2024
1 parent c65a707 commit 1c1abca
Show file tree
Hide file tree
Showing 13 changed files with 731 additions and 6 deletions.
2 changes: 1 addition & 1 deletion .changes/v3.0.0/716-features.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
* Added `TmOrg` and `types.TmOrg` to structure for OpenAPI management of Organizations with methods
* Added `TmOrg` and `types.TmOrg` structure for OpenAPI management of Organizations with methods
`VCDClient.CreateTmOrg`, `VCDClient.GetAllTmOrgs`, `VCDClient.GetTmOrgByName`,
`VCDClient.GetTmOrgById`, `TmOrg.Update`, `TmOrg.Delete`, `TmOrg.Disable` [GH-716]
18 changes: 18 additions & 0 deletions .changes/v3.0.0/718-features.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
* Added `VCDClient.GetNsxtManagerOpenApiByUrl` method to retrieve configured NSX-T Manager entry
based on URL [GH-718]
* Added `VCDClient.GetVCenterByUrl` method to retrieve configured vCenter server entry based on URL
[GH-718]
* Added `VCenter.RefreshStorageProfiles` to refresh storage profiles available in vCenter server
[GH-718]
* Added `Region` and `types.Region` to structure for OpenAPI management of Regions with methods
`VCDClient.CreateRegion`, `VCDClient.GetAllRegions`, `VCDClient.GetRegionByName`,
`VCDClient.GetRegionById`, `Region.Update`, `Region.Delete` [GH-718]
* Added `Supervisor` and `types.Supervisor` structure for reading available Supervisors
`VCDClient.GetAllSupervisors`, `VCDClient.GetSupervisorById`, `VCDClient.GetSupervisorByName`,
`VCDClient.GetSupervisorByNameAndVcenterId`, `Vcenter.GetAllSupervisors`,
`Vcenter.GetSupervisorByName` [GH-718]
* Added `SupervisorZone` and `types.SupervisorZone` structure for reading available Supervisor Zones
`Supervisor.GetAllSupervisorZones`, `Supervisor.GetSupervisorZoneById`,
`Supervisor.GetSupervisorZoneByName`, `Supervisor.`, `VCDClient.GetAllSupervisors`,
`VCDClient.GetSupervisorById`, `VCDClient.GetSupervisorByName`, `Vcenter.GetAllSupervisors`
[GH-718]
31 changes: 27 additions & 4 deletions govcd/api_vcd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,13 @@ type TestConfig struct {
Tm struct {
RegionStoragePolicy string `yaml:"regionStoragePolicy"`

CreateVcenter bool `yaml:"createVcenter"`
VcenterUsername string `yaml:"vcenterUsername"`
VcenterPassword string `yaml:"vcenterPassword"`
VcenterUrl string `yaml:"vcenterUrl"`
CreateVcenter bool `yaml:"createVcenter"`
VcenterUsername string `yaml:"vcenterUsername"`
VcenterPassword string `yaml:"vcenterPassword"`
VcenterUrl string `yaml:"vcenterUrl"`
VcenterStorageProfile string `yaml:"vcenterStorageProfile"`
VcenterSupervisor string `yaml:"vcenterSupervisor"`
VcenterSupervisorZone string `yaml:"vcenterSupervisorZone"`

CreateNsxtManager bool `yaml:"createNsxtManager"`
NsxtManagerUsername string `yaml:"nsxtManagerUsername"`
Expand Down Expand Up @@ -953,6 +956,26 @@ func (vcd *TestVCD) removeLeftoverEntities(entity CleanupEntity) {
return
}

vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
case "OpenApiEntityVcenter":
vc, err := vcd.client.GetVCenterByName(entity.Name)
if err != nil {
vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
return
}

err = vc.Disable()
if err != nil {
vcd.infoCleanup("removeLeftoverEntries: [ERROR] Error disabling %s '%s': %s\n", entity.EntityType, entity.Name, err)
return
}

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

vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
case "vapp":
vdc := vcd.vdc
Expand Down
28 changes: 28 additions & 0 deletions govcd/nsxt_manager_openapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,34 @@ func (vcdClient *VCDClient) GetNsxtManagerOpenApiByName(name string) (*NsxtManag
return singleEntity, nil
}

// GetNsxtManagerOpenApiByName retrieves NSX-T Manager by name
func (vcdClient *VCDClient) GetNsxtManagerOpenApiByUrl(nsxtManagerUrl string) (*NsxtManagerOpenApi, error) {
if nsxtManagerUrl == "" {
return nil, fmt.Errorf("%s lookup requires URL", labelNsxtManagerOpenApi)
}

// API filtering by URL is not supported so relying on local filtering
nsxtManagers, err := vcdClient.GetAllNsxtManagersOpenApi(nil)
if err != nil {
return nil, err
}

filteredEntities := make([]*NsxtManagerOpenApi, 0)
for _, nsxtManager := range nsxtManagers {
if nsxtManager.NsxtManagerOpenApi.Url == nsxtManagerUrl {
filteredEntities = append(filteredEntities, nsxtManager)
}

}

singleEntity, err := oneOrError("Url", nsxtManagerUrl, filteredEntities)
if err != nil {
return nil, err
}

return singleEntity, nil
}

// Update NSX-T Manager configuration
func (t *NsxtManagerOpenApi) Update(TmNsxtManagerConfig *types.NsxtManagerOpenApi) (*NsxtManagerOpenApi, error) {
c := crudConfig{
Expand Down
3 changes: 3 additions & 0 deletions govcd/openapi_endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ var endpointMinApiVersions = map[string]string{
types.OpenApiPathVcf + types.OpenApiEndpointRegionStoragePolicies: "40.0",
types.OpenApiPathVcf + types.OpenApiEndpointContentLibraries: "40.0",
types.OpenApiPathVcf + types.OpenApiEndpointNsxManagers: "40.0",
types.OpenApiPathVcf + types.OpenApiEndpointRegions: "40.0",
types.OpenApiPathVcf + types.OpenApiEndpointSupervisors: "40.0",
types.OpenApiPathVcf + types.OpenApiEndpointSupervisorZones: "40.0",
}

// endpointElevatedApiVersions endpoint elevated API versions
Expand Down
100 changes: 100 additions & 0 deletions govcd/tm_region.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package govcd

import (
"fmt"
"net/url"

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

const labelRegion = "Region"

type Region struct {
Region *types.Region
vcdClient *VCDClient
}

// wrap is a hidden helper that facilitates the usage of a generic CRUD function
//
//lint:ignore U1000 this method is used in generic functions, but annoys staticcheck
func (r Region) wrap(inner *types.Region) *Region {
r.Region = inner
return &r
}

// CreateRegion creates a new region
func (vcdClient *VCDClient) CreateRegion(config *types.Region) (*Region, error) {
c := crudConfig{
entityLabel: labelRegion,
endpoint: types.OpenApiPathVcf + types.OpenApiEndpointRegions,
}
outerType := Region{vcdClient: vcdClient}
return createOuterEntity(&vcdClient.Client, outerType, c, config)
}

// GetAllRegions retrieves all Regions with an optional query filter
func (vcdClient *VCDClient) GetAllRegions(queryParameters url.Values) ([]*Region, error) {
c := crudConfig{
entityLabel: labelRegion,
endpoint: types.OpenApiPathVcf + types.OpenApiEndpointRegions,
queryParameters: queryParameters,
}

outerType := Region{vcdClient: vcdClient}
return getAllOuterEntities(&vcdClient.Client, outerType, c)
}

// GetRegionByName retrieves a region by name
func (vcdClient *VCDClient) GetRegionByName(name string) (*Region, error) {
if name == "" {
return nil, fmt.Errorf("%s lookup requires name", labelRegion)
}

queryParams := url.Values{}
queryParams.Add("filter", "name=="+name)

filteredEntities, err := vcdClient.GetAllRegions(queryParams)
if err != nil {
return nil, err
}

singleEntity, err := oneOrError("name", name, filteredEntities)
if err != nil {
return nil, err
}

return vcdClient.GetRegionById(singleEntity.Region.ID)
}

// GetRegionById retrieves a region by ID
func (vcdClient *VCDClient) GetRegionById(id string) (*Region, error) {
c := crudConfig{
entityLabel: labelRegion,
endpoint: types.OpenApiPathVcf + types.OpenApiEndpointRegions,
endpointParams: []string{id},
}

outerType := Region{vcdClient: vcdClient}
return getOuterEntity(&vcdClient.Client, outerType, c)
}

// Update Region with new configuration
func (r *Region) Update(RegionConfig *types.Region) (*Region, error) {
c := crudConfig{
entityLabel: labelRegion,
endpoint: types.OpenApiPathVcf + types.OpenApiEndpointRegions,
endpointParams: []string{r.Region.ID},
}
outerType := Region{vcdClient: r.vcdClient}
return updateOuterEntity(&r.vcdClient.Client, outerType, c, RegionConfig)
}

// Delete Region
func (r *Region) Delete() error {
c := crudConfig{
entityLabel: labelRegion,
endpoint: types.OpenApiPathVcf + types.OpenApiEndpointRegions,
endpointParams: []string{r.Region.ID},
}
return deleteEntityById(&r.vcdClient.Client, c)
}
163 changes: 163 additions & 0 deletions govcd/tm_region_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
//go:build tm || functional || ALL

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

package govcd

import (
"net/url"

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

func (vcd *TestVCD) Test_TmRegion(check *C) {
skipNonTm(vcd, check)
sysadminOnly(vcd, check)

vc, vcCreated, nsxtManager, nsxtManagerCreated := getOrCreateVcAndNsxtManager(vcd, check)
supervisor, err := vc.GetSupervisorByName(vcd.config.Tm.VcenterSupervisor)
check.Assert(err, IsNil)

r := &types.Region{
Name: check.TestName(),
NsxManager: &types.OpenApiReference{
ID: nsxtManager.NsxtManagerOpenApi.ID,
},
Supervisors: []types.OpenApiReference{
{
ID: supervisor.Supervisor.SupervisorID,
Name: supervisor.Supervisor.Name,
},
},
StoragePolicies: []string{vcd.config.Tm.VcenterStorageProfile},
IsEnabled: true,
}

createdRegion, err := vcd.client.CreateRegion(r)
check.Assert(err, IsNil)
check.Assert(createdRegion.Region, NotNil)
AddToCleanupListOpenApi(createdRegion.Region.ID, check.TestName(), types.OpenApiPathVcf+types.OpenApiEndpointRegions+createdRegion.Region.ID)

check.Assert(createdRegion.Region.Status, Equals, "READY") // Region is operational

// Get By Name
byName, err := vcd.client.GetRegionByName(r.Name)
check.Assert(err, IsNil)
check.Assert(byName, NotNil)

// Get By ID
byId, err := vcd.client.GetRegionById(createdRegion.Region.ID)
check.Assert(err, IsNil)
check.Assert(byId, NotNil)

check.Assert(byName.Region, DeepEquals, byId.Region)

// Get All
allRegions, err := vcd.client.GetAllRegions(nil)
check.Assert(err, IsNil)
check.Assert(allRegions, NotNil)
check.Assert(len(allRegions) > 0, Equals, true)

// TODO: TM: No Update so far
// Update
// createdRegion.Region.IsEnabled = false
// updated, err := createdRegion.Update(createdRegion.Region)
// check.Assert(err, IsNil)
// check.Assert(updated, NotNil)

// Delete
err = createdRegion.Delete()
check.Assert(err, IsNil)

notFoundByName, err := vcd.client.GetRegionByName(createdRegion.Region.Name)
check.Assert(ContainsNotFound(err), Equals, true)
check.Assert(notFoundByName, IsNil)

// Cleanup
if vcCreated {
err = vc.Disable()
check.Assert(err, IsNil)
err = vc.Delete()
check.Assert(err, IsNil)
}

if nsxtManagerCreated {
err = nsxtManager.Delete()
check.Assert(err, IsNil)
}
}

// getOrCreateVcAndNsxtManager will check configuration file and create vCenter and NSX-T Manager if
// stated in the 'createVcenter' and 'createNsxtManager' properties and they are not present in TM.
// Otherwise it just retrieves them
func getOrCreateVcAndNsxtManager(vcd *TestVCD, check *C) (*VCenter, bool, *NsxtManagerOpenApi, bool) {
vCenterCreated := false
nsxtManagerCreated := false
vc, err := vcd.client.GetVCenterByUrl(vcd.config.Tm.VcenterUrl)
if ContainsNotFound(err) && !vcd.config.Tm.CreateVcenter {
check.Skip("vCenter is not configured and configuration is not allowed in config file")
}
if ContainsNotFound(err) {
vcCfg := &types.VSphereVirtualCenter{
Name: check.TestName() + "-vc",
Username: vcd.config.Tm.VcenterUsername,
Password: vcd.config.Tm.VcenterPassword,
Url: vcd.config.Tm.VcenterUrl,
IsEnabled: true,
}
// Certificate must be trusted before adding vCenter
url, err := url.Parse(vcCfg.Url)
check.Assert(err, IsNil)
trustedCert, err := vcd.client.AutoTrustCertificate(url)
check.Assert(err, IsNil)
if trustedCert != nil {
AddToCleanupListOpenApi(trustedCert.TrustedCertificate.ID, check.TestName()+"trusted-cert", types.OpenApiPathVersion1_0_0+types.OpenApiEndpointTrustedCertificates+trustedCert.TrustedCertificate.ID)
}

vc, err = vcd.client.CreateVcenter(vcCfg)
check.Assert(err, IsNil)
check.Assert(vc, NotNil)
PrependToCleanupList(vcCfg.Name, "OpenApiEntityVcenter", check.TestName(), types.OpenApiPathVersion1_0_0+types.OpenApiEndpointVirtualCenters+vc.VSphereVCenter.VcId)

vCenterCreated = true
// Refresh connected vCenter to be sure that all artifacts are loaded
printVerbose("# Refreshing vCenter %s\n", vc.VSphereVCenter.Url)
err = vc.Refresh()
check.Assert(err, IsNil)

printVerbose("# Refreshing Storage Profiles in vCenter %s\n", vc.VSphereVCenter.Url)
err = vc.RefreshStorageProfiles()
check.Assert(err, IsNil)
}

nsxtManager, err := vcd.client.GetNsxtManagerOpenApiByUrl(vcd.config.Tm.NsxtManagerUrl)
if ContainsNotFound(err) && !vcd.config.Tm.CreateNsxtManager {
check.Skip("NSX-T Manager is not configured and configuration is not allowed in config file")
}
if ContainsNotFound(err) {
nsxtCfg := &types.NsxtManagerOpenApi{
Name: check.TestName(),
Username: vcd.config.Tm.NsxtManagerUsername,
Password: vcd.config.Tm.NsxtManagerPassword,
Url: vcd.config.Tm.NsxtManagerUrl,
}
// Certificate must be trusted before adding NSX-T Manager
url, err := url.Parse(nsxtCfg.Url)
check.Assert(err, IsNil)
trustedCert, err := vcd.client.AutoTrustCertificate(url)
check.Assert(err, IsNil)
if trustedCert != nil {
AddToCleanupListOpenApi(trustedCert.TrustedCertificate.ID, check.TestName()+"trusted-cert", types.OpenApiPathVersion1_0_0+types.OpenApiEndpointTrustedCertificates+trustedCert.TrustedCertificate.ID)
}
nsxtManager, err = vcd.client.CreateNsxtManagerOpenApi(nsxtCfg)
check.Assert(err, IsNil)
check.Assert(nsxtManager, NotNil)
PrependToCleanupListOpenApi(nsxtManager.NsxtManagerOpenApi.ID, check.TestName(), types.OpenApiPathVcf+types.OpenApiEndpointNsxManagers+nsxtManager.NsxtManagerOpenApi.ID)
nsxtManagerCreated = true
}

return vc, vCenterCreated, nsxtManager, nsxtManagerCreated
}
Loading

0 comments on commit 1c1abca

Please sign in to comment.