github.com/vmware/go-vcloud-director/v2@v2.24.0/govcd/common_test.go (about)

     1  //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 || ALL
     2  
     3  /*
     4   * Copyright 2021 VMware, Inc.  All rights reserved.  Licensed under the Apache v2 License.
     5   */
     6  
     7  package govcd
     8  
     9  import (
    10  	"errors"
    11  	"fmt"
    12  	"io"
    13  	"net/http"
    14  	"net/url"
    15  	"os"
    16  	"regexp"
    17  	"sort"
    18  	"strconv"
    19  	"time"
    20  
    21  	"github.com/vmware/go-vcloud-director/v2/util"
    22  
    23  	"github.com/vmware/go-vcloud-director/v2/types/v56"
    24  
    25  	. "gopkg.in/check.v1"
    26  )
    27  
    28  // createAndGetResourcesForVmCreation creates vAPP and two VM for the testing
    29  func (vcd *TestVCD) createAndGetResourcesForVmCreation(check *C, vmName string) (*Vdc, *EdgeGateway, VAppTemplate, *VApp, types.NetworkConnectionSection, error) {
    30  	if vcd.config.VCD.Catalog.Name == "" {
    31  		check.Skip("No Catalog name given for VDC tests")
    32  	}
    33  
    34  	if vcd.config.VCD.Catalog.CatalogItem == "" {
    35  		check.Skip("No Catalog item given for VDC tests")
    36  	}
    37  	// Get org and vdc
    38  	org, err := vcd.client.GetAdminOrgByName(vcd.config.VCD.Org)
    39  	check.Assert(err, IsNil)
    40  	vdc, err := org.GetVDCByName(vcd.config.VCD.Vdc, false)
    41  	check.Assert(err, IsNil)
    42  	check.Assert(vdc, NotNil)
    43  	edge, err := vcd.vdc.GetEdgeGatewayByName(vcd.config.VCD.EdgeGateway, false)
    44  	check.Assert(err, IsNil)
    45  	// Find catalog and catalog item
    46  	catalog, err := org.GetCatalogByName(vcd.config.VCD.Catalog.Name, false)
    47  	check.Assert(err, IsNil)
    48  	check.Assert(catalog, NotNil)
    49  	catalogItem, err := catalog.GetCatalogItemByName(vcd.config.VCD.Catalog.CatalogItem, false)
    50  	check.Assert(err, IsNil)
    51  	// Skip the test if catalog item is not Photon OS
    52  	if !isItemPhotonOs(*catalogItem) {
    53  		check.Skip(fmt.Sprintf("Skipping test because catalog item %s is not Photon OS",
    54  			vcd.config.VCD.Catalog.CatalogItem))
    55  	}
    56  	fmt.Printf("# Creating RAW vApp '%s'", vmName)
    57  	vappTemplate, err := catalogItem.GetVAppTemplate()
    58  	check.Assert(err, IsNil)
    59  	// Compose Raw vApp
    60  	vapp, err := vdc.CreateRawVApp(vmName, "")
    61  	check.Assert(err, IsNil)
    62  	check.Assert(vapp, NotNil)
    63  	// vApp was created - let's add it to cleanup list
    64  	AddToCleanupList(vmName, "vapp", "", "createTestVapp")
    65  	// Wait until vApp becomes configurable
    66  	initialVappStatus, err := vapp.GetStatus()
    67  	check.Assert(err, IsNil)
    68  	if initialVappStatus != "RESOLVED" { // RESOLVED vApp is ready to accept operations
    69  		err = vapp.BlockWhileStatus(initialVappStatus, vapp.client.MaxRetryTimeout)
    70  		check.Assert(err, IsNil)
    71  	}
    72  	fmt.Printf(". Done\n")
    73  	fmt.Printf("# Attaching VDC network '%s' to vApp '%s'", vcd.config.VCD.Network.Net1, vmName)
    74  	// Attach VDC network to vApp so that VMs can use it
    75  	net, err := vdc.GetOrgVdcNetworkByName(vcd.config.VCD.Network.Net1, false)
    76  	check.Assert(err, IsNil)
    77  	task, err := vapp.AddRAWNetworkConfig([]*types.OrgVDCNetwork{net.OrgVDCNetwork})
    78  	check.Assert(err, IsNil)
    79  	err = task.WaitTaskCompletion()
    80  	check.Assert(err, IsNil)
    81  	fmt.Printf(". Done\n")
    82  	// Spawn 2 VMs with python servers in the newly created vApp
    83  	desiredNetConfig := types.NetworkConnectionSection{}
    84  	desiredNetConfig.PrimaryNetworkConnectionIndex = 0
    85  	desiredNetConfig.NetworkConnection = append(desiredNetConfig.NetworkConnection,
    86  		&types.NetworkConnection{
    87  			IsConnected:             true,
    88  			IPAddressAllocationMode: types.IPAllocationModePool,
    89  			Network:                 vcd.config.VCD.Network.Net1,
    90  			NetworkConnectionIndex:  0,
    91  		})
    92  	return vdc, edge, vappTemplate, vapp, desiredNetConfig, err
    93  }
    94  
    95  // spawnVM spawns VMs in provided vApp from template and also applies customization script to
    96  // spawn a Python 3 HTTP server
    97  func spawnVM(name string, memorySize int, vdc Vdc, vapp VApp, net types.NetworkConnectionSection, vAppTemplate VAppTemplate, check *C, customizationScript string, powerOn bool) (VM, error) {
    98  	fmt.Printf("# Spawning VM '%s'", name)
    99  	task, err := vapp.AddNewVM(name, vAppTemplate, &net, true)
   100  	check.Assert(err, IsNil)
   101  	err = task.WaitTaskCompletion()
   102  	check.Assert(err, IsNil)
   103  	vm, err := vapp.GetVMByName(name, true)
   104  	check.Assert(err, IsNil)
   105  	fmt.Printf(". Done\n")
   106  
   107  	fmt.Printf("# Applying 2 vCPU and "+strconv.Itoa(memorySize)+"MB configuration for VM '%s'", name)
   108  	err = vm.ChangeCPU(2, 1)
   109  	check.Assert(err, IsNil)
   110  
   111  	err = vm.ChangeMemory(int64(memorySize))
   112  	check.Assert(err, IsNil)
   113  	fmt.Printf(". Done\n")
   114  
   115  	if customizationScript != "" {
   116  		fmt.Printf("# Applying customization script for VM '%s'", name)
   117  		task, err = vm.RunCustomizationScript(name, customizationScript)
   118  		check.Assert(err, IsNil)
   119  		err = task.WaitTaskCompletion()
   120  		check.Assert(err, IsNil)
   121  		fmt.Printf(". Done\n")
   122  	}
   123  
   124  	if powerOn {
   125  		fmt.Printf("# Powering on VM '%s'", name)
   126  		task, err = vm.PowerOn()
   127  		check.Assert(err, IsNil)
   128  		err = task.WaitTaskCompletion()
   129  		check.Assert(err, IsNil)
   130  		fmt.Printf(". Done\n")
   131  	}
   132  
   133  	return *vm, nil
   134  }
   135  
   136  // isItemPhotonOs checks if a catalog item is Photon OS
   137  func isItemPhotonOs(item CatalogItem) bool {
   138  	vappTemplate, err := item.GetVAppTemplate()
   139  	// Unable to get template - can validate it's Photon OS
   140  	if err != nil {
   141  		return false
   142  	}
   143  	// Photon OS template has exactly 1 child
   144  	if len(vappTemplate.VAppTemplate.Children.VM) != 1 {
   145  		return false
   146  	}
   147  
   148  	// If child name is not "Photon OS" it's not Photon OS
   149  	if vappTemplate.VAppTemplate.Children.VM[0].Name != "Photon OS" {
   150  		return false
   151  	}
   152  
   153  	return true
   154  }
   155  
   156  // cacheLoadBalancer is meant to store load balancer settings before any operations so that all
   157  // configuration can be checked after manipulation
   158  func testCacheLoadBalancer(edge EdgeGateway, check *C) (*types.LbGeneralParamsWithXml, string) {
   159  	beforeLb, err := edge.GetLBGeneralParams()
   160  	check.Assert(err, IsNil)
   161  	beforeLbXml := testGetEdgeEndpointXML(types.LbConfigPath, edge, check)
   162  	return beforeLb, beforeLbXml
   163  }
   164  
   165  // testGetEdgeEndpointXML is used for additional validation that modifying edge gateway endpoint
   166  // does not change any single field. It returns an XML string of whole configuration
   167  func testGetEdgeEndpointXML(endpoint string, edge EdgeGateway, check *C) string {
   168  
   169  	httpPath, err := edge.buildProxiedEdgeEndpointURL(endpoint)
   170  	check.Assert(err, IsNil)
   171  
   172  	resp, err := edge.client.ExecuteRequestWithCustomError(httpPath, http.MethodGet, types.AnyXMLMime,
   173  		fmt.Sprintf("unable to get XML from endpoint %s: %%s", endpoint), nil, &types.NSXError{})
   174  	check.Assert(err, IsNil)
   175  
   176  	defer func(Body io.ReadCloser) {
   177  		err := Body.Close()
   178  		if err != nil {
   179  			util.Logger.Printf("error closing response Body [testGetEdgeEndpointXML]: %s", err)
   180  		}
   181  	}(resp.Body)
   182  
   183  	body, err := io.ReadAll(resp.Body)
   184  	check.Assert(err, IsNil)
   185  
   186  	return string(body)
   187  }
   188  
   189  // testCheckLoadBalancerConfig validates if both raw XML string and load balancer struct remain
   190  // identical after settings manipulation.
   191  func testCheckLoadBalancerConfig(beforeLb *types.LbGeneralParamsWithXml, beforeLbXml string, edge EdgeGateway, check *C) {
   192  	afterLb, err := edge.GetLBGeneralParams()
   193  	check.Assert(err, IsNil)
   194  
   195  	afterLbXml := testGetEdgeEndpointXML(types.LbConfigPath, edge, check)
   196  
   197  	// remove `<version></version>` tag from both XML represntation and struct for deep comparison
   198  	// because this version changes with each update and will never be the same after a few
   199  	// operations
   200  
   201  	reVersion := regexp.MustCompile(`<version>\w*<\/version>`)
   202  	beforeLbXml = reVersion.ReplaceAllLiteralString(beforeLbXml, "")
   203  	afterLbXml = reVersion.ReplaceAllLiteralString(afterLbXml, "")
   204  
   205  	beforeLb.Version = ""
   206  	afterLb.Version = ""
   207  
   208  	check.Assert(beforeLb, DeepEquals, afterLb)
   209  	check.Assert(beforeLbXml, DeepEquals, afterLbXml)
   210  }
   211  
   212  // deployVappForTest aims to replace createVappForTest
   213  func deployVappForTest(vcd *TestVCD, vappName string) (*VApp, error) {
   214  	// Populate OrgVDCNetwork
   215  	net, err := vcd.vdc.GetOrgVdcNetworkByName(vcd.config.VCD.Network.Net1, false)
   216  	if err != nil {
   217  		return nil, fmt.Errorf("error finding network : %s", err)
   218  	}
   219  
   220  	// Populate Catalog
   221  	cat, err := vcd.org.GetCatalogByName(vcd.config.VCD.Catalog.Name, false)
   222  	if err != nil || cat == nil {
   223  		return nil, fmt.Errorf("error finding catalog : %s", err)
   224  	}
   225  	// Populate Catalog Item
   226  	catitem, err := cat.GetCatalogItemByName(vcd.config.VCD.Catalog.CatalogItem, false)
   227  	if err != nil {
   228  		return nil, fmt.Errorf("error finding catalog item : %s", err)
   229  	}
   230  	// Get VAppTemplate
   231  	vAppTemplate, err := catitem.GetVAppTemplate()
   232  	if err != nil {
   233  		return nil, fmt.Errorf("error finding vapptemplate : %s", err)
   234  	}
   235  	// Get StorageProfileReference
   236  	storageProfileRef, err := vcd.vdc.FindStorageProfileReference(vcd.config.VCD.StorageProfile.SP1)
   237  	if err != nil {
   238  		return nil, fmt.Errorf("error finding storage profile: %s", err)
   239  	}
   240  
   241  	// Create empty vApp
   242  	vapp, err := vcd.vdc.CreateRawVApp(vappName, "description")
   243  	if err != nil {
   244  		return nil, fmt.Errorf("error creating vapp: %s", err)
   245  	}
   246  
   247  	// After a successful creation, the entity is added to the cleanup list.
   248  	// If something fails after this point, the entity will be removed
   249  	AddToCleanupList(vappName, "vapp", "", "createTestVapp")
   250  
   251  	// Create vApp networking
   252  	vAppNetworkConfig, err := vapp.AddOrgNetwork(&VappNetworkSettings{}, net.OrgVDCNetwork, false)
   253  	if err != nil {
   254  		return nil, fmt.Errorf("error creating vApp network. %s", err)
   255  	}
   256  
   257  	// Create VM with only one NIC connected to vapp_net
   258  	networkConnectionSection := &types.NetworkConnectionSection{
   259  		PrimaryNetworkConnectionIndex: 0,
   260  	}
   261  
   262  	netConn := &types.NetworkConnection{
   263  		Network:                 vAppNetworkConfig.NetworkConfig[0].NetworkName,
   264  		IsConnected:             true,
   265  		NetworkConnectionIndex:  0,
   266  		IPAddressAllocationMode: types.IPAllocationModePool,
   267  	}
   268  
   269  	networkConnectionSection.NetworkConnection = append(networkConnectionSection.NetworkConnection, netConn)
   270  
   271  	task, err := vapp.AddNewVMWithStorageProfile("test_vm", vAppTemplate, networkConnectionSection, &storageProfileRef, true)
   272  	if err != nil {
   273  		return nil, fmt.Errorf("error creating the VM: %s", err)
   274  	}
   275  
   276  	err = task.WaitTaskCompletion()
   277  	if err != nil {
   278  		return nil, fmt.Errorf("error while waiting for the VM to be created %s", err)
   279  	}
   280  
   281  	err = vapp.BlockWhileStatus("UNRESOLVED", vapp.client.MaxRetryTimeout)
   282  	if err != nil {
   283  		return nil, fmt.Errorf("error waiting for created test vApp to have working state: %s", err)
   284  	}
   285  
   286  	return vapp, nil
   287  }
   288  
   289  // Checks whether an independent disk is attached to a VM, and detaches it
   290  // moved from disk_test.go
   291  func (vcd *TestVCD) detachIndependentDisk(disk Disk) error {
   292  
   293  	// See if the disk is attached to the VM
   294  	vmRef, err := disk.AttachedVM()
   295  	if err != nil {
   296  		return err
   297  	}
   298  	// If the disk is attached to the VM, detach disk from the VM
   299  	if vmRef != nil {
   300  
   301  		vm, err := vcd.client.Client.GetVMByHref(vmRef.HREF)
   302  		if err != nil {
   303  			return err
   304  		}
   305  
   306  		// Detach the disk from VM
   307  		task, err := vm.DetachDisk(&types.DiskAttachOrDetachParams{
   308  			Disk: &types.Reference{
   309  				HREF: disk.Disk.HREF,
   310  			},
   311  		})
   312  		if err != nil {
   313  			return err
   314  		}
   315  		err = task.WaitTaskCompletion()
   316  		if err != nil {
   317  			return err
   318  		}
   319  	}
   320  	return nil
   321  }
   322  
   323  // moved from vapp_test.go
   324  func verifyNetworkConnectionSection(check *C, actual, desired *types.NetworkConnectionSection) {
   325  
   326  	check.Assert(len(actual.NetworkConnection), Equals, len(desired.NetworkConnection))
   327  	check.Assert(actual.PrimaryNetworkConnectionIndex, Equals, desired.PrimaryNetworkConnectionIndex)
   328  
   329  	sort.SliceStable(actual.NetworkConnection, func(i, j int) bool {
   330  		return actual.NetworkConnection[i].NetworkConnectionIndex <
   331  			actual.NetworkConnection[j].NetworkConnectionIndex
   332  	})
   333  
   334  	for _, nic := range actual.NetworkConnection {
   335  		actualNic := actual.NetworkConnection[nic.NetworkConnectionIndex]
   336  		desiredNic := desired.NetworkConnection[nic.NetworkConnectionIndex]
   337  
   338  		check.Assert(actualNic.MACAddress, Not(Equals), "")
   339  		check.Assert(actualNic.NetworkAdapterType, Not(Equals), "")
   340  		check.Assert(actualNic.IPAddressAllocationMode, Equals, desiredNic.IPAddressAllocationMode)
   341  		check.Assert(actualNic.Network, Equals, desiredNic.Network)
   342  		check.Assert(actualNic.NetworkConnectionIndex, Equals, desiredNic.NetworkConnectionIndex)
   343  
   344  		if actualNic.IPAddressAllocationMode != types.IPAllocationModeNone {
   345  			check.Assert(actualNic.IPAddress, Not(Equals), "")
   346  		}
   347  	}
   348  }
   349  
   350  // Ensure vApp is suitable for VM test
   351  // Some VM tests may fail if vApp is not powered on, so VM tests can call this function to ensure the vApp is suitable for VM tests
   352  // moved from vm_test.go
   353  func (vcd *TestVCD) ensureVappIsSuitableForVMTest(vapp VApp) error {
   354  	status, err := vapp.GetStatus()
   355  
   356  	if err != nil {
   357  		return err
   358  	}
   359  
   360  	// If vApp is not powered on (status = 4), power on vApp
   361  	if status != types.VAppStatuses[4] {
   362  		task, err := vapp.PowerOn()
   363  		if err != nil {
   364  			return err
   365  		}
   366  		err = task.WaitTaskCompletion()
   367  		if err != nil {
   368  			return err
   369  		}
   370  	}
   371  
   372  	return nil
   373  }
   374  
   375  // Ensure VM is suitable for VM test
   376  // Please call ensureVappAvailableForVMTest first to power on the vApp because this function cannot handle VM in suspension state due to lack of VM APIs (e.g. discard VM suspend API)
   377  // Some VM tests may fail if VM is not powered on or powered off, so VM tests can call this function to ensure the VM is suitable for VM tests
   378  // moved from vm_test.go
   379  func (vcd *TestVCD) ensureVMIsSuitableForVMTest(vm *VM) error {
   380  	// if the VM is not powered on (status = 4) or not powered off, wait for the VM power on
   381  	// wait for around 1 min
   382  	valid := false
   383  	for i := 0; i < 6; i++ {
   384  		status, err := vm.GetStatus()
   385  		if err != nil {
   386  			return err
   387  		}
   388  
   389  		// If the VM is powered on (status = 4)
   390  		if status == types.VAppStatuses[4] {
   391  			// Prevent affect Test_ChangeMemorySize
   392  			// because TestVCD.Test_AttachedVMDisk is run before Test_ChangeMemorySize and Test_ChangeMemorySize will fail the test if the VM is powered on,
   393  			task, err := vm.Undeploy()
   394  			if err != nil {
   395  				return err
   396  			}
   397  			err = task.WaitTaskCompletion()
   398  			if err != nil {
   399  				return err
   400  			}
   401  		}
   402  
   403  		// If the VM is powered on (status = 4) or powered off (status = 8)
   404  		if status == types.VAppStatuses[4] || status == types.VAppStatuses[8] {
   405  			valid = true
   406  		}
   407  
   408  		// If 1st to 5th attempt is completed, sleep 10 seconds and try again
   409  		// The last attempt will exit this for loop immediately, so no need to sleep
   410  		if i < 5 {
   411  			time.Sleep(time.Second * 10)
   412  		}
   413  	}
   414  
   415  	if !valid {
   416  		return errors.New("the VM is not powered on or powered off")
   417  	}
   418  
   419  	return nil
   420  }
   421  
   422  // moved from org_test.go
   423  func doesOrgExist(check *C, vcd *TestVCD) {
   424  	var org *AdminOrg
   425  	for i := 0; i < 30; i++ {
   426  		org, _ = vcd.client.GetAdminOrgByName(TestDeleteOrg)
   427  		if org == nil {
   428  			break
   429  		} else {
   430  			time.Sleep(time.Second)
   431  		}
   432  	}
   433  	check.Assert(org, IsNil)
   434  }
   435  
   436  // Helper function that creates an external network to be used in other tests
   437  // moved from externalnetwork_test.go
   438  func (vcd *TestVCD) testCreateExternalNetwork(testName, networkName, dnsSuffix string) (skippingReason string, externalNetwork *types.ExternalNetwork, task Task, err error) {
   439  
   440  	if vcd.skipAdminTests {
   441  		return fmt.Sprintf(TestRequiresSysAdminPrivileges, testName), externalNetwork, Task{}, nil
   442  	}
   443  
   444  	if vcd.config.VCD.ExternalNetwork == "" {
   445  		return fmt.Sprintf("%s: External network isn't configured. Test can't proceed", testName), externalNetwork, Task{}, nil
   446  	}
   447  
   448  	if vcd.config.VCD.VimServer == "" {
   449  		return fmt.Sprintf("%s: Vim server isn't configured. Test can't proceed", testName), externalNetwork, Task{}, nil
   450  	}
   451  
   452  	if vcd.config.VCD.ExternalNetworkPortGroup == "" {
   453  		return fmt.Sprintf("%s: Port group isn't configured. Test can't proceed", testName), externalNetwork, Task{}, nil
   454  	}
   455  
   456  	if vcd.config.VCD.ExternalNetworkPortGroupType == "" {
   457  		return fmt.Sprintf("%s: Port group type isn't configured. Test can't proceed", testName), externalNetwork, Task{}, nil
   458  	}
   459  
   460  	virtualCenters, err := QueryVirtualCenters(vcd.client, fmt.Sprintf("name==%s", vcd.config.VCD.VimServer))
   461  	if err != nil {
   462  		return "", externalNetwork, Task{}, err
   463  	}
   464  	if len(virtualCenters) == 0 {
   465  		return fmt.Sprintf("No vSphere server found with name '%s'", vcd.config.VCD.VimServer), externalNetwork, Task{}, nil
   466  	}
   467  	vimServerHref := virtualCenters[0].HREF
   468  
   469  	// Resolve port group info
   470  	portGroups, err := QueryPortGroups(vcd.client, fmt.Sprintf("name==%s;portgroupType==%s", url.QueryEscape(vcd.config.VCD.ExternalNetworkPortGroup), vcd.config.VCD.ExternalNetworkPortGroupType))
   471  	if err != nil {
   472  		return "", externalNetwork, Task{}, err
   473  	}
   474  	if len(portGroups) == 0 {
   475  		return fmt.Sprintf("No port group found with name '%s'", vcd.config.VCD.ExternalNetworkPortGroup), externalNetwork, Task{}, nil
   476  	}
   477  	if len(portGroups) > 1 {
   478  		return fmt.Sprintf("More than one port group found with name '%s'", vcd.config.VCD.ExternalNetworkPortGroup), externalNetwork, Task{}, nil
   479  	}
   480  
   481  	externalNetwork = &types.ExternalNetwork{
   482  		Name:        networkName,
   483  		Description: "Test Create External Network",
   484  		Configuration: &types.NetworkConfiguration{
   485  			IPScopes: &types.IPScopes{
   486  				IPScope: []*types.IPScope{&types.IPScope{
   487  					Gateway:   "192.168.201.1",
   488  					Netmask:   "255.255.255.0",
   489  					DNS1:      "192.168.202.253",
   490  					DNS2:      "192.168.202.254",
   491  					DNSSuffix: dnsSuffix,
   492  					IPRanges: &types.IPRanges{
   493  						IPRange: []*types.IPRange{
   494  							&types.IPRange{
   495  								StartAddress: "192.168.201.3",
   496  								EndAddress:   "192.168.201.100",
   497  							},
   498  							&types.IPRange{
   499  								StartAddress: "192.168.201.105",
   500  								EndAddress:   "192.168.201.140",
   501  							},
   502  						},
   503  					},
   504  				}, &types.IPScope{
   505  					Gateway:   "192.168.231.1",
   506  					Netmask:   "255.255.255.0",
   507  					DNS1:      "192.168.232.253",
   508  					DNS2:      "192.168.232.254",
   509  					DNSSuffix: dnsSuffix,
   510  					IPRanges: &types.IPRanges{
   511  						IPRange: []*types.IPRange{
   512  							&types.IPRange{
   513  								StartAddress: "192.168.231.3",
   514  								EndAddress:   "192.168.231.100",
   515  							},
   516  							&types.IPRange{
   517  								StartAddress: "192.168.231.105",
   518  								EndAddress:   "192.168.231.140",
   519  							},
   520  							&types.IPRange{
   521  								StartAddress: "192.168.231.145",
   522  								EndAddress:   "192.168.231.150",
   523  							},
   524  						},
   525  					},
   526  				},
   527  				}},
   528  			FenceMode: "isolated",
   529  		},
   530  		VimPortGroupRefs: &types.VimObjectRefs{
   531  			VimObjectRef: []*types.VimObjectRef{
   532  				&types.VimObjectRef{
   533  					VimServerRef: &types.Reference{
   534  						HREF: vimServerHref,
   535  					},
   536  					MoRef:         portGroups[0].MoRef,
   537  					VimObjectType: vcd.config.VCD.ExternalNetworkPortGroupType,
   538  				},
   539  			},
   540  		},
   541  	}
   542  	task, err = CreateExternalNetwork(vcd.client, externalNetwork)
   543  	return skippingReason, externalNetwork, task, err
   544  }
   545  
   546  // deleteLbServerPoolIfExists is used to cleanup before creation of component. It returns error only if there was
   547  // other error than govcd.ErrorEntityNotFound
   548  // moved from lbserverpool_test.go
   549  func deleteLbServerPoolIfExists(edge EdgeGateway, name string) error {
   550  	err := edge.DeleteLbServerPoolByName(name)
   551  	if err != nil && !ContainsNotFound(err) {
   552  		return err
   553  	}
   554  	if err != nil && ContainsNotFound(err) {
   555  		return nil
   556  	}
   557  
   558  	fmt.Printf("# Removed leftover LB server pool'%s'\n", name)
   559  	return nil
   560  }
   561  
   562  // deleteLbServiceMonitorIfExists is used to cleanup before creation of component. It returns error only if there was
   563  // other error than govcd.ErrorEntityNotFound
   564  // moved from lbservicemonitor_test.go
   565  func deleteLbServiceMonitorIfExists(edge EdgeGateway, name string) error {
   566  	err := edge.DeleteLbServiceMonitorByName(name)
   567  	if err != nil && !ContainsNotFound(err) {
   568  		return err
   569  	}
   570  	if err != nil && ContainsNotFound(err) {
   571  		return nil
   572  	}
   573  
   574  	fmt.Printf("# Removed leftover LB service monitor'%s'\n", name)
   575  	return nil
   576  }
   577  
   578  // deleteLbAppProfileIfExists is used to cleanup before creation of component. It returns error only if there was
   579  // other error than govcd.ErrorEntityNotFound
   580  // moved from lbappprofile_test.go
   581  func deleteLbAppProfileIfExists(edge EdgeGateway, name string) error {
   582  	err := edge.DeleteLbAppProfileByName(name)
   583  	if err != nil && !ContainsNotFound(err) {
   584  		return err
   585  	}
   586  	if err != nil && ContainsNotFound(err) {
   587  		return nil
   588  	}
   589  
   590  	fmt.Printf("# Removed leftover LB app profile '%s'\n", name)
   591  	return nil
   592  }
   593  
   594  // deleteLbAppRuleIfExists is used to cleanup before creation of component. It returns error only if there was
   595  // other error than govcd.ErrorEntityNotFound
   596  // moved from lbapprule_test.go
   597  func deleteLbAppRuleIfExists(edge EdgeGateway, name string) error {
   598  	err := edge.DeleteLbAppRuleByName(name)
   599  	if err != nil && !ContainsNotFound(err) {
   600  		return err
   601  	}
   602  	if err != nil && ContainsNotFound(err) {
   603  		return nil
   604  	}
   605  
   606  	fmt.Printf("# Removed leftover LB app rule '%s'\n", name)
   607  	return nil
   608  }
   609  
   610  // moved from vm_test.go
   611  func deleteVapp(vcd *TestVCD, name string) error {
   612  	vapp, err := vcd.vdc.GetVAppByName(name, true)
   613  	if err != nil {
   614  		return fmt.Errorf("error getting vApp: %s", err)
   615  	}
   616  	task, _ := vapp.Undeploy()
   617  	_ = task.WaitTaskCompletion()
   618  
   619  	// Detach all Org networks during vApp removal because network removal errors if it happens
   620  	// very quickly (as the next task) after vApp removal
   621  	task, _ = vapp.RemoveAllNetworks()
   622  	err = task.WaitTaskCompletion()
   623  	if err != nil {
   624  		return fmt.Errorf("error removing networks from vApp: %s", err)
   625  	}
   626  
   627  	task, err = vapp.Delete()
   628  	if err != nil {
   629  		return fmt.Errorf("error deleting vApp: %s", err)
   630  	}
   631  	err = task.WaitTaskCompletion()
   632  	if err != nil {
   633  		return fmt.Errorf("error waiting for vApp deletion task: %s", err)
   634  	}
   635  	return nil
   636  }
   637  
   638  func deleteNsxtVapp(vcd *TestVCD, name string) error {
   639  	vapp, err := vcd.nsxtVdc.GetVAppByName(name, true)
   640  	if err != nil {
   641  		return fmt.Errorf("error getting vApp: %s", err)
   642  	}
   643  	task, _ := vapp.Undeploy()
   644  	_ = task.WaitTaskCompletion()
   645  
   646  	// Detach all Org networks during vApp removal because network removal errors if it happens
   647  	// very quickly (as the next task) after vApp removal
   648  	task, _ = vapp.RemoveAllNetworks()
   649  	err = task.WaitTaskCompletion()
   650  	if err != nil {
   651  		return fmt.Errorf("error removing networks from vApp: %s", err)
   652  	}
   653  
   654  	task, err = vapp.Delete()
   655  	if err != nil {
   656  		return fmt.Errorf("error deleting vApp: %s", err)
   657  	}
   658  	err = task.WaitTaskCompletion()
   659  	if err != nil {
   660  		return fmt.Errorf("error waiting for vApp deletion task: %s", err)
   661  	}
   662  	return nil
   663  }
   664  
   665  // makeEmptyVapp creates a given vApp without any VM
   666  func makeEmptyVapp(vdc *Vdc, name string, description string) (*VApp, error) {
   667  
   668  	vapp, err := vdc.CreateRawVApp(name, description)
   669  	if err != nil {
   670  		return nil, err
   671  	}
   672  	if vapp == nil {
   673  		return nil, fmt.Errorf("[makeEmptyVapp] unexpected nil vApp returned")
   674  	}
   675  	initialVappStatus, err := vapp.GetStatus()
   676  	if err != nil {
   677  		return nil, err
   678  	}
   679  	if initialVappStatus != "RESOLVED" {
   680  		err = vapp.BlockWhileStatus(initialVappStatus, vapp.client.MaxRetryTimeout)
   681  		if err != nil {
   682  			return nil, err
   683  		}
   684  	}
   685  	return vapp, nil
   686  }
   687  
   688  // makeEmptyVm creates an empty VM inside a given vApp
   689  func makeEmptyVm(vapp *VApp, name string) (*VM, error) {
   690  	newDisk := types.DiskSettings{
   691  		AdapterType:     "5",
   692  		SizeMb:          int64(100),
   693  		BusNumber:       0,
   694  		UnitNumber:      0,
   695  		ThinProvisioned: addrOf(true),
   696  	}
   697  	requestDetails := &types.RecomposeVAppParamsForEmptyVm{
   698  		CreateItem: &types.CreateItem{
   699  			Name:                      name,
   700  			NetworkConnectionSection:  &types.NetworkConnectionSection{},
   701  			Description:               "created by makeEmptyVm",
   702  			GuestCustomizationSection: nil,
   703  			VmSpecSection: &types.VmSpecSection{
   704  				Modified:          addrOf(true),
   705  				Info:              "Virtual Machine specification",
   706  				OsType:            "debian10Guest",
   707  				NumCpus:           addrOf(1),
   708  				NumCoresPerSocket: addrOf(1),
   709  				CpuResourceMhz:    &types.CpuResourceMhz{Configured: 1},
   710  				MemoryResourceMb:  &types.MemoryResourceMb{Configured: 512},
   711  				MediaSection:      nil,
   712  				DiskSection:       &types.DiskSection{DiskSettings: []*types.DiskSettings{&newDisk}},
   713  				HardwareVersion:   &types.HardwareVersion{Value: "vmx-13"},
   714  				VmToolsVersion:    "",
   715  				VirtualCpuType:    "VM32",
   716  				TimeSyncWithHost:  nil,
   717  			},
   718  			BootImage: nil,
   719  		},
   720  		AllEULAsAccepted: true,
   721  	}
   722  
   723  	vm, err := vapp.AddEmptyVm(requestDetails)
   724  	if err != nil {
   725  		return nil, err
   726  	}
   727  
   728  	return vm, nil
   729  }
   730  
   731  // spawnTestVdc spawns a VDC in a given adminOrgName to be used in tests
   732  func spawnTestVdc(vcd *TestVCD, check *C, adminOrgName string) *Vdc {
   733  	adminOrg, err := vcd.client.GetAdminOrgByName(adminOrgName)
   734  	check.Assert(err, IsNil)
   735  
   736  	providerVdcHref := getVdcProviderVdcHref(vcd, check)
   737  	storageProfile, err := vcd.client.QueryProviderVdcStorageProfileByName(vcd.config.VCD.ProviderVdc.StorageProfile, providerVdcHref)
   738  	check.Assert(err, IsNil)
   739  	networkPoolHref := getVdcNetworkPoolHref(vcd, check)
   740  
   741  	vdcConfiguration := &types.VdcConfiguration{
   742  		Name:            check.TestName() + "-VDC",
   743  		Xmlns:           types.XMLNamespaceVCloud,
   744  		AllocationModel: "Flex",
   745  		ComputeCapacity: []*types.ComputeCapacity{
   746  			&types.ComputeCapacity{
   747  				CPU: &types.CapacityWithUsage{
   748  					Units:     "MHz",
   749  					Allocated: 1024,
   750  					Limit:     1024,
   751  				},
   752  				Memory: &types.CapacityWithUsage{
   753  					Allocated: 1024,
   754  					Limit:     1024,
   755  					Units:     "MB",
   756  				},
   757  			},
   758  		},
   759  		VdcStorageProfile: []*types.VdcStorageProfileConfiguration{&types.VdcStorageProfileConfiguration{
   760  			Enabled: addrOf(true),
   761  			Units:   "MB",
   762  			Limit:   1024,
   763  			Default: true,
   764  			ProviderVdcStorageProfile: &types.Reference{
   765  				HREF: storageProfile.HREF,
   766  			},
   767  		},
   768  		},
   769  		NetworkPoolReference: &types.Reference{
   770  			HREF: networkPoolHref,
   771  		},
   772  		ProviderVdcReference: &types.Reference{
   773  			HREF: providerVdcHref,
   774  		},
   775  		IsEnabled:             true,
   776  		IsThinProvision:       true,
   777  		UsesFastProvisioning:  true,
   778  		IsElastic:             addrOf(true),
   779  		IncludeMemoryOverhead: addrOf(true),
   780  	}
   781  
   782  	vdc, err := adminOrg.CreateOrgVdc(vdcConfiguration)
   783  	check.Assert(err, IsNil)
   784  	check.Assert(vdc, NotNil)
   785  
   786  	AddToCleanupList(vdcConfiguration.Name, "vdc", vcd.org.Org.Name, check.TestName())
   787  
   788  	return vdc
   789  }
   790  
   791  // spawnTestOrg spawns an Org to be used in tests
   792  func spawnTestOrg(vcd *TestVCD, check *C, nameSuffix string) string {
   793  	newOrg, err := vcd.client.GetAdminOrgByName(vcd.config.VCD.Org)
   794  	check.Assert(err, IsNil)
   795  	newOrgName := check.TestName() + "-" + nameSuffix
   796  	task, err := CreateOrg(vcd.client, newOrgName, newOrgName, newOrgName, newOrg.AdminOrg.OrgSettings, true)
   797  	check.Assert(err, IsNil)
   798  	err = task.WaitTaskCompletion()
   799  	check.Assert(err, IsNil)
   800  	AddToCleanupList(newOrgName, "org", "", check.TestName())
   801  
   802  	return newOrgName
   803  }
   804  
   805  func getVdcProviderVdcHref(vcd *TestVCD, check *C) string {
   806  	results, err := vcd.client.QueryWithNotEncodedParams(nil, map[string]string{
   807  		"type":   "providerVdc",
   808  		"filter": fmt.Sprintf("name==%s", vcd.config.VCD.ProviderVdc.Name),
   809  	})
   810  	check.Assert(err, IsNil)
   811  	if len(results.Results.VMWProviderVdcRecord) == 0 {
   812  		check.Skip(fmt.Sprintf("No Provider VDC found with name '%s'", vcd.config.VCD.ProviderVdc.Name))
   813  	}
   814  	providerVdcHref := results.Results.VMWProviderVdcRecord[0].HREF
   815  
   816  	return providerVdcHref
   817  }
   818  
   819  func getVdcNetworkPoolHref(vcd *TestVCD, check *C) string {
   820  	results, err := vcd.client.QueryWithNotEncodedParams(nil, map[string]string{
   821  		"type":   "networkPool",
   822  		"filter": fmt.Sprintf("name==%s", vcd.config.VCD.ProviderVdc.NetworkPool),
   823  	})
   824  	check.Assert(err, IsNil)
   825  	if len(results.Results.NetworkPoolRecord) == 0 {
   826  		check.Skip(fmt.Sprintf("No network pool found with name '%s'", vcd.config.VCD.ProviderVdc.NetworkPool))
   827  	}
   828  	networkPoolHref := results.Results.NetworkPoolRecord[0].HREF
   829  
   830  	return networkPoolHref
   831  }
   832  
   833  // convertSliceOfStringsToOpenApiReferenceIds converts []string to []types.OpenApiReference by filling
   834  // types.OpenApiReference.ID fields
   835  func convertSliceOfStringsToOpenApiReferenceIds(ids []string) []types.OpenApiReference {
   836  	resultReferences := make([]types.OpenApiReference, len(ids))
   837  	for i, v := range ids {
   838  		resultReferences[i].ID = v
   839  	}
   840  
   841  	return resultReferences
   842  }
   843  
   844  // extractIdsFromOpenApiReferences extracts []string with IDs from []types.OpenApiReference which contains ID and Names
   845  func extractIdsFromOpenApiReferences(refs []types.OpenApiReference) []string {
   846  	resultStrings := make([]string, len(refs))
   847  	for index := range refs {
   848  		resultStrings[index] = refs[index].ID
   849  	}
   850  
   851  	return resultStrings
   852  }
   853  
   854  // checkSkipWhenApiToken skips the test if the connection was established using an API token
   855  func (vcd *TestVCD) checkSkipWhenApiToken(check *C) {
   856  	if vcd.client.Client.UsingAccessToken {
   857  		check.Skip("This test can't run on API token")
   858  	}
   859  }
   860  
   861  func createNsxtVAppAndVm(vcd *TestVCD, check *C) (*VApp, *VM) {
   862  	cat, err := vcd.org.GetCatalogByName(vcd.config.VCD.Catalog.NsxtBackedCatalogName, false)
   863  	check.Assert(err, IsNil)
   864  	check.Assert(cat, NotNil)
   865  	// Populate Catalog Item
   866  	catitem, err := cat.GetCatalogItemByName(vcd.config.VCD.Catalog.NsxtCatalogItem, false)
   867  	check.Assert(err, IsNil)
   868  	check.Assert(catitem, NotNil)
   869  	// Get VAppTemplate
   870  	vapptemplate, err := catitem.GetVAppTemplate()
   871  	check.Assert(err, IsNil)
   872  	check.Assert(vapptemplate.VAppTemplate.Children.VM[0].HREF, NotNil)
   873  
   874  	return createNsxtVAppAndVmFromCustomTemplate(vcd, check, &vapptemplate)
   875  }
   876  
   877  func createNsxtVAppAndVmFromCustomTemplate(vcd *TestVCD, check *C, vapptemplate *VAppTemplate) (*VApp, *VM) {
   878  	vapp, err := vcd.nsxtVdc.CreateRawVApp(check.TestName(), check.TestName())
   879  	check.Assert(err, IsNil)
   880  	check.Assert(vapp, NotNil)
   881  	// After a successful creation, the entity is added to the cleanup list.
   882  	AddToCleanupList(vapp.VApp.Name, "vapp", vcd.nsxtVdc.Vdc.Name, check.TestName())
   883  
   884  	// Check that vApp is powered-off
   885  	vappStatus, err := vapp.GetStatus()
   886  	check.Assert(err, IsNil)
   887  	check.Assert(vappStatus, Equals, "RESOLVED")
   888  
   889  	task, err := vapp.PowerOn()
   890  	check.Assert(err, IsNil)
   891  	check.Assert(task, NotNil)
   892  	err = task.WaitTaskCompletion()
   893  	check.Assert(err, IsNil)
   894  
   895  	vappStatus, err = vapp.GetStatus()
   896  	check.Assert(err, IsNil)
   897  	check.Assert(vappStatus, Equals, "POWERED_ON")
   898  
   899  	// Once the operation is successful, we won't trigger a failure
   900  	// until after the vApp deletion
   901  	check.Check(vapp.VApp.Name, Equals, check.TestName())
   902  	check.Check(vapp.VApp.Description, Equals, check.TestName())
   903  
   904  	// Construct VM
   905  	vmDef := &types.ReComposeVAppParams{
   906  		Ovf:              types.XMLNamespaceOVF,
   907  		Xsi:              types.XMLNamespaceXSI,
   908  		Xmlns:            types.XMLNamespaceVCloud,
   909  		AllEULAsAccepted: true,
   910  		// Deploy:           false,
   911  		Name: vapp.VApp.Name,
   912  		// PowerOn: false, // Not touching power state at this phase
   913  		SourcedItem: &types.SourcedCompositionItemParam{
   914  			Source: &types.Reference{
   915  				HREF: vapptemplate.VAppTemplate.Children.VM[0].HREF,
   916  				Name: check.TestName() + "-vm-tmpl",
   917  			},
   918  			VMGeneralParams: &types.VMGeneralParams{
   919  				Description: "test-vm-description",
   920  			},
   921  			InstantiationParams: &types.InstantiationParams{
   922  				NetworkConnectionSection: &types.NetworkConnectionSection{},
   923  			},
   924  		},
   925  	}
   926  	vm, err := vapp.AddRawVM(vmDef)
   927  	check.Assert(err, IsNil)
   928  	check.Assert(vm, NotNil)
   929  	check.Assert(vm.VM.Name, Equals, vmDef.SourcedItem.Source.Name)
   930  
   931  	// Refresh vApp to have latest state
   932  	err = vapp.Refresh()
   933  	check.Assert(err, IsNil)
   934  
   935  	return vapp, vm
   936  }
   937  
   938  // makeVappGroup creates multiple vApps, each with several VMs,
   939  // as defined in `groupDefinition`.
   940  // Returns a list of vApps
   941  func makeVappGroup(label string, vdc *Vdc, groupDefinition map[string][]string) ([]*VApp, error) {
   942  	var vappList []*VApp
   943  	for vappName, vmNames := range groupDefinition {
   944  		existingVapp, err := vdc.GetVAppByName(vappName, false)
   945  		if err == nil {
   946  
   947  			if existingVapp.VApp.Children == nil || len(existingVapp.VApp.Children.VM) == 0 {
   948  				return nil, fmt.Errorf("found vApp %s but without VMs", vappName)
   949  			}
   950  			foundVms := 0
   951  			for _, vmName := range vmNames {
   952  				for _, existingVM := range existingVapp.VApp.Children.VM {
   953  					if existingVM.Name == vmName {
   954  						foundVms++
   955  					}
   956  				}
   957  			}
   958  			if foundVms < 2 {
   959  				return nil, fmt.Errorf("found vApp %s but with %d VMs instead of 2 ", vappName, foundVms)
   960  			}
   961  
   962  			vappList = append(vappList, existingVapp)
   963  			if testVerbose {
   964  				fmt.Printf("Using existing vApp %s\n", vappName)
   965  			}
   966  			continue
   967  		}
   968  
   969  		if testVerbose {
   970  			fmt.Printf("Creating vApp %s\n", vappName)
   971  		}
   972  		vapp, err := makeEmptyVapp(vdc, vappName, "")
   973  		if err != nil {
   974  			return nil, err
   975  		}
   976  		if os.Getenv("GOVCD_KEEP_TEST_OBJECTS") == "" {
   977  			AddToCleanupList(vappName, "vapp", vdc.Vdc.Name, label)
   978  		}
   979  		for _, vmName := range vmNames {
   980  			if testVerbose {
   981  				fmt.Printf("\tCreating VM %s/%s\n", vappName, vmName)
   982  			}
   983  			_, err := makeEmptyVm(vapp, vmName)
   984  			if err != nil {
   985  				return nil, err
   986  			}
   987  		}
   988  		vappList = append(vappList, vapp)
   989  	}
   990  	return vappList, nil
   991  }