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

Add external network #185

Merged
merged 39 commits into from
May 8, 2019
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
a94bd97
Create and delete for external networks
Feb 19, 2019
0194be4
Fix errors in marshalled xml
Mar 8, 2019
b728d8c
document structs; shuffle methods
Mar 15, 2019
65eb459
Move get by name method to external network struct
Mar 15, 2019
e753b02
make query virtual centers method public
Mar 18, 2019
4d6e3fe
Change import path after rebase with go modules
Mar 22, 2019
94014b4
address feedback
Apr 5, 2019
80bdd28
check more properties in create exnet test
Apr 5, 2019
399b351
Make new method for getting an external network
Apr 9, 2019
8091420
change method name
Apr 10, 2019
f4845c5
change deprecation comment
Apr 10, 2019
2a339ab
Add implementation needed for external network creation
vbauzys Apr 25, 2019
5214fd7
Made Ipscopes support multi scopes
vbauzys Apr 30, 2019
6969c2c
Fix comment and add changelog message
vbauzys Apr 30, 2019
bde21a0
Updated configuration samples
vbauzys Apr 30, 2019
a697b3d
Fix cleanup name
vbauzys May 2, 2019
29f409b
Fix for race check error
vbauzys May 2, 2019
460c1d1
Add check to require sys admin
vbauzys May 2, 2019
5eeac02
Add check missing configuration for tests
vbauzys May 6, 2019
02a9697
Improve comments
vbauzys May 6, 2019
2bbaf9b
Merge branch 'master' into add-external-network
vbauzys May 6, 2019
d24c0bf
Added tags for tests according latest PR
vbauzys May 6, 2019
59e1029
Fix error message
vbauzys May 6, 2019
9f69a35
Add test tags
vbauzys May 6, 2019
d388f3f
Remove extension tag, add missing extnetwork tags
vbauzys May 6, 2019
21074b9
Add elaborated comment
vbauzys May 6, 2019
c102069
Fix groups -> group
vbauzys May 6, 2019
4a78b93
Improve comment
vbauzys May 6, 2019
e8df197
Removed getByName function, added more generic one
vbauzys May 6, 2019
5778d54
Fixed type according vCD API
vbauzys May 6, 2019
17e426f
Fixed race condition
vbauzys May 6, 2019
2a5141e
Fix grammar
vbauzys May 7, 2019
bea812e
Fix comments
vbauzys May 7, 2019
4bdd2f6
Fix mistype
vbauzys May 7, 2019
ada5223
Fix comment
vbauzys May 7, 2019
4a4302a
Move tests from extension
vbauzys May 7, 2019
a548ed9
Moved functions to system.go
vbauzys May 7, 2019
009f59c
Add comment for function and enforce fence mode to creat ext net
vbauzys May 8, 2019
010e2e8
Improve comment
vbauzys May 8, 2019
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

FEATURES:

* Added external network get/create/delete functions
* Added metadata add/remove functions to VM.
* Added ability to do vCD version checks and comparison [#174](https://github.com/vmware/go-vcloud-director/pull/174)
using VCDClient.APIVCDMaxVersionIs(string) and VCDClient.APIClientVersionIs(string).
Expand Down
32 changes: 25 additions & 7 deletions govcd/api_vcd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ const (
TestVMAttachOrDetachDisk = "TestVMAttachOrDetachDisk"
TestVMAttachDisk = "TestVMAttachDisk"
TestVMDetachDisk = "TestVMDetachDisk"
TestCreateExternalNetwork = "TestCreateExternalNetwork"
TestDeleteExternalNetwork = "TestDeleteExternalNetwork"
)

const (
Expand Down Expand Up @@ -88,13 +90,16 @@ type TestConfig struct {
SP1 string `yaml:"storageProfile1"`
SP2 string `yaml:"storageProfile2,omitempty"`
} `yaml:"storageProfile"`
ExternalIp string `yaml:"externalIp,omitempty"`
ExternalNetmask string `yaml:"externalNetmask,omitempty"`
InternalIp string `yaml:"internalIp,omitempty"`
InternalNetmask string `yaml:"internalNetmask,omitempty"`
EdgeGateway string `yaml:"edgeGateway,omitempty"`
ExternalNetwork string `yaml:"externalNetwork,omitempty"`
Disk struct {
ExternalIp string `yaml:"externalIp,omitempty"`
ExternalNetmask string `yaml:"externalNetmask,omitempty"`
InternalIp string `yaml:"internalIp,omitempty"`
InternalNetmask string `yaml:"internalNetmask,omitempty"`
EdgeGateway string `yaml:"edgeGateway,omitempty"`
ExternalNetwork string `yaml:"externalNetwork,omitempty"`
ExternalNetworkPortGroup string `yaml:"externalNetworkPortGroup,omitempty"`
ExternalNetworkPortGroupType string `yaml:"externalNetworkPortGroupType,omitempty"`
VimServer string `yaml:"vimServer,omitempty"`
Disk struct {
Size int `yaml:"size,omitempty"`
SizeForUpdate int `yaml:"sizeForUpdate,omitempty"`
}
Expand Down Expand Up @@ -440,6 +445,19 @@ func (vcd *TestVCD) removeLeftoverEntities(entity CleanupEntity) {
vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
}
return
case "externalNetwork":
externalNetwork, err := GetExternalNetwork(vcd.client, entity.Name)
if err != nil {
vcd.infoCleanup(notFoundMsg, "externalNetwork", entity.Name)
return
}
err = externalNetwork.DeleteWait()
if err == nil {
vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
} else {
vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
}
return
case "mediaImage":
if entity.Parent == "" {
vcd.infoCleanup("removeLeftoverEntries: [ERROR] No VDC and ORG provided for media '%s'\n", entity.Name)
Expand Down
39 changes: 25 additions & 14 deletions govcd/extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@
package govcd

import (
"errors"
"fmt"
"github.com/vmware/go-vcloud-director/v2/types/v56"
"net/http"
)

// DEPRECATED please use GetExternalNetwork function instead
func GetExternalNetworkByName(vcdClient *VCDClient, networkName string) (*types.ExternalNetworkReference, error) {
extNetworkRefs := &types.ExternalNetworkReferences{}

extNetworkHREF, err := getExternalNetworkHref(vcdClient)
extNetworkHREF, err := getExternalNetworkHref(&vcdClient.Client)
if err != nil {
return &types.ExternalNetworkReference{}, err
}
Expand All @@ -33,28 +34,38 @@ func GetExternalNetworkByName(vcdClient *VCDClient, networkName string) (*types.
return &types.ExternalNetworkReference{}, nil
}

func getExternalNetworkHref(vcdClient *VCDClient) (string, error) {
extensions, err := getExtension(vcdClient)
if err != nil {
return "", err
func GetExternalNetwork(vcdClient *VCDClient, networkName string) (*ExternalNetwork, error) {
externalNetwork := NewExternalNetwork(&vcdClient.Client)
err := externalNetwork.GetByName(networkName)
return externalNetwork, err
}

func CreateExternalNetwork(vcdClient *VCDClient, externalNetwork *types.ExternalNetwork) (Task, error) {

if !vcdClient.Client.IsSysAdmin {
return Task{}, fmt.Errorf("functionality requires system administrator privileges")
}

for _, extensionLink := range extensions.Link {
if extensionLink.Type == "application/vnd.vmware.admin.vmwExternalNetworkReferences+xml" {
return extensionLink.HREF, nil
}
err := validateExternalNetwork(externalNetwork)
if err != nil {
return Task{}, err
}

return "", errors.New("external network link isn't found")
externalNetHREF := vcdClient.Client.VCDHREF
externalNetHREF.Path += "/admin/extension/externalnets"

// Return the task
return vcdClient.Client.ExecuteTaskRequest(externalNetHREF.String(), http.MethodPost,
types.MimeExternalNetwork, "error instantiating a new ExternalNetwork: %s", externalNetwork)
}

func getExtension(vcdClient *VCDClient) (*types.Extension, error) {
func getExtension(client *Client) (*types.Extension, error) {
extensions := &types.Extension{}

extensionHREF := vcdClient.Client.VCDHREF
extensionHREF := client.VCDHREF
extensionHREF.Path += "/admin/extension/"

_, err := vcdClient.Client.ExecuteRequest(extensionHREF.String(), http.MethodGet,
_, err := client.ExecuteRequest(extensionHREF.String(), http.MethodGet,
"", "error retrieving extension: %s", nil, extensions)

return extensions, err
Expand Down
130 changes: 127 additions & 3 deletions govcd/extension_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ package govcd

import (
"fmt"
"github.com/vmware/go-vcloud-director/v2/types/v56"
. "gopkg.in/check.v1"
"net/url"
"time"
)

// Retrieves an external network and checks that its contents are filled as expected
func (vcd *TestVCD) Test_GetExternalNetwork(check *C) {

fmt.Printf("Running: %s\n", check.TestName())
if vcd.skipAdminTests {
check.Skip(fmt.Sprintf(TestRequiresSysAdminPrivileges, check.TestName()))
Expand All @@ -24,7 +26,129 @@ func (vcd *TestVCD) Test_GetExternalNetwork(check *C) {
check.Assert(err, IsNil)
LogExternalNetwork(*externalNetwork)
check.Assert(externalNetwork.HREF, Not(Equals), "")
expectedType := "application/vnd.vmware.admin.extension.network+xml"
check.Assert(externalNetwork.Name, Equals, networkName)
check.Assert(externalNetwork.Type, Equals, expectedType)
check.Assert(externalNetwork.Type, Equals, types.MimeExtensionNetwork)
}

func (vcd *TestVCD) Test_CreateExternalNetwork(check *C) {
fmt.Printf("Running: %s\n", check.TestName())
if vcd.skipAdminTests {
check.Skip(fmt.Sprintf(TestRequiresSysAdminPrivileges, check.TestName()))
}

if vcd.config.VCD.ExternalNetwork == "" {
check.Skip("Test_GetByName: External network isn't configured. Test can't proceed")
}

if vcd.config.VCD.VimServer == "" {
check.Skip("Test_GetByName: Vim server isn't configured. Test can't proceed")
}

if vcd.config.VCD.ExternalNetworkPortGroup == "" {
check.Skip("Test_GetByName: Port group isn't configured. Test can't proceed")
}

if vcd.config.VCD.ExternalNetworkPortGroupType == "" {
check.Skip("Test_GetByName: Port group type isn't configured. Test can't proceed")
}

virtualCenters, err := QueryVirtualCenters(vcd.client, fmt.Sprintf("(name==%s)", vcd.config.VCD.VimServer))
check.Assert(err, IsNil)
if len(virtualCenters) == 0 {
check.Skip(fmt.Sprintf("No vSphere server found with name '%s'", vcd.config.VCD.VimServer))
}
vimServerHref := virtualCenters[0].HREF

// Resolve port group info
portGroups, err := QueryPortGroups(vcd.client, fmt.Sprintf("(name==%s;portgroupType==%s)", url.QueryEscape(vcd.config.VCD.ExternalNetworkPortGroup), vcd.config.VCD.ExternalNetworkPortGroupType))
check.Assert(err, IsNil)
if len(portGroups) == 0 {
check.Skip(fmt.Sprintf("No port group found with name '%s'", vcd.config.VCD.ExternalNetworkPortGroup))
}
if len(portGroups) > 1 {
check.Skip(fmt.Sprintf("More then one found with name '%s'", vcd.config.VCD.ExternalNetworkPortGroup))
}

externalNetwork := &types.ExternalNetwork{
Name: TestCreateExternalNetwork,
Description: "Test Create External Network",
Xmlns: types.XMLNamespaceExtension,
XmlnsVCloud: types.XMLNamespaceVCloud,
Configuration: &types.NetworkConfiguration{
Xmlns: types.XMLNamespaceVCloud,
IPScopes: &types.IPScopes{
IPScope: []*types.IPScope{&types.IPScope{
Gateway: "192.168.201.1",
Netmask: "255.255.255.0",
DNS1: "192.168.202.253",
DNS2: "192.168.202.254",
DNSSuffix: "some.net",
IPRanges: &types.IPRanges{
IPRange: []*types.IPRange{
&types.IPRange{
StartAddress: "192.168.201.3",
EndAddress: "192.168.201.250",
},
},
},
},
}},
FenceMode: "isolated",
},
VimPortGroupRefs: &types.VimObjectRefs{
VimObjectRef: []*types.VimObjectRef{
&types.VimObjectRef{
VimServerRef: &types.Reference{
HREF: vimServerHref,
},
MoRef: portGroups[0].MoRef,
VimObjectType: vcd.config.VCD.ExternalNetworkPortGroupType,
},
},
},
}
task, err := CreateExternalNetwork(vcd.client, externalNetwork)
check.Assert(err, IsNil)
check.Assert(task.Task, Not(Equals), types.Task{})

err = task.WaitTaskCompletion()
check.Assert(err, IsNil)

newExternalNetwork := NewExternalNetwork(&vcd.client.Client)
err = newExternalNetwork.GetByName(TestCreateExternalNetwork)
check.Assert(err, IsNil)
check.Assert(newExternalNetwork.ExternalNetwork.Name, Equals, TestCreateExternalNetwork)

ipScope := newExternalNetwork.ExternalNetwork.Configuration.IPScopes.IPScope
check.Assert(ipScope[0].Gateway, Equals, "192.168.201.1")
check.Assert(ipScope[0].Netmask, Equals, "255.255.255.0")
check.Assert(ipScope[0].DNS1, Equals, "192.168.202.253")
check.Assert(ipScope[0].DNS2, Equals, "192.168.202.254")
check.Assert(ipScope[0].DNSSuffix, Equals, "some.net")

check.Assert(len(ipScope[0].IPRanges.IPRange), Equals, 1)
ipRange := ipScope[0].IPRanges.IPRange[0]
check.Assert(ipRange.StartAddress, Equals, "192.168.201.3")
check.Assert(ipRange.EndAddress, Equals, "192.168.201.250")

check.Assert(newExternalNetwork.ExternalNetwork.Configuration.FenceMode, Equals, "isolated")

// Needs to be deleted as port group used by other tests
// Workaround to refresh until task is fully completed - as task wait isn't enough
// Task still exists and creates NETWORK_DELETE error, so we wait until disappears
for i := 0; i < 30; i++ {
err = newExternalNetwork.Refresh()
check.Assert(err, IsNil)
if newExternalNetwork.ExternalNetwork.Tasks != nil && len(newExternalNetwork.ExternalNetwork.Tasks.Task) == 0 {
break
} else {
time.Sleep(1 * time.Second)
}
}

err = newExternalNetwork.DeleteWait()
if err != nil {
AddToCleanupList(externalNetwork.Name, "externalNetwork", "", "Test_CreateExternalNetwork")
}
check.Assert(err, IsNil)
}
114 changes: 114 additions & 0 deletions govcd/externalnetwork.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* Copyright 2019 VMware, Inc. All rights reserved. Licensed under the Apache v2 License.
*/

package govcd

import (
"errors"
"fmt"
"github.com/vmware/go-vcloud-director/v2/types/v56"
"github.com/vmware/go-vcloud-director/v2/util"
"net/http"
)

type ExternalNetwork struct {
ExternalNetwork *types.ExternalNetwork
client *Client
}

func NewExternalNetwork(cli *Client) *ExternalNetwork {
return &ExternalNetwork{
ExternalNetwork: new(types.ExternalNetwork),
client: cli,
}
}

func getExternalNetworkHref(client *Client) (string, error) {
extensions, err := getExtension(client)
if err != nil {
return "", err
}

for _, extensionLink := range extensions.Link {
if extensionLink.Type == "application/vnd.vmware.admin.vmwExternalNetworkReferences+xml" {
return extensionLink.HREF, nil
}
}

return "", errors.New("external network link isn't found")
}

func (externalNetwork ExternalNetwork) GetByName(networkName string) error {

if !externalNetwork.client.IsSysAdmin {
return fmt.Errorf("functionality requires system administrator privileges")
}

extNetworkHREF, err := getExternalNetworkHref(externalNetwork.client)
if err != nil {
return err
}

extNetworkRefs := &types.ExternalNetworkReferences{}
_, err = externalNetwork.client.ExecuteRequest(extNetworkHREF, http.MethodGet,
types.MimeNetworkConnectionSection, "error retrieving external networks: %s", nil, extNetworkRefs)
if err != nil {
return err
}

for _, netRef := range extNetworkRefs.ExternalNetworkReference {
if netRef.Name == networkName {
externalNetwork.ExternalNetwork.HREF = netRef.HREF
return externalNetwork.Refresh()
}
}

return fmt.Errorf("external network %s not found", networkName)
}

func (externalNetwork ExternalNetwork) Refresh() error {

if !externalNetwork.client.IsSysAdmin {
return fmt.Errorf("functionality requires system administrator privileges")
}

_, err := externalNetwork.client.ExecuteRequest(externalNetwork.ExternalNetwork.HREF, http.MethodGet,
"", "error refreshing external network: %s", nil, externalNetwork.ExternalNetwork)

return err
}

func validateExternalNetwork(externalNetwork *types.ExternalNetwork) error {
if externalNetwork.Name == "" {
return errors.New("external Network missing required field: Name")
}
if externalNetwork.Xmlns == "" {
return errors.New("external Network missing required field: Xmlns")
}
return nil
}

func (externalNetwork *ExternalNetwork) Delete() (Task, error) {
util.Logger.Printf("[TRACE] ExternalNetwork.Delete")

if !externalNetwork.client.IsSysAdmin {
return Task{}, fmt.Errorf("functionality requires system administrator privileges")
}

// Return the task
return externalNetwork.client.ExecuteTaskRequest(externalNetwork.ExternalNetwork.HREF, http.MethodDelete,
"", "error deleting external network: %s", nil)
}

func (externalNetwork *ExternalNetwork) DeleteWait() error {
task, err := externalNetwork.Delete()
if err != nil {
return err
}
err = task.WaitTaskCompletion()
if err != nil {
return fmt.Errorf("couldn't finish removing external network %#v", err)
}
return nil
}
Loading