Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TM IP Spaces #724

Merged
merged 11 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .changes/v3.0.0/714-features.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
[GH-714]
* vCenter management types `VCenter` and `types.VSphereVirtualCenter` adds Create, Update and Delete
methods: `VCDClient.CreateVcenter`, `VCDClient.GetAllVCenters`, `VCDClient.GetVCenterByName`,
`VCDClient.GetVCenterById`, `VCenter.Update`, `VCenter.Delete`, `VCenter.Refresh` [GH-714]
`VCDClient.GetVCenterById`, `VCenter.Update`, `VCenter.Delete`, `VCenter.RefreshVcenter`,
`VCenter.Refresh` [GH-714, GH-1360]
* Add NSX-T Manager management types `NsxtManagerOpenApi`, `types.NsxtManagerOpenApi` and methods
`VCDClient.CreateNsxtManagerOpenApi`, `VCDClient.GetAllNsxtManagersOpenApi`,
`VCDClient.GetNsxtManagerOpenApiById`, `VCDClient.GetNsxtManagerOpenApiByName`,
Expand Down
2 changes: 1 addition & 1 deletion .changes/v3.0.0/717-features.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
* Added `ContentLibraryItem` and `types.ContentLibraryItem` structures to manage Content Library Items
with methods `ContentLibrary.CreateContentLibraryItem`, `ContentLibrary.GetAllContentLibraryItems`,
`ContentLibrary.GetContentLibraryItemByName`, `ContentLibrary.GetContentLibraryItemById`, `ContentLibraryItem.Update`,
`ContentLibraryItem.Delete`, `VCDClient.GetContentLibraryItemById` [GH-717]
`ContentLibraryItem.Delete`, `VCDClient.GetContentLibraryItemById` [GH-717, GH-724]
2 changes: 1 addition & 1 deletion .changes/v3.0.0/718-features.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[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
* Added `Region` and `types.Region` structures 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
Expand Down
4 changes: 4 additions & 0 deletions .changes/v3.0.0/724-features.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
* Added `TmIpSpace` and `types.TmIpSpace` structures for OpenAPI management of TM IP Spaces with
methods `VCDClient.CreateTmIpSpace`, `VCDClient.GetAllTmIpSpaces`, `VCDClient.GetTmIpSpaceByName`,
`VCDClient.GetTmIpSpaceById`, `VCDClient.GetTmIpSpaceByNameAndRegionId`, `TmIpSpace.Update`,
`TmIpSpace.Delete` [GH-724]
1 change: 1 addition & 0 deletions govcd/openapi_endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ var endpointMinApiVersions = map[string]string{
types.OpenApiPathVcf + types.OpenApiEndpointSupervisorZones: "40.0",
types.OpenApiPathVcf + types.OpenApiEndpointZones: "40.0",
types.OpenApiPathVcf + types.OpenApiEndpointTmVdcs: "40.0",
types.OpenApiPathVcf + types.OpenApiEndpointTmIpSpaces: "40.0",
}

// endpointElevatedApiVersions endpoint elevated API versions
Expand Down
2 changes: 1 addition & 1 deletion govcd/tm_common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func getOrCreateVCenter(vcd *TestVCD, check *C) (*VCenter, func()) {
time.Sleep(1 * time.Minute) // TODO: TM: Reevaluate need for sleep
// Refresh connected vCenter to be sure that all artifacts are loaded
printVerbose("# Refreshing vCenter %s\n", vc.VSphereVCenter.Url)
err = vc.Refresh()
err = vc.RefreshVcenter()
check.Assert(err, IsNil)

printVerbose("# Refreshing Storage Profiles in vCenter %s\n", vc.VSphereVCenter.Url)
Expand Down
2 changes: 1 addition & 1 deletion govcd/tm_content_library_item.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ func uploadContentLibraryItemFile(name string, cli *ContentLibraryItem, filesToU
}()

ud := uploadDetails{
uploadLink: strings.ReplaceAll(fileToUpload.TransferUrl, "/transfer", "/tm/transfer"), // TODO: TM: Workaround, the link is missing /tm in path, so it gives 404 as-is
uploadLink: fileToUpload.TransferUrl,
uploadedBytes: 0,
fileSizeToUpload: fileToUpload.ExpectedSizeBytes,
uploadPieceSize: args.UploadPieceSize,
Expand Down
125 changes: 125 additions & 0 deletions govcd/tm_ip_space.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package govcd

import (
"fmt"
"net/url"

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

const labelTmIpSpace = "TM IP Space"

// TmIpSpace provides configuration of mainly the external IP Prefixes that specifies
// the accessible external networks from the data center
type TmIpSpace struct {
TmIpSpace *types.TmIpSpace
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 TmIpSpace) wrap(inner *types.TmIpSpace) *TmIpSpace {
g.TmIpSpace = inner
return &g
}

// CreateTmIpSpace creates a TM IP Space with a given configuration
func (vcdClient *VCDClient) CreateTmIpSpace(config *types.TmIpSpace) (*TmIpSpace, error) {
c := crudConfig{
entityLabel: labelTmIpSpace,
endpoint: types.OpenApiPathVcf + types.OpenApiEndpointTmIpSpaces,
}
outerType := TmIpSpace{vcdClient: vcdClient}
return createOuterEntity(&vcdClient.Client, outerType, c, config)
}

// GetAllTmIpSpaces fetches all TM IP Spaces with an optional query filter
func (vcdClient *VCDClient) GetAllTmIpSpaces(queryParameters url.Values) ([]*TmIpSpace, error) {
c := crudConfig{
entityLabel: labelTmIpSpace,
endpoint: types.OpenApiPathVcf + types.OpenApiEndpointTmIpSpaces,
queryParameters: queryParameters,
}

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

// GetTmIpSpaceByName retrieves TM IP Spaces with a given name
func (vcdClient *VCDClient) GetTmIpSpaceByName(name string) (*TmIpSpace, error) {
if name == "" {
return nil, fmt.Errorf("%s lookup requires name", labelTmIpSpace)
}

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

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

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

return vcdClient.GetTmIpSpaceById(singleEntity.TmIpSpace.ID)
}

// GetTmIpSpaceById retrieves an exact IP Space with a given ID
func (vcdClient *VCDClient) GetTmIpSpaceById(id string) (*TmIpSpace, error) {
c := crudConfig{
entityLabel: labelTmIpSpace,
endpoint: types.OpenApiPathVcf + types.OpenApiEndpointTmIpSpaces,
endpointParams: []string{id},
}

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

// GetTmIpSpaceByNameAndRegionId retrieves TM IP Spaces with a given name in a provided Region
func (vcdClient *VCDClient) GetTmIpSpaceByNameAndRegionId(name, regionId string) (*TmIpSpace, error) {
if name == "" || regionId == "" {
return nil, fmt.Errorf("%s lookup requires name and Region ID", labelTmIpSpace)
}

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

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

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

return vcdClient.GetTmIpSpaceById(singleEntity.TmIpSpace.ID)
}

// Update TM IP Space
func (o *TmIpSpace) Update(TmIpSpaceConfig *types.TmIpSpace) (*TmIpSpace, error) {
c := crudConfig{
entityLabel: labelTmIpSpace,
endpoint: types.OpenApiPathVcf + types.OpenApiEndpointTmIpSpaces,
endpointParams: []string{o.TmIpSpace.ID},
}
outerType := TmIpSpace{vcdClient: o.vcdClient}
return updateOuterEntity(&o.vcdClient.Client, outerType, c, TmIpSpaceConfig)
}

// Delete TM IP Space
func (o *TmIpSpace) Delete() error {
c := crudConfig{
entityLabel: labelTmIpSpace,
endpoint: types.OpenApiPathVcf + types.OpenApiEndpointTmIpSpaces,
endpointParams: []string{o.TmIpSpace.ID},
}
return deleteEntityById(&o.vcdClient.Client, c)
}
88 changes: 88 additions & 0 deletions govcd/tm_ip_space_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//go:build tm || functional || ALL

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

package govcd

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

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

vc, vcCleanup := getOrCreateVCenter(vcd, check)
defer vcCleanup()
nsxtManager, nsxtManagerCleanup := getOrCreateNsxtManager(vcd, check)
defer nsxtManagerCleanup()
supervisor, err := vc.GetSupervisorByName(vcd.config.Tm.VcenterSupervisor)
check.Assert(err, IsNil)
region, regionCleanup := getOrCreateRegion(vcd, nsxtManager, supervisor, check)
defer regionCleanup()

ipSpaceType := &types.TmIpSpace{
Name: check.TestName(),
RegionRef: types.OpenApiReference{ID: region.Region.ID},
Description: check.TestName(),
DefaultQuota: types.TmIpSpaceDefaultQuota{
MaxCidrCount: 3,
MaxIPCount: -1,
MaxSubnetSize: 24,
},
ExternalScopeCidr: "12.12.0.0/30",
InternalScopeCidrBlocks: []types.TmIpSpaceInternalScopeCidrBlocks{
{
Cidr: "10.0.0.0/24",
},
},
}

createdIpSpace, err := vcd.client.CreateTmIpSpace(ipSpaceType)
check.Assert(err, IsNil)
check.Assert(createdIpSpace, NotNil)
// Add to cleanup list
PrependToCleanupListOpenApi(createdIpSpace.TmIpSpace.ID, check.TestName(), types.OpenApiPathVcf+types.OpenApiEndpointTmIpSpaces+createdIpSpace.TmIpSpace.ID)
defer func() {
err = createdIpSpace.Delete()
check.Assert(err, IsNil)
}()

// Get TM VDC By Name
byName, err := vcd.client.GetTmIpSpaceByName(ipSpaceType.Name)
check.Assert(err, IsNil)
check.Assert(byName.TmIpSpace, DeepEquals, createdIpSpace.TmIpSpace)

// Get TM VDC By Id
byId, err := vcd.client.GetTmIpSpaceById(createdIpSpace.TmIpSpace.ID)
check.Assert(err, IsNil)
check.Assert(byId.TmIpSpace, DeepEquals, createdIpSpace.TmIpSpace)

// Get By Name and Org ID
byNameAndOrgId, err := vcd.client.GetTmIpSpaceByNameAndRegionId(createdIpSpace.TmIpSpace.Name, region.Region.ID)
check.Assert(err, IsNil)
check.Assert(byNameAndOrgId.TmIpSpace, DeepEquals, createdIpSpace.TmIpSpace)

// Get By Name and Org ID in non existent Region
byNameAndInvalidOrgId, err := vcd.client.GetTmIpSpaceByNameAndRegionId(createdIpSpace.TmIpSpace.Name, "urn:vcloud:region:a93c9db9-0000-0000-0000-a8f7eeda85f9")
check.Assert(err, NotNil)
check.Assert(byNameAndInvalidOrgId, IsNil)

// Not Found tests
byNameInvalid, err := vcd.client.GetTmIpSpaceByName("fake-name")
check.Assert(ContainsNotFound(err), Equals, true)
check.Assert(byNameInvalid, IsNil)

byIdInvalid, err := vcd.client.GetTmIpSpaceById("urn:vcloud:ipSpace:5344b964-0000-0000-0000-d554913db643")
check.Assert(ContainsNotFound(err), Equals, true)
check.Assert(byIdInvalid, IsNil)

// Update
createdIpSpace.TmIpSpace.Name = check.TestName() + "-update"
updatedVdc, err := createdIpSpace.Update(createdIpSpace.TmIpSpace)
check.Assert(err, IsNil)
check.Assert(updatedVdc.TmIpSpace, DeepEquals, createdIpSpace.TmIpSpace)
}
20 changes: 18 additions & 2 deletions govcd/vsphere_vcenter.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,26 @@ func (v VCenter) GetVimServerUrl() (string, error) {
return url.JoinPath(v.client.Client.rootVcdHref(), "api", "admin", "extension", "vimServer", extractUuid(v.VSphereVCenter.VcId))
}

// Refresh triggers a refresh operation on vCenter that syncs up vCenter components such as
// Refresh vCenter structure
func (v *VCenter) Refresh() error {
// Retrieval endpoints by Name and by ID return differently formated url (the by Id one returns
// URL with port http://host:443, while the one by name - doesn't). Using the same getByName to
// match format everywhere

// newVcenter, err := v.client.GetVCenterById(v.VSphereVCenter.VcId)
newVcenter, err := v.client.GetVCenterByName(v.VSphereVCenter.Name) // TODO: TM: use above retrieval by ID
if err != nil {
return fmt.Errorf("error refreshing vCenter: %s", err)
}

v.VSphereVCenter = newVcenter.VSphereVCenter
return nil
}

// RefreshVcenter triggers a refresh operation on vCenter that syncs up vCenter components such as
// supervisors
// It uses legacy endpoint as there is no OpenAPI endpoint for this operation
func (v *VCenter) Refresh() error {
func (v *VCenter) RefreshVcenter() error {
refreshUrl, err := url.JoinPath(v.client.Client.rootVcdHref(), "api", "admin", "extension", "vimServer", extractUuid(v.VSphereVCenter.VcId), "action", "refresh")
if err != nil {
return fmt.Errorf("error building refresh path: %s", err)
Expand Down
5 changes: 4 additions & 1 deletion govcd/vsphere_vcenter_tm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,13 @@ func (vcd *TestVCD) Test_VCenter(check *C) {
check.Assert(err, IsNil)
check.Assert(v, NotNil)

err = v.Refresh()
check.Assert(err, IsNil)

// Add to cleanup list
PrependToCleanupListOpenApi(v.VSphereVCenter.VcId, check.TestName(), types.OpenApiPathVersion1_0_0+types.OpenApiEndpointVirtualCenters+v.VSphereVCenter.VcId)

err = v.Refresh()
err = v.RefreshVcenter()
check.Assert(err, IsNil)

// Get By Name
Expand Down
1 change: 1 addition & 0 deletions types/v56/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,7 @@ const (
OpenApiEndpointSupervisorZones = "supervisorZones/"
OpenApiEndpointZones = "zones/"
OpenApiEndpointTmVdcs = "virtualDatacenters/"
OpenApiEndpointTmIpSpaces = "ipSpaces/"
)

// Header keys to run operations in tenant context
Expand Down
61 changes: 61 additions & 0 deletions types/v56/tm.go
Original file line number Diff line number Diff line change
Expand Up @@ -291,3 +291,64 @@ type Zone struct {
// Providers, this value represents the total given to all Tenants
MemoryUsedMiB int `json:"memoryUsedMiB"`
}

// TmIpSpace provides configuration of mainly the external IP Prefixes that specifies
// the accessible external networks from the data center
type TmIpSpace struct {
ID string `json:"id,omitempty"`
// Name of the IP Space
Name string `json:"name"`
// Description of the IP Space
Description string `json:"description,omitempty"`
// RegionRef is the region that this IP Space belongs in. Only Provider Gateways in the same Region can be
// associated with this IP Space. This field cannot be updated
RegionRef OpenApiReference `json:"regionRef"`
// Default IP quota that applies to all the organizations the Ip Space is assigned to
DefaultQuota TmIpSpaceDefaultQuota `json:"defaultQuota,omitempty"`
// ExternalScopeCidr defines the total span of IP addresses to which the IP space has access.
// This typically defines the span of IP addresses outside the bounds of a Data Center. For the
// internet, this may be 0.0.0.0/0. For a WAN, this could be 10.0.0.0/8.
ExternalScopeCidr string `json:"externalScopeCidr,omitempty"`
// InternalScopeCidrBlocks defines the span of IP addresses used within a Data Center. For new
// CIDR value not in the existing list, a new IP Block will be created. For existing CIDR value,
// the IP Block's name can be updated. If an existing CIDR value is removed from the list, the
// the IP Block is removed from the IP Space.
InternalScopeCidrBlocks []TmIpSpaceInternalScopeCidrBlocks `json:"internalScopeCidrBlocks,omitempty"`
// Represents current status of the networking entity. Possible values are:
// * PENDING - Desired entity configuration has been received by system and is pending realization.
// * CONFIGURING - The system is in process of realizing the entity.
// * REALIZED - The entity is successfully realized in the system.
// * REALIZATION_FAILED - There are some issues and the system is not able to realize the entity.
// * UNKNOWN - Current state of entity is unknown.
Status string `json:"status,omitempty"`
}

// IP Space quota defines the maximum number of IPv4 IPs and CIDRs that can be allocated and used by
// the IP Space across all its Internal Scopes
type TmIpSpaceDefaultQuota struct {
// The maximum number of CIDRs with size maxSubnetSize or less, that can be allocated from all
// the Internal Scopes of the IP Space. A '-1' value means no cap on the number of the CIDRs
// used
MaxCidrCount int `json:"maxCidrCount,omitempty"`
// The maximum number of single floating IP addresses that can be allocated and used from all
// the Internal Scopes of the IP Space. A '-1' value means no cap on the number of floating IP
// Addresses
MaxIPCount int `json:"maxIpCount,omitempty"`
// The maximum size of the subnets, represented as a prefix length. The CIDRs that are allocated
// from the Internal Scopes of the IP Space must be smaller or equal to the specified size. For
// example, for a maxSubnetSize of 24, CIDRs with prefix length of 24, 28 or 30 can be
// allocated
MaxSubnetSize int `json:"maxSubnetSize,omitempty"`
}

// An IP Block represents a named CIDR that is backed by a network provider
type TmIpSpaceInternalScopeCidrBlocks struct {
// Unique backing ID of the IP Block. This is not a Tenant Manager URN. This field is read-only and is ignored on create/update
ID string `json:"id,omitempty"`
// The name of the IP Block. If not set, a random name will be generated that will be prefixed
// with the name of the IP Space. This property is updatable if there's an existing IP Block
// with the CIDR value
Name string `json:"name,omitempty"`
// The CIDR that represents this IP Block. This property is not updatable
Cidr string `json:"cidr,omitempty"`
}
Loading