From b963db417cd4a9221030899d6d0841f06ce4a2aa Mon Sep 17 00:00:00 2001 From: Dainius Serplis Date: Tue, 1 Oct 2024 13:13:14 +0300 Subject: [PATCH 1/4] WIP Signed-off-by: Dainius Serplis --- Makefile | 3 + govcd/api_vcd_test.go | 65 +++++++++++------ govcd/api_vcd_versions.go | 4 ++ govcd/common_test.go | 2 +- govcd/openapi_endpoints.go | 9 +++ govcd/tm_org.go | 107 ++++++++++++++++++++++++++++ govcd/tm_org_test.go | 61 ++++++++++++++++ govcd/tm_region.go | 94 ++++++++++++++++++++++++ govcd/tm_supervisor.go | 67 +++++++++++++++++ govcd/tm_supervisor_zone.go | 76 ++++++++++++++++++++ govcd/tm_vdc.go | 106 +++++++++++++++++++++++++++ types/v56/constants.go | 7 ++ types/v56/tm.go | 138 ++++++++++++++++++++++++++++++++++++ 13 files changed, 717 insertions(+), 22 deletions(-) create mode 100644 govcd/tm_org.go create mode 100644 govcd/tm_org_test.go create mode 100644 govcd/tm_region.go create mode 100644 govcd/tm_supervisor.go create mode 100644 govcd/tm_supervisor_zone.go create mode 100644 govcd/tm_vdc.go create mode 100644 types/v56/tm.go diff --git a/Makefile b/Makefile index fd8f55219..63ff37e2d 100644 --- a/Makefile +++ b/Makefile @@ -56,6 +56,9 @@ testlb: testnsxv: cd govcd && go test -tags "nsxv" -timeout $(timeout) -check.vv +testtm: + cd govcd && go test -tags "tm" -timeout 0 -check.vv + # vet runs the Go source code static analysis tool `vet` to find # any common errors. vet: diff --git a/govcd/api_vcd_test.go b/govcd/api_vcd_test.go index a9630ba20..313354284 100644 --- a/govcd/api_vcd_test.go +++ b/govcd/api_vcd_test.go @@ -1,4 +1,4 @@ -//go:build api || openapi || functional || catalog || vapp || gateway || network || org || query || extnetwork || task || vm || vdc || system || disk || lb || lbAppRule || lbAppProfile || lbServerPool || lbServiceMonitor || lbVirtualServer || user || search || nsxv || nsxt || auth || affinity || role || alb || certificate || vdcGroup || metadata || providervdc || rde || vsphere || uiPlugin || cse || slz || ALL +//go:build api || openapi || functional || catalog || vapp || gateway || network || org || query || extnetwork || task || vm || vdc || system || disk || lb || lbAppRule || lbAppProfile || lbServerPool || lbServiceMonitor || lbVirtualServer || user || search || nsxv || nsxt || auth || affinity || role || alb || certificate || vdcGroup || metadata || providervdc || rde || vsphere || uiPlugin || cse || slz || tm || ALL /* * Copyright 2022 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. @@ -97,6 +97,7 @@ const ( const ( TestRequiresSysAdminPrivileges = "Test %s requires system administrator privileges" + TestRequiresTm = "Test %s requires TM" ) type Tenant struct { @@ -648,7 +649,13 @@ func (vcd *TestVCD) SetUpSuite(check *C) { if err == nil { versionInfo = fmt.Sprintf("version %s built at %s", version, versionTime) } - fmt.Printf("Running on VCD %s (%s)\nas user %s@%s (using %s)\n", vcd.config.Provider.Url, versionInfo, + env := "VCD" + isTm := vcd.client.Client.IsTm() + if isTm { + env = "TM" + } + + fmt.Printf("Running on %s %s (%s)\nas user %s@%s (using %s)\n", env, vcd.config.Provider.Url, versionInfo, vcd.config.Provider.User, vcd.config.Provider.SysOrg, authenticationMode) if !vcd.client.Client.IsSysAdmin { vcd.skipAdminTests = true @@ -661,29 +668,33 @@ func (vcd *TestVCD) SetUpSuite(check *C) { persistentCleanupIp = vcd.config.Provider.Url persistentCleanupIp = reHttp.ReplaceAllString(persistentCleanupIp, "") persistentCleanupIp = reApi.ReplaceAllString(persistentCleanupIp, "") - // set org - vcd.org, err = vcd.client.GetOrgByName(config.VCD.Org) - if err != nil { - fmt.Printf("error retrieving org %s: %s\n", config.VCD.Org, err) - os.Exit(1) - } - // set vdc - vcd.vdc, err = vcd.org.GetVDCByName(config.VCD.Vdc, false) - if err != nil || vcd.vdc == nil { - panic(err) - } - // configure NSX-T VDC for convenience if it is specified in configuration - if config.VCD.Nsxt.Vdc != "" { - vcd.nsxtVdc, err = vcd.org.GetVDCByName(config.VCD.Nsxt.Vdc, false) + if !isTm { + // set org + vcd.org, err = vcd.client.GetOrgByName(config.VCD.Org) if err != nil { - panic(fmt.Errorf("error geting NSX-T VDC '%s': %s", config.VCD.Nsxt.Vdc, err)) + fmt.Printf("error retrieving org %s: %s\n", config.VCD.Org, err) + os.Exit(1) + } + // set vdc + vcd.vdc, err = vcd.org.GetVDCByName(config.VCD.Vdc, false) + if err != nil || vcd.vdc == nil { + panic(err) + } + + // configure NSX-T VDC for convenience if it is specified in configuration + if config.VCD.Nsxt.Vdc != "" { + vcd.nsxtVdc, err = vcd.org.GetVDCByName(config.VCD.Nsxt.Vdc, false) + if err != nil { + panic(fmt.Errorf("error geting NSX-T VDC '%s': %s", config.VCD.Nsxt.Vdc, err)) + } } } - // If neither the vApp or VM tags are set, we also skip the - // creation of the default vApp - if !isTagSet("vapp") && !isTagSet("vm") { + // vApp creation is skipped for one out of two reasons: + // * neither the vApp or VM tags are set + // * env is TM + if (!isTagSet("vapp") && !isTagSet("vm")) || isTm { // vcd.skipVappTests = true skipVappCreation = true } @@ -1810,7 +1821,7 @@ func (vcd *TestVCD) TestClient_getloginurl(check *C) { check.Fatalf("err: %s", err) } - if client.sessionHREF.Path != "/cloudapi/1.0.0/sessions" { + if !strings.HasSuffix(client.sessionHREF.Path, "/cloudapi/1.0.0/sessions") { check.Fatalf("Getting LoginUrl failed, url: %s", client.sessionHREF.Path) } } @@ -2063,6 +2074,18 @@ func skipNoNsxtAlbConfiguration(vcd *TestVCD, check *C) { } } +func skipNonTm(vcd *TestVCD, check *C) { + if !vcd.client.Client.IsTm() { + check.Skip(fmt.Sprintf(TestRequiresTm, check.TestName())) + } +} + +func sysadminOnly(vcd *TestVCD, check *C) { + if vcd.skipAdminTests { + check.Skip(fmt.Sprintf(TestRequiresSysAdminPrivileges, check.TestName())) + } +} + // skipOpenApiEndpointTest is a helper to skip tests for particular unsupported OpenAPI endpoints func skipOpenApiEndpointTest(vcd *TestVCD, check *C, endpoint string) { minimumRequiredApiVersion := endpointMinApiVersions[endpoint] diff --git a/govcd/api_vcd_versions.go b/govcd/api_vcd_versions.go index 6e873989d..fca2d3cdd 100644 --- a/govcd/api_vcd_versions.go +++ b/govcd/api_vcd_versions.go @@ -371,3 +371,7 @@ func (client *Client) VersionEqualOrGreater(compareTo string, howManyDigits int) return fullVersion.Version.GreaterThanOrEqual(compareToVersion), nil } + +func (client *Client) IsTm() bool { + return client.APIVCDMaxVersionIs(">= 40") // TODO - make it more precise +} diff --git a/govcd/common_test.go b/govcd/common_test.go index 8e0701df0..919ad0430 100644 --- a/govcd/common_test.go +++ b/govcd/common_test.go @@ -1,4 +1,4 @@ -//go:build api || auth || functional || catalog || vapp || gateway || network || org || query || extnetwork || task || vm || vdc || system || disk || lb || lbAppRule || lbAppProfile || lbServerPool || lbServiceMonitor || lbVirtualServer || user || role || nsxv || nsxt || openapi || affinity || search || alb || certificate || vdcGroup || metadata || providervdc || rde || uiPlugin || vsphere || cse || slz || ALL +//go:build api || auth || functional || catalog || vapp || gateway || network || org || query || extnetwork || task || vm || vdc || system || disk || lb || lbAppRule || lbAppProfile || lbServerPool || lbServiceMonitor || lbVirtualServer || user || role || nsxv || nsxt || openapi || affinity || search || alb || certificate || vdcGroup || metadata || providervdc || rde || uiPlugin || vsphere || cse || slz || tm || ALL /* * Copyright 2021 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. diff --git a/govcd/openapi_endpoints.go b/govcd/openapi_endpoints.go index 2522fcadf..e6fdf0445 100644 --- a/govcd/openapi_endpoints.go +++ b/govcd/openapi_endpoints.go @@ -154,6 +154,12 @@ var endpointMinApiVersions = map[string]string{ // NSX-T Tier 0 router interfaces that can be used for IP Space uplink assignment types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointNsxtTier0RouterInterfaces: "38.0", + + // VCF + types.OpenApiPathVcf + types.OpenApiEndpointRegions: "40.0", + types.OpenApiPathVcf + types.OpenApiEndpointSupervisors: "40.0", + types.OpenApiPathVcf + types.OpenApiEndpointSupervisorZones: "40.0", + types.OpenApiPathVcf + types.OpenApiEndpointTmVdcs: "40.0", } // endpointElevatedApiVersions endpoint elevated API versions @@ -240,6 +246,9 @@ var endpointElevatedApiVersions = map[string][]string{ //"37.1", // Introduced support "38.0", // Adds 'Interfaces' structure for associating particular Tier-0 router interfaces }, + types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointOrgs: { + "40.0", // TM Orgs + }, } // checkOpenApiEndpointCompatibility checks if VCD version (to which the client is connected) is sufficient to work with diff --git a/govcd/tm_org.go b/govcd/tm_org.go new file mode 100644 index 000000000..d4d91b53f --- /dev/null +++ b/govcd/tm_org.go @@ -0,0 +1,107 @@ +package govcd + +/* + * Copyright 2024 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. + */ + +import ( + "fmt" + "net/url" + + "github.com/vmware/go-vcloud-director/v2/types/v56" +) + +const labelOrganization = "Organization" + +type TmOrg struct { + TmOrg *types.TmOrg + 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 (g TmOrg) wrap(inner *types.TmOrg) *TmOrg { + g.TmOrg = inner + return &g +} + +func (vcdClient *VCDClient) CreateTmOrg(config *types.TmOrg) (*TmOrg, error) { + if !vcdClient.Client.IsTm() { + return nil, fmt.Errorf("err") + } + c := crudConfig{ + entityLabel: labelOrganization, + endpoint: types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointOrgs, + } + outerType := TmOrg{vcdClient: vcdClient} + return createOuterEntity(&vcdClient.Client, outerType, c, config) +} + +func (vcdClient *VCDClient) GetAllTmOrgs(queryParameters url.Values) ([]*TmOrg, error) { + c := crudConfig{ + entityLabel: labelOrganization, + endpoint: types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointOrgs, + queryParameters: queryParameters, + } + + outerType := TmOrg{vcdClient: vcdClient} + return getAllOuterEntities(&vcdClient.Client, outerType, c) +} + +func (vcdClient *VCDClient) GetTmOrgByName(name string) (*TmOrg, error) { + if name == "" { + return nil, fmt.Errorf("%s lookup requires name", labelOrganization) + } + + queryParams := url.Values{} + queryParams.Add("filter", "name=="+name) + + filteredEntities, err := vcdClient.GetAllTmOrgs(queryParams) + if err != nil { + return nil, err + } + + singleEntity, err := oneOrError("name", name, filteredEntities) + if err != nil { + return nil, err + } + + return vcdClient.GetTmOrgById(singleEntity.TmOrg.ID) +} + +func (vcdClient *VCDClient) GetTmOrgById(id string) (*TmOrg, error) { + c := crudConfig{ + entityLabel: labelOrganization, + endpoint: types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointOrgs, + endpointParams: []string{id}, + } + + outerType := TmOrg{vcdClient: vcdClient} + return getOuterEntity(&vcdClient.Client, outerType, c) +} + +func (o *TmOrg) Update(TmOrgConfig *types.TmOrg) (*TmOrg, error) { + c := crudConfig{ + entityLabel: labelOrganization, + endpoint: types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointOrgs, + endpointParams: []string{o.TmOrg.ID}, + } + outerType := TmOrg{vcdClient: o.vcdClient} + return updateOuterEntity(&o.vcdClient.Client, outerType, c, TmOrgConfig) +} + +func (o *TmOrg) Delete() error { + c := crudConfig{ + entityLabel: labelOrganization, + endpoint: types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointOrgs, + endpointParams: []string{o.TmOrg.ID}, + } + return deleteEntityById(&o.vcdClient.Client, c) +} + +func (o *TmOrg) Disable() error { + o.TmOrg.IsEnabled = false + _, err := o.Update(o.TmOrg) + return err +} diff --git a/govcd/tm_org_test.go b/govcd/tm_org_test.go new file mode 100644 index 000000000..e1cb2deb9 --- /dev/null +++ b/govcd/tm_org_test.go @@ -0,0 +1,61 @@ +//go:build tm || functional || ALL + +/* + * Copyright 2024 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. + */ + +package govcd + +import ( + . "gopkg.in/check.v1" + + "github.com/vmware/go-vcloud-director/v2/types/v56" +) + +func (vcd *TestVCD) Test_TmOrg(check *C) { + skipNonTm(vcd, check) + sysadminOnly(vcd, check) + + cfg := &types.TmOrg{ + Name: check.TestName(), + DisplayName: check.TestName(), + CanManageOrgs: true, + } + + tmOrg, err := vcd.client.CreateTmOrg(cfg) + check.Assert(err, IsNil) + check.Assert(tmOrg, NotNil) + + // Add to cleanup list + PrependToCleanupListOpenApi(tmOrg.TmOrg.ID, check.TestName(), types.OpenApiPathVersion1_0_0+types.OpenApiEndpointOrgs+tmOrg.TmOrg.ID) + + // Get By Name + byName, err := vcd.client.GetTmOrgByName(cfg.Name) + check.Assert(err, IsNil) + check.Assert(byName, NotNil) + + // Get By ID + byId, err := vcd.client.GetTmOrgById(tmOrg.TmOrg.ID) + check.Assert(err, IsNil) + check.Assert(byId, NotNil) + + // Get All + allTmOrgs, err := vcd.client.GetAllTmOrgs(nil) + check.Assert(err, IsNil) + check.Assert(allTmOrgs, NotNil) + check.Assert(len(allTmOrgs) > 0, Equals, true) + + // Update + tmOrg.TmOrg.IsEnabled = false + updated, err := tmOrg.Update(tmOrg.TmOrg) + check.Assert(err, IsNil) + check.Assert(updated, NotNil) + + // Delete + err = tmOrg.Delete() + check.Assert(err, IsNil) + + notFoundByName, err := vcd.client.GetTmOrgByName(cfg.Name) + check.Assert(ContainsNotFound(err), Equals, true) + check.Assert(notFoundByName, IsNil) +} diff --git a/govcd/tm_region.go b/govcd/tm_region.go new file mode 100644 index 000000000..1ed64816f --- /dev/null +++ b/govcd/tm_region.go @@ -0,0 +1,94 @@ +package govcd + +import ( + "fmt" + "net/url" + + "github.com/vmware/go-vcloud-director/v2/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 +} + +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) +} + +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[Region, types.Region](&vcdClient.Client, outerType, c) +} + +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) +} + +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[Region, types.Region](&vcdClient.Client, outerType, c) +} + +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) +} + +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) +} diff --git a/govcd/tm_supervisor.go b/govcd/tm_supervisor.go new file mode 100644 index 000000000..8358f50c7 --- /dev/null +++ b/govcd/tm_supervisor.go @@ -0,0 +1,67 @@ +package govcd + +import ( + "fmt" + "net/url" + + "github.com/vmware/go-vcloud-director/v2/types/v56" +) + +const labelSupervisor = "Supervisor" + +// Supervisor is a type for handling VCF Supervisors +type Supervisor struct { + Supervisor *types.Supervisor + 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 (s Supervisor) wrap(inner *types.Supervisor) *Supervisor { + s.Supervisor = inner + return &s +} + +func (vcdClient *VCDClient) GetAllSupervisors(queryParameters url.Values) ([]*Supervisor, error) { + c := crudConfig{ + endpoint: types.OpenApiPathVcf + types.OpenApiEndpointSupervisors, + entityLabel: labelSupervisor, + queryParameters: queryParameters, + } + + outerType := Supervisor{vcdClient: vcdClient} + return getAllOuterEntities(&vcdClient.Client, outerType, c) +} + +func (vcdClient *VCDClient) GetSupervisorById(id string) (*Supervisor, error) { + c := crudConfig{ + entityLabel: labelSupervisor, + endpoint: types.OpenApiPathVcf + types.OpenApiEndpointSupervisors, + endpointParams: []string{id}, + } + + outerType := Supervisor{vcdClient: vcdClient} + return getOuterEntity(&vcdClient.Client, outerType, c) +} + +func (vcdClient *VCDClient) GetSupervisorByName(name string) (*Supervisor, error) { + if name == "" { + return nil, fmt.Errorf("%s lookup requires name", labelSupervisor) + } + + queryParams := url.Values{} + queryParams.Add("filter", "name=="+name) + + filteredEntities, err := vcdClient.GetAllSupervisors(queryParams) + if err != nil { + return nil, err + } + + singleEntity, err := oneOrError("name", name, filteredEntities) + if err != nil { + return nil, err + } + + return singleEntity, nil +} diff --git a/govcd/tm_supervisor_zone.go b/govcd/tm_supervisor_zone.go new file mode 100644 index 000000000..f76cdaf39 --- /dev/null +++ b/govcd/tm_supervisor_zone.go @@ -0,0 +1,76 @@ +package govcd + +import ( + "fmt" + "net/url" + + "github.com/vmware/go-vcloud-director/v2/types/v56" +) + +const labelSupervisorZone = "Supervisor Zone" + +// Supervisor is a type for handling VCF Supervisors +type SupervisorZone struct { + SupervisorZone *types.SupervisorZone + 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 (s SupervisorZone) wrap(inner *types.SupervisorZone) *SupervisorZone { + s.SupervisorZone = inner + return &s +} + +func (s *Supervisor) GetAllSupervisorZones(queryParameters url.Values) ([]*SupervisorZone, error) { + c := crudConfig{ + endpoint: types.OpenApiPathVcf + types.OpenApiEndpointSupervisorZones, + entityLabel: labelSupervisorZone, + queryParameters: queryParameters, + } + + outerType := SupervisorZone{vcdClient: s.vcdClient} + return getAllOuterEntities(&s.vcdClient.Client, outerType, c) +} + +func (s *Supervisor) GetSupervisorZoneById(id string) (*SupervisorZone, error) { + if id == "" { + return nil, fmt.Errorf("'id' must be set") + } + + queryParams := copyOrNewUrlValues(nil) + queryParams = queryParameterFilterAnd("supervisor.id=="+s.Supervisor.SupervisorID, queryParams) + + c := crudConfig{ + endpoint: types.OpenApiPathVcf + types.OpenApiEndpointSupervisorZones, + entityLabel: labelSupervisorZone, + queryParameters: queryParams, + endpointParams: []string{id}, + } + + outerType := SupervisorZone{vcdClient: s.vcdClient} + return getOuterEntity(&s.vcdClient.Client, outerType, c) +} + +func (s *Supervisor) GetSupervisorZoneByName(name string) (*SupervisorZone, error) { + if name == "" { + return nil, fmt.Errorf("%s lookup requires name", labelSupervisor) + } + + queryParams := copyOrNewUrlValues(nil) + queryParams = queryParameterFilterAnd("supervisor.id=="+s.Supervisor.SupervisorID, queryParams) + queryParams = queryParameterFilterAnd("name=="+name, queryParams) + + filteredEntities, err := s.GetAllSupervisorZones(queryParams) + if err != nil { + return nil, err + } + + singleEntity, err := oneOrError("name", name, filteredEntities) + if err != nil { + return nil, err + } + + return singleEntity, nil +} diff --git a/govcd/tm_vdc.go b/govcd/tm_vdc.go new file mode 100644 index 000000000..4f3300678 --- /dev/null +++ b/govcd/tm_vdc.go @@ -0,0 +1,106 @@ +package govcd + +/* + * Copyright 2024 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. + */ + +import ( + "fmt" + "net/url" + + "github.com/vmware/go-vcloud-director/v2/types/v56" +) + +const labelVdc = "Vdc" + +type TmVdc struct { + TmVdc *types.TmVdc + 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 (g TmVdc) wrap(inner *types.TmVdc) *TmVdc { + g.TmVdc = inner + return &g +} + +func (vcdClient *VCDClient) CreateTmVdc(config *types.TmVdc) (*TmVdc, error) { + if !vcdClient.Client.IsTm() { + return nil, fmt.Errorf("err") + } + c := crudConfig{ + entityLabel: labelVdc, + endpoint: types.OpenApiPathVcf + types.OpenApiEndpointTmVdcs, + } + outerType := TmVdc{vcdClient: vcdClient} + return createOuterEntity(&vcdClient.Client, outerType, c, config) +} + +func (vcdClient *VCDClient) GetAllTmVdcs(queryParameters url.Values) ([]*TmVdc, error) { + c := crudConfig{ + entityLabel: labelVdc, + endpoint: types.OpenApiPathVcf + types.OpenApiEndpointTmVdcs, + queryParameters: queryParameters, + } + + outerType := TmVdc{vcdClient: vcdClient} + return getAllOuterEntities(&vcdClient.Client, outerType, c) +} + +func (vcdClient *VCDClient) GetTmVdcByName(name string) (*TmVdc, error) { + if name == "" { + return nil, fmt.Errorf("%s lookup requires name", labelVdc) + } + + // TODO - revisit filtering as filtering by name returns an error + filteredEntities, err := vcdClient.GetAllTmVdcs(nil) + if err != nil { + return nil, err + } + + for i := range filteredEntities { + if filteredEntities[i].TmVdc.Name == name { + return filteredEntities[i], nil + } + } + + return nil, fmt.Errorf("%s no VDC found by name '%s'", ErrorEntityNotFound, name) +} + +func (vcdClient *VCDClient) GetTmVdcById(id string) (*TmVdc, error) { + c := crudConfig{ + entityLabel: labelVdc, + endpoint: types.OpenApiPathVcf + types.OpenApiEndpointTmVdcs, + endpointParams: []string{id}, + } + + outerType := TmVdc{vcdClient: vcdClient} + return getOuterEntity(&vcdClient.Client, outerType, c) +} + +func (o *TmVdc) Update(TmVdcConfig *types.TmVdc) (*TmVdc, error) { + c := crudConfig{ + entityLabel: labelVdc, + endpoint: types.OpenApiPathVcf + types.OpenApiEndpointTmVdcs, + endpointParams: []string{o.TmVdc.ID}, + } + outerType := TmVdc{vcdClient: o.vcdClient} + return updateOuterEntity(&o.vcdClient.Client, outerType, c, TmVdcConfig) +} + +func (o *TmVdc) Delete() error { + c := crudConfig{ + entityLabel: labelVdc, + endpoint: types.OpenApiPathVcf + types.OpenApiEndpointTmVdcs, + endpointParams: []string{o.TmVdc.ID}, + } + return deleteEntityById(&o.vcdClient.Client, c) +} + +func (o *TmVdc) Disable() error { + o.TmVdc.IsEnabled = false + _, err := o.Update(o.TmVdc) + return err +} diff --git a/types/v56/constants.go b/types/v56/constants.go index 3a238931b..98fb8543c 100644 --- a/types/v56/constants.go +++ b/types/v56/constants.go @@ -384,6 +384,7 @@ const ( // These constants allow constructing OpenAPI endpoint paths and avoid strings in code for easy replacement in the // future. const ( + OpenApiPathVcf = "vcf/" OpenApiPathVersion1_0_0 = "1.0.0/" OpenApiPathVersion2_0_0 = "2.0.0/" OpenApiEndpointRoles = "roles/" @@ -517,6 +518,12 @@ const ( // OpenAPI Org OpenApiEndpointOrgs = "orgs/" + + OpenApiEndpointRegions = "regions/" + OpenApiEndpointSupervisors = "supervisors/" + OpenApiEndpointSupervisorZones = "zones/" + OpenApiEndpointNsxManagers = "nsxManagers/" + OpenApiEndpointTmVdcs = "virtualDatacenters/" ) // Header keys to run operations in tenant context diff --git a/types/v56/tm.go b/types/v56/tm.go new file mode 100644 index 000000000..c2469381e --- /dev/null +++ b/types/v56/tm.go @@ -0,0 +1,138 @@ +package types + +type Supervisor struct { + // The immutable identifier of this supervisor. + SupervisorID string `json:"supervisorId"` + // The name of this supervisor. + Name string `json:"name"` + // The Region this Supervisor is associated with. If null, it has not been associated with a Region. + Region *OpenApiReference `json:"region,omitempty"` + // The vCenter this supervisor is associated with. + VirtualCenter *OpenApiReference `json:"virtualCenter"` +} + +type SupervisorZone struct { + ID string `json:"id"` + // The name of this zone. + Name string `json:"name"` + // The supervisor this zone belongs to. + Supervisor *OpenApiReference `json:"supervisor"` + // The vCenter this supervisor zone is associated with. + VirtualCenter *OpenApiReference `json:"virtualCenter"` +} + +type Region struct { + ID string `json:"id,omitempty"` + // The name of the region. It must follow RFC 1123 Label Names to conform with Kubernetes standards. + Name string `json:"name"` + // The NSX manager for the region. + NsxManager *OpenApiReference `json:"nsxManager"` + // Total CPU resources in MHz available to this Region. + CPUCapacityMHz int `json:"cpuCapacityMHz,omitempty"` + // Total CPU reservation resources in MHz available to this Region. + CPUReservationCapacityMHz int `json:"cpuReservationCapacityMHz,omitempty"` + // The description of the region. + Description string `json:"description,omitempty"` + // Whether the region is enabled or not. + IsEnabled bool `json:"isEnabled,omitempty"` + // Total memory resources (in mebibytes) available to this Region. + MemoryCapacityMiB int `json:"memoryCapacityMiB,omitempty"` + // Total memory reservation resources (in mebibytes) available to this Region. + MemoryReservationCapacityMiB int `json:"memoryReservationCapacityMiB,omitempty"` + // The creation status of the Provider VDC. Possible values are READY, NOT_READY, ERROR, FAILED. + // A Region needs to be ready and enabled to be usable. + Status string `json:"status,omitempty"` + // A list of supervisors in a region + + Supervisors []OpenApiReference `json:"supervisors,omitempty"` + // A list of distinct vCenter storage policy names from the vCenters taking part in this region. + // A storage policy with the given name must exist in all the vCenters of this region otherwise + // it will not be accepted. Only the storage policies added to a region can be published to the + // tenant Virtual Datacenters. + StoragePolicies []string `json:"storagePolicies,omitempty"` +} + +type TmOrg struct { + ID string `json:"id,omitempty"` + // Name of organization that will be used in the URL slug + Name string `json:"name"` + // DisplayName contains a full display name of the organization + DisplayName string `json:"displayName"` + // Description of the Org + Description string `json:"description,omitempty"` + + // CanManageOrgs sets whether or not this org can manage other tenant orgs. + // This can be toggled to true to automatically perform the following steps: + // * Publishes the Default Sub-Provider Entitlement Rights Bundle to the org + // * Publishes the Sub-Provider Administrator global role (if it exists) to the org + // * Creates a Default Rights Bundle in the org containing all publishable rights that are + // currently published to the org. Marks that Rights Bundle as publish all. + // * Clones all default roles currently published to the org into Global Roles in the org. Marks + // them all publish all + // Cannot be set to false as there may be any number of Rights Bundles granting sub-provider + // rights to this org. Instead, unpublish any rights bundles that have the Org Traverse right + // from this org + CanManageOrgs bool `json:"canManageOrgs,omitempty"` + // CanPublish defines whether the organization can publish catalogs externally + CanPublish bool `json:"canPublish,omitempty"` + // CatalogCount withing the Org + CatalogCount int `json:"catalogCount,omitempty"` + // DirectlyManagedOrgCount contains the count of the orgs this org directly manages + DirectlyManagedOrgCount int `json:"directlyManagedOrgCount,omitempty"` + // DiskCount defines the number of disks in the Org + DiskCount int `json:"diskCount,omitempty"` + + // IsClassicTenant defines whether the organization is a classic VRA-style tenant. This field + // cannot be updated. Note this style is deprecated and this field exists for the purpose of VRA + // backwards compatibility. + IsClassicTenant bool `json:"isClassicTenant,omitempty"` + // IsEnabled defines if the Org is enabled + IsEnabled bool `json:"isEnabled,omitempty"` + // ManagedBy defines the provider Org that manages this Organization + ManagedBy *OpenApiReference `json:"managedBy,omitempty"` + // MaskedEventTaskUsername sets username as it appears in the tenant events/tasks. Requires + // 'Organization Edit Username Mask' + MaskedEventTaskUsername string `json:"maskedEventTaskUsername,omitempty"` + // OrgVdcCount contains count of VDCs assigned to the Org + OrgVdcCount int `json:"orgVdcCount,omitempty"` + // RunningVMCount contains count of VM running in the Org + RunningVMCount int `json:"runningVMCount,omitempty"` + // UserCount contains user count in the Org + UserCount int `json:"userCount,omitempty"` + // VappCount contains vApp count in the Org + VappCount int `json:"vappCount,omitempty"` +} + +type TmVdc struct { + ID string `json:"id,omitempty"` + // Name of the VDC + Name string `json:"name"` + // Description of the VDC + Description string `json:"description"` + // IsEnabled defines if the VDC is enabled + IsEnabled bool `json:"isEnabled"` + // Org reference + Org *OpenApiReference `json:"org"` + // Region reference + Region *OpenApiReference `json:"region"` + // Status contains creation status of the VDC + Status string `json:"status"` + // Supervisors contain references to Supervisors + Supervisors []OpenApiReference `json:"supervisors,omitempty"` + // ZoneResourceAllocation contain references of each zone within Supervisor + ZoneResourceAllocation []*TmVdcZoneResourceAllocation `json:"zoneResourceAllocation,omitempty"` +} + +// TmVdcZoneResourceAllocation defines resource allocation for a single zone +type TmVdcZoneResourceAllocation struct { + ResourceAllocation TmVdcResourceAllocation `json:"resourceAllocation"` + Zone *OpenApiReference `json:"zone"` +} + +// TmVdcResourceAllocation defines compute resources of single VDC +type TmVdcResourceAllocation struct { + CPULimitMHz int `json:"cpuLimitMHz"` + CPUReservationMHz int `json:"cpuReservationMHz"` + MemoryLimitMiB int `json:"memoryLimitMiB"` + MemoryReservationMiB int `json:"memoryReservationMiB"` +} From e9d165c97a14c59243641b63cdd2167d4476cc95 Mon Sep 17 00:00:00 2001 From: Dainius Serplis Date: Tue, 1 Oct 2024 15:29:21 +0300 Subject: [PATCH 2/4] Add changelog Signed-off-by: Dainius Serplis --- .changes/v3.0.0/710-features.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .changes/v3.0.0/710-features.md diff --git a/.changes/v3.0.0/710-features.md b/.changes/v3.0.0/710-features.md new file mode 100644 index 000000000..5d26c5369 --- /dev/null +++ b/.changes/v3.0.0/710-features.md @@ -0,0 +1,15 @@ +* Added `TmOrg` and `types.TmOrg` to structure for OpenAPI management of Organizations with methods + `VCDClient.CreateTmOrg`, `VCDClient.GetAllTmOrgs`, `VCDClient.GetTmOrgByName`, + `VCDClient.GetTmOrgById`, `TmOrg.Update`, `TmOrg.Delete`, `.TmOrg.Disable` [GH-710] +* Added `TmVdc` and `types.TmVdc` to structure for OpenAPI management of Organizations VDCs with + methods `VCDClient.CreateTmVdc`, `VCDClient.GetAllTmVdcs`, `VCDClient.GetTmVdcByName`, + `VCDClient.GetTmVdcById`, `TmVdc.Update`, `TmVdc.Delete`, `.TmVdc.Disable` [GH-710] +* Added types `Region` and `types.Region` for managing regions with methods + `VCDClient.CreateRegion`, `VCDClient.GetAllRegions`, `VCDClient.GetRegionByName`, + `VCDClient.GetRegionById`, `Region.Update`, `Region.Delete` [GH-710] +* Added types `Supervisor` and `types.Supervisor` for reading Supervisors + `VCDClient.GetAllSupervisors`, `VCDClient.GetSupervisorById`, `VCDClient.GetSupervisorByName` + [GH-710] +* Added types `SupervisorZone` and `types.SupervisorZone` for reading Supervisors + `VCDClient.GetAllSupervisorZones`, `VCDClient.GetSupervisorZoneById`, + `VCDClient.GetSupervisorZoneByName` [GH-710] From 62ac23ba8de467cf6907a4b2cf04e7a3d46533ad Mon Sep 17 00:00:00 2001 From: Dainius Serplis Date: Wed, 2 Oct 2024 07:59:28 +0300 Subject: [PATCH 3/4] Add some missing field docs Signed-off-by: Dainius Serplis --- types/v56/tm.go | 1 + 1 file changed, 1 insertion(+) diff --git a/types/v56/tm.go b/types/v56/tm.go index c2469381e..773c6cbe3 100644 --- a/types/v56/tm.go +++ b/types/v56/tm.go @@ -103,6 +103,7 @@ type TmOrg struct { VappCount int `json:"vappCount,omitempty"` } +// TmVdc defines a structure for creating VDCs using OpenAPI endpoint type TmVdc struct { ID string `json:"id,omitempty"` // Name of the VDC From 5ac4de2b927612034ce3d88e307da87604e9fc71 Mon Sep 17 00:00:00 2001 From: Dainius Serplis Date: Wed, 2 Oct 2024 09:28:25 +0300 Subject: [PATCH 4/4] Self review, improve tests Signed-off-by: Dainius Serplis --- govcd/api_vcd_test.go | 7 ++++++- govcd/tm_region_test.go | 31 +++++++++++++++++++++++++++++++ govcd/tm_vdc_test.go | 31 +++++++++++++++++++++++++++++++ types/v56/tm.go | 1 + 4 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 govcd/tm_region_test.go create mode 100644 govcd/tm_vdc_test.go diff --git a/govcd/api_vcd_test.go b/govcd/api_vcd_test.go index 313354284..fa981fd43 100644 --- a/govcd/api_vcd_test.go +++ b/govcd/api_vcd_test.go @@ -141,7 +141,12 @@ type TestConfig struct { HttpTimeout int64 `yaml:"httpTimeout,omitempty"` } Tenants []Tenant `yaml:"tenants,omitempty"` - VCD struct { + Tm struct { + Org string `yaml:"org"` + Region string `yaml:"region"` + Vdc string `yaml:"vdc"` + } `yaml:"tm"` + VCD struct { Org string `yaml:"org"` Vdc string `yaml:"vdc"` ProviderVdc struct { diff --git a/govcd/tm_region_test.go b/govcd/tm_region_test.go new file mode 100644 index 000000000..85036447d --- /dev/null +++ b/govcd/tm_region_test.go @@ -0,0 +1,31 @@ +//go:build tm || functional || ALL + +/* + * Copyright 2024 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. + */ + +package govcd + +import ( + . "gopkg.in/check.v1" +) + +func (vcd *TestVCD) Test_TmRegion(check *C) { + skipNonTm(vcd, check) + sysadminOnly(vcd, check) + + region, err := vcd.client.GetRegionByName(vcd.config.Tm.Region) + check.Assert(err, IsNil) + check.Assert(region, NotNil) + + // Get by ID + regionById, err := vcd.client.GetRegionById(region.Region.ID) + check.Assert(err, IsNil) + check.Assert(regionById, NotNil) + + check.Assert(region.Region, DeepEquals, regionById.Region) + + allTmVdc, err := vcd.client.GetAllRegions(nil) + check.Assert(err, IsNil) + check.Assert(len(allTmVdc) > 0, Equals, true) +} diff --git a/govcd/tm_vdc_test.go b/govcd/tm_vdc_test.go new file mode 100644 index 000000000..b9a477ba3 --- /dev/null +++ b/govcd/tm_vdc_test.go @@ -0,0 +1,31 @@ +//go:build tm || functional || ALL + +/* + * Copyright 2024 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. + */ + +package govcd + +import ( + . "gopkg.in/check.v1" +) + +func (vcd *TestVCD) Test_TmVdc(check *C) { + skipNonTm(vcd, check) + sysadminOnly(vcd, check) + + vdc, err := vcd.client.GetTmVdcByName(vcd.config.Tm.Vdc) + check.Assert(err, IsNil) + check.Assert(vdc, NotNil) + + // Get by ID + vdcById, err := vcd.client.GetTmVdcById(vdc.TmVdc.ID) + check.Assert(err, IsNil) + check.Assert(vdcById, NotNil) + + check.Assert(vdc.TmVdc, DeepEquals, vdcById.TmVdc) + + allTmVdc, err := vcd.client.GetAllTmVdcs(nil) + check.Assert(err, IsNil) + check.Assert(len(allTmVdc) > 0, Equals, true) +} diff --git a/types/v56/tm.go b/types/v56/tm.go index 773c6cbe3..bb1223641 100644 --- a/types/v56/tm.go +++ b/types/v56/tm.go @@ -52,6 +52,7 @@ type Region struct { StoragePolicies []string `json:"storagePolicies,omitempty"` } +// TmOrg defines structure for creating type TmOrg struct { ID string `json:"id,omitempty"` // Name of organization that will be used in the URL slug