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

IP Spaces CRUD #578

Merged
merged 10 commits into from
Jun 20, 2023
Merged
Show file tree
Hide file tree
Changes from 5 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: 3 additions & 0 deletions .changes/v2.21.0/578-features.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
* Added IP Space CRUD support via `IpSpace` and `types.IpSpace` and `VCDClient.CreateIpSpace`,
`VCDClient.GetAllIpSpaceSummaries`, `VCDClient.GetIpSpaceById`, `VCDClient.GetIpSpaceByName`,
`VCDClient.GetIpSpaceByNameAndOrgId`, `IpSpace.Update`, `IpSpace.Delete` [GH-578]
227 changes: 227 additions & 0 deletions govcd/ip_space.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
/*
* Copyright 2023 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"
)

// IpSpace provides structured approach to allocating public and private IP addresses by preventing
// the use of overlapping IP addresses across organizations and organization VDCs.
//
// An IP space consists of a set of defined non-overlapping IP ranges and small CIDR blocks that are
// reserved and used during the consumption aspect of the IP space life cycle. An IP space can be
// either IPv4 or IPv6, but not both.
//
// Every IP space has an internal scope and an external scope. The internal scope of an IP space is
// a list of CIDR notations that defines the exact span of IP addresses in which all ranges and
// blocks must be contained in. The external scope defines the total span of IP addresses to which
// the IP space has access, for example the internet or a WAN.
type IpSpace struct {
IpSpace *types.IpSpace
vcdClient *VCDClient
}

// CreateIpSpace creates IP Space with desired configuration
func (vcdClient *VCDClient) CreateIpSpace(ipSpaceConfig *types.IpSpace) (*IpSpace, error) {
client := vcdClient.Client
endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointIpSpaces
apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint)
if err != nil {
return nil, err
}

urlRef, err := client.OpenApiBuildEndpoint(endpoint)
if err != nil {
return nil, err
}

result := &IpSpace{
IpSpace: &types.IpSpace{},
vcdClient: vcdClient,
}

err = client.OpenApiPostItem(apiVersion, urlRef, nil, ipSpaceConfig, result.IpSpace, nil)
if err != nil {
return nil, err
}

return result, nil
}

// GetAllIpSpaceSummaries retrieve summaries of all IP Spaces with an optional filter
// Note. There is no API endpoint to get multiple IP Spaces with their full definitions. Only
// "summaries" endpoint exists, but it does not include all fields. To retrieve complete structure
// one can use `GetIpSpaceById` or `GetIpSpaceByName`
func (vcdClient *VCDClient) GetAllIpSpaceSummaries(queryParameters url.Values) ([]*IpSpace, error) {
client := vcdClient.Client
endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointIpSpaceSummaries
apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint)
if err != nil {
return nil, err
}

urlRef, err := client.OpenApiBuildEndpoint(endpoint)
if err != nil {
return nil, err
}

typeResponses := []*types.IpSpace{{}}
err = client.OpenApiGetAllItems(apiVersion, urlRef, queryParameters, &typeResponses, nil)
if err != nil {
return nil, err
}

// Wrap all typeResponses into DefinedEntityType types with client
adezxc marked this conversation as resolved.
Show resolved Hide resolved
results := make([]*IpSpace, len(typeResponses))
for sliceIndex := range typeResponses {
results[sliceIndex] = &IpSpace{
IpSpace: typeResponses[sliceIndex],
vcdClient: vcdClient,
}
}

return results, nil
}

// GetIpSpaceById retrieves IP Space with a given ID
func (vcdClient *VCDClient) GetIpSpaceById(id string) (*IpSpace, error) {
if id == "" {
return nil, fmt.Errorf("IP Space lookup requires ID")
}

client := vcdClient.Client
endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointIpSpaces
apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint)
if err != nil {
return nil, err
}

urlRef, err := client.OpenApiBuildEndpoint(endpoint, id)
if err != nil {
return nil, err
}

response := &IpSpace{
vcdClient: vcdClient,
IpSpace: &types.IpSpace{},
}

err = client.OpenApiGetItem(apiVersion, urlRef, nil, response.IpSpace, nil)
if err != nil {
return nil, err
}

return response, nil
}

// GetIpSpaceByName retrieves IP Space with a given name
// Note. It will return an error if multiple IP Spaces exist with the same name
func (vcdClient *VCDClient) GetIpSpaceByName(name string) (*IpSpace, error) {
if name == "" {
return nil, fmt.Errorf("IP Space lookup requires name")
}

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

filteredIpSpaces, err := vcdClient.GetAllIpSpaceSummaries(queryParameters)
if err != nil {
return nil, fmt.Errorf("error getting IP Spaces: %s", err)
}

singleIpSpace, err := oneOrError("name", name, filteredIpSpaces)
if err != nil {
return nil, fmt.Errorf("error ")
}

return vcdClient.GetIpSpaceById(singleIpSpace.IpSpace.ID)
}

// GetIpSpaceByNameAndOrgId retrieves IP Space with a given name in a particular Org
// Note. Only PRIVATE IP spaces belong to Orgs
func (vcdClient *VCDClient) GetIpSpaceByNameAndOrgId(name, orgId string) (*IpSpace, error) {
if name == "" {
Didainius marked this conversation as resolved.
Show resolved Hide resolved
return nil, fmt.Errorf("IP Space lookup requires name")
}

queryParameters := url.Values{}
queryParameters.Add("filter", "name=="+name)
queryParameters = queryParameterFilterAnd("orgRef.id=="+orgId, queryParameters)

filteredIpSpaces, err := vcdClient.GetAllIpSpaceSummaries(queryParameters)
if err != nil {
return nil, fmt.Errorf("error getting IP Spaces: %s", err)
}

singleIpSpace, err := oneOrError("name", name, filteredIpSpaces)
if err != nil {
return nil, fmt.Errorf("error ")
}

return vcdClient.GetIpSpaceById(singleIpSpace.IpSpace.ID)
}

// Update updates IP Space with new config
func (ipSpace *IpSpace) Update(ipSpaceConfig *types.IpSpace) (*IpSpace, error) {
adezxc marked this conversation as resolved.
Show resolved Hide resolved
client := ipSpace.vcdClient.Client
endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointIpSpaces
apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint)
if err != nil {
return nil, err
}

ipSpaceConfig.ID = ipSpace.IpSpace.ID
urlRef, err := client.OpenApiBuildEndpoint(endpoint, ipSpaceConfig.ID)
if err != nil {
return nil, err
}

returnIpSpace := &IpSpace{
IpSpace: &types.IpSpace{},
vcdClient: ipSpace.vcdClient,
}

err = client.OpenApiPutItem(apiVersion, urlRef, nil, ipSpaceConfig, returnIpSpace.IpSpace, nil)
if err != nil {
return nil, fmt.Errorf("error updating IP Space: %s", err)
}

return returnIpSpace, nil

}

// Delete deletes IP Space
func (ipSpace *IpSpace) Delete() error {
if ipSpace == nil || ipSpace.IpSpace == nil || ipSpace.IpSpace.ID == "" {
return fmt.Errorf("IP Space must have ID")
}

client := ipSpace.vcdClient.Client
endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointIpSpaces
apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint)
if err != nil {
return err
}

urlRef, err := client.OpenApiBuildEndpoint(endpoint, ipSpace.IpSpace.ID)
if err != nil {
return err
}

err = client.OpenApiDeleteItem(apiVersion, urlRef, nil, nil)
if err != nil {
return err
}

if err != nil {
return fmt.Errorf("error deleting IP space: %s", err)
}

return nil
}
Loading