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

     1  //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 || ALL
     2  
     3  /*
     4   * Copyright 2022 VMware, Inc.  All rights reserved.  Licensed under the Apache v2 License.
     5   */
     6  
     7  package govcd
     8  
     9  import (
    10  	"bufio"
    11  	"encoding/json"
    12  	"flag"
    13  	"fmt"
    14  	"gopkg.in/yaml.v2"
    15  	"net/http"
    16  	"net/url"
    17  	"os"
    18  	"path/filepath"
    19  	"regexp"
    20  	"runtime"
    21  	"strings"
    22  	"sync"
    23  	"testing"
    24  	"time"
    25  
    26  	. "gopkg.in/check.v1"
    27  
    28  	"github.com/vmware/go-vcloud-director/v2/types/v56"
    29  	"github.com/vmware/go-vcloud-director/v2/util"
    30  )
    31  
    32  func init() {
    33  	testingTags["api"] = "api_vcd_test.go"
    34  
    35  	// To list the flags when we run "go test -tags functional -vcd-help", the flag name must start with "vcd"
    36  	// They will all appear alongside the native flags when we use an invalid one
    37  	setBoolFlag(&vcdHelp, "vcd-help", "VCD_HELP", "Show vcd flags")
    38  	setBoolFlag(&enableDebug, "vcd-debug", "GOVCD_DEBUG", "enables debug output")
    39  	setBoolFlag(&testVerbose, "vcd-verbose", "GOVCD_TEST_VERBOSE", "enables verbose output")
    40  	setBoolFlag(&skipVappCreation, "vcd-skip-vapp-creation", "GOVCD_SKIP_VAPP_CREATION", "Skips vApp creation")
    41  	setBoolFlag(&ignoreCleanupFile, "vcd-ignore-cleanup-file", "GOVCD_IGNORE_CLEANUP_FILE", "Does not process previous cleanup file")
    42  	setBoolFlag(&debugShowRequestEnabled, "vcd-show-request", "GOVCD_SHOW_REQ", "Shows API request")
    43  	setBoolFlag(&debugShowResponseEnabled, "vcd-show-response", "GOVCD_SHOW_RESP", "Shows API response")
    44  	setBoolFlag(&connectAsOrgUser, "vcd-as-org-user", "VCD_TEST_ORG_USER", "Connect as Org user")
    45  	setBoolFlag(&connectAsOrgUser, "vcd-test-org-user", "VCD_TEST_ORG_USER", "Connect as Org user")
    46  	flag.IntVar(&connectTenantNum, "vcd-connect-tenant", connectTenantNum, "change index of tenant to use (0=first)")
    47  }
    48  
    49  const (
    50  	// Names for entities created by the tests
    51  	TestCreateOrg                 = "TestCreateOrg"
    52  	TestDeleteOrg                 = "TestDeleteOrg"
    53  	TestUpdateOrg                 = "TestUpdateOrg"
    54  	TestCreateCatalog             = "TestCreateCatalog"
    55  	TestCreateCatalogDesc         = "Catalog created by tests"
    56  	TestRefreshOrgFullName        = "TestRefreshOrgFullName"
    57  	TestUpdateCatalog             = "TestUpdateCatalog"
    58  	TestDeleteCatalog             = "TestDeleteCatalog"
    59  	TestRefreshOrg                = "TestRefreshOrg"
    60  	TestComposeVapp               = "TestComposeVapp"
    61  	TestComposeVappDesc           = "vApp created by tests"
    62  	TestSetUpSuite                = "TestSetUpSuite"
    63  	TestUploadOvf                 = "TestUploadOvf"
    64  	TestDeleteCatalogItem         = "TestDeleteCatalogItem"
    65  	TestCreateOrgVdc              = "TestCreateOrgVdc"
    66  	TestRefreshOrgVdc             = "TestRefreshOrgVdc"
    67  	TestCreateOrgVdcNetworkRouted = "TestCreateOrgVdcNetworkRouted"
    68  	TestCreateOrgVdcNetworkDhcp   = "TestCreateOrgVdcNetworkDhcp"
    69  	TestCreateOrgVdcNetworkIso    = "TestCreateOrgVdcNetworkIso"
    70  	TestCreateOrgVdcNetworkDirect = "TestCreateOrgVdcNetworkDirect"
    71  	TestUploadMedia               = "TestUploadMedia"
    72  	TestCatalogUploadMedia        = "TestCatalogUploadMedia"
    73  	TestCreateDisk                = "TestCreateDisk"
    74  	TestUpdateDisk                = "TestUpdateDisk"
    75  	TestDeleteDisk                = "TestDeleteDisk"
    76  	TestRefreshDisk               = "TestRefreshDisk"
    77  	TestAttachedVMDisk            = "TestAttachedVMDisk"
    78  	TestVdcFindDiskByHREF         = "TestVdcFindDiskByHREF"
    79  	TestFindDiskByHREF            = "TestFindDiskByHREF"
    80  	TestDisk                      = "TestDisk"
    81  	// #nosec G101 -- Not a credential
    82  	TestVMAttachOrDetachDisk  = "TestVMAttachOrDetachDisk"
    83  	TestVMAttachDisk          = "TestVMAttachDisk"
    84  	TestVMDetachDisk          = "TestVMDetachDisk"
    85  	TestCreateExternalNetwork = "TestCreateExternalNetwork"
    86  	TestDeleteExternalNetwork = "TestDeleteExternalNetwork"
    87  	TestLbServiceMonitor      = "TestLbServiceMonitor"
    88  	TestLbServerPool          = "TestLbServerPool"
    89  	TestLbAppProfile          = "TestLbAppProfile"
    90  	TestLbAppRule             = "TestLbAppRule"
    91  	TestLbVirtualServer       = "TestLbVirtualServer"
    92  	TestLb                    = "TestLb"
    93  	TestNsxvSnatRule          = "TestNsxvSnatRule"
    94  	TestNsxvDnatRule          = "TestNsxvDnatRule"
    95  )
    96  
    97  const (
    98  	TestRequiresSysAdminPrivileges = "Test %s requires system administrator privileges"
    99  )
   100  
   101  type Tenant struct {
   102  	User     string `yaml:"user,omitempty"`
   103  	Password string `yaml:"password,omitempty"`
   104  	Token    string `yaml:"token,omitempty"`
   105  	ApiToken string `yaml:"api_token,omitempty"`
   106  	SysOrg   string `yaml:"sysOrg,omitempty"`
   107  }
   108  
   109  // Struct to get info from a config yaml file that the user
   110  // specifies
   111  type TestConfig struct {
   112  	Provider struct {
   113  		User       string `yaml:"user"`
   114  		Password   string `yaml:"password"`
   115  		Token      string `yaml:"token"`
   116  		ApiToken   string `yaml:"api_token"`
   117  		VcdVersion string `yaml:"vcdVersion,omitempty"`
   118  		ApiVersion string `yaml:"apiVersion,omitempty"`
   119  
   120  		// UseSamlAdfs specifies if SAML auth is used for authenticating vCD instead of local login.
   121  		// The above `User` and `Password` will be used to authenticate against ADFS IdP when true.
   122  		UseSamlAdfs bool `yaml:"useSamlAdfs"`
   123  
   124  		// CustomAdfsRptId allows to set custom Relaying Party Trust identifier if needed. Only has
   125  		// effect if `UseSamlAdfs` is true.
   126  		CustomAdfsRptId string `yaml:"customAdfsRptId"`
   127  
   128  		// The variables `SamlUser`, `SamlPassword` and `SamlCustomRptId` are optional and are
   129  		// related to an additional test run specifically with SAML user/password. It is useful in
   130  		// case local user is used for test run (defined by above 'User', 'Password' variables).
   131  		// SamlUser takes ADFS friendly format ('contoso.com\username' or 'username@contoso.com')
   132  		SamlUser        string `yaml:"samlUser,omitempty"`
   133  		SamlPassword    string `yaml:"samlPassword,omitempty"`
   134  		SamlCustomRptId string `yaml:"samlCustomRptId,omitempty"`
   135  
   136  		Url             string `yaml:"url"`
   137  		SysOrg          string `yaml:"sysOrg"`
   138  		MaxRetryTimeout int    `yaml:"maxRetryTimeout,omitempty"`
   139  		HttpTimeout     int64  `yaml:"httpTimeout,omitempty"`
   140  	}
   141  	Tenants []Tenant `yaml:"tenants,omitempty"`
   142  	VCD     struct {
   143  		Org         string `yaml:"org"`
   144  		Vdc         string `yaml:"vdc"`
   145  		ProviderVdc struct {
   146  			Name           string `yaml:"name"`
   147  			StorageProfile string `yaml:"storage_profile"`
   148  			NetworkPool    string `yaml:"network_pool"`
   149  		} `yaml:"provider_vdc"`
   150  		NsxtProviderVdc struct {
   151  			Name                   string `yaml:"name"`
   152  			StorageProfile         string `yaml:"storage_profile"`
   153  			StorageProfile2        string `yaml:"storage_profile_2"`
   154  			NetworkPool            string `yaml:"network_pool"`
   155  			PlacementPolicyVmGroup string `yaml:"placementPolicyVmGroup,omitempty"`
   156  		} `yaml:"nsxt_provider_vdc"`
   157  		Catalog struct {
   158  			Name                      string `yaml:"name,omitempty"`
   159  			NsxtBackedCatalogName     string `yaml:"nsxtBackedCatalogName,omitempty"`
   160  			Description               string `yaml:"description,omitempty"`
   161  			CatalogItem               string `yaml:"catalogItem,omitempty"`
   162  			CatalogItemWithEfiSupport string `yaml:"catalogItemWithEfiSupport,omitempty"`
   163  			NsxtCatalogItem           string `yaml:"nsxtCatalogItem,omitempty"`
   164  			CatalogItemDescription    string `yaml:"catalogItemDescription,omitempty"`
   165  			CatalogItemWithMultiVms   string `yaml:"catalogItemWithMultiVms,omitempty"`
   166  			VmNameInMultiVmItem       string `yaml:"vmNameInMultiVmItem,omitempty"`
   167  		} `yaml:"catalog"`
   168  		Network struct {
   169  			Net1 string `yaml:"network1"`
   170  			Net2 string `yaml:"network2,omitempty"`
   171  		} `yaml:"network"`
   172  		StorageProfile struct {
   173  			SP1 string `yaml:"storageProfile1"`
   174  			SP2 string `yaml:"storageProfile2,omitempty"`
   175  		} `yaml:"storageProfile"`
   176  		ExternalIp                   string `yaml:"externalIp,omitempty"`
   177  		ExternalNetmask              string `yaml:"externalNetmask,omitempty"`
   178  		InternalIp                   string `yaml:"internalIp,omitempty"`
   179  		InternalNetmask              string `yaml:"internalNetmask,omitempty"`
   180  		EdgeGateway                  string `yaml:"edgeGateway,omitempty"`
   181  		ExternalNetwork              string `yaml:"externalNetwork,omitempty"`
   182  		ExternalNetworkPortGroup     string `yaml:"externalNetworkPortGroup,omitempty"`
   183  		ExternalNetworkPortGroupType string `yaml:"externalNetworkPortGroupType,omitempty"`
   184  		VimServer                    string `yaml:"vimServer,omitempty"`
   185  		LdapServer                   string `yaml:"ldapServer,omitempty"`
   186  		Nsxt                         struct {
   187  			Manager                   string `yaml:"manager"`
   188  			Tier0router               string `yaml:"tier0router"`
   189  			Tier0routerVrf            string `yaml:"tier0routerVrf"`
   190  			NsxtDvpg                  string `yaml:"nsxtDvpg"`
   191  			GatewayQosProfile         string `yaml:"gatewayQosProfile"`
   192  			Vdc                       string `yaml:"vdc"`
   193  			ExternalNetwork           string `yaml:"externalNetwork"`
   194  			EdgeGateway               string `yaml:"edgeGateway"`
   195  			NsxtImportSegment         string `yaml:"nsxtImportSegment"`
   196  			NsxtImportSegment2        string `yaml:"nsxtImportSegment2"`
   197  			VdcGroup                  string `yaml:"vdcGroup"`
   198  			VdcGroupEdgeGateway       string `yaml:"vdcGroupEdgeGateway"`
   199  			NsxtEdgeCluster           string `yaml:"nsxtEdgeCluster"`
   200  			RoutedNetwork             string `yaml:"routedNetwork"`
   201  			NsxtAlbControllerUrl      string `yaml:"nsxtAlbControllerUrl"`
   202  			NsxtAlbControllerUser     string `yaml:"nsxtAlbControllerUser"`
   203  			NsxtAlbControllerPassword string `yaml:"nsxtAlbControllerPassword"`
   204  			NsxtAlbImportableCloud    string `yaml:"nsxtAlbImportableCloud"`
   205  			NsxtAlbServiceEngineGroup string `yaml:"nsxtAlbServiceEngineGroup"`
   206  			IpDiscoveryProfile        string `yaml:"ipDiscoveryProfile"`
   207  			MacDiscoveryProfile       string `yaml:"macDiscoveryProfile"`
   208  			SpoofGuardProfile         string `yaml:"spoofGuardProfile"`
   209  			QosProfile                string `yaml:"qosProfile"`
   210  			SegmentSecurityProfile    string `yaml:"segmentSecurityProfile"`
   211  		} `yaml:"nsxt"`
   212  	} `yaml:"vcd"`
   213  	Vsphere struct {
   214  		ResourcePoolForVcd1 string `yaml:"resourcePoolForVcd1,omitempty"`
   215  		ResourcePoolForVcd2 string `yaml:"resourcePoolForVcd2,omitempty"`
   216  	} `yaml:"vsphere,omitempty"`
   217  	Logging struct {
   218  		Enabled          bool   `yaml:"enabled,omitempty"`
   219  		LogFileName      string `yaml:"logFileName,omitempty"`
   220  		LogHttpRequest   bool   `yaml:"logHttpRequest,omitempty"`
   221  		LogHttpResponse  bool   `yaml:"logHttpResponse,omitempty"`
   222  		SkipResponseTags string `yaml:"skipResponseTags,omitempty"`
   223  		ApiLogFunctions  string `yaml:"apiLogFunctions,omitempty"`
   224  		VerboseCleanup   bool   `yaml:"verboseCleanup,omitempty"`
   225  	} `yaml:"logging"`
   226  	OVA struct {
   227  		OvaPath            string `yaml:"ovaPath,omitempty"`
   228  		OvaChunkedPath     string `yaml:"ovaChunkedPath,omitempty"`
   229  		OvaMultiVmPath     string `yaml:"ovaMultiVmPath,omitempty"`
   230  		OvaWithoutSizePath string `yaml:"ovaWithoutSizePath,omitempty"`
   231  		OvfPath            string `yaml:"ovfPath,omitempty"`
   232  		OvfUrl             string `yaml:"ovfUrl,omitempty"`
   233  	} `yaml:"ova"`
   234  	Media struct {
   235  		MediaPath        string `yaml:"mediaPath,omitempty"`
   236  		Media            string `yaml:"mediaName,omitempty"`
   237  		NsxtMedia        string `yaml:"nsxtBackedMediaName,omitempty"`
   238  		PhotonOsOvaPath  string `yaml:"photonOsOvaPath,omitempty"`
   239  		MediaUdfTypePath string `yaml:"mediaUdfTypePath,omitempty"`
   240  		UiPluginPath     string `yaml:"uiPluginPath,omitempty"`
   241  	} `yaml:"media"`
   242  	Cse struct {
   243  		Version        string `yaml:"version,omitempty"`
   244  		SolutionsOrg   string `yaml:"solutionsOrg,omitempty"`
   245  		TenantOrg      string `yaml:"tenantOrg,omitempty"`
   246  		TenantVdc      string `yaml:"tenantVdc,omitempty"`
   247  		RoutedNetwork  string `yaml:"routedNetwork,omitempty"`
   248  		EdgeGateway    string `yaml:"edgeGateway,omitempty"`
   249  		StorageProfile string `yaml:"storageProfile,omitempty"`
   250  		OvaCatalog     string `yaml:"ovaCatalog,omitempty"`
   251  		OvaName        string `yaml:"ovaName,omitempty"`
   252  	} `yaml:"cse,omitempty"`
   253  }
   254  
   255  // Test struct for vcloud-director.
   256  // Test functions use the struct to get
   257  // an org, vdc, vapp, and client to run
   258  // tests on
   259  type TestVCD struct {
   260  	client         *VCDClient
   261  	org            *Org
   262  	vdc            *Vdc
   263  	nsxtVdc        *Vdc
   264  	vapp           *VApp
   265  	config         TestConfig
   266  	skipVappTests  bool
   267  	skipAdminTests bool
   268  }
   269  
   270  // Cleanup entity structure used by the tear-down procedure
   271  // at the end of the tests to remove leftover entities
   272  type CleanupEntity struct {
   273  	Name            string
   274  	EntityType      string
   275  	Parent          string
   276  	CreatedBy       string
   277  	OpenApiEndpoint string
   278  }
   279  
   280  // CleanupInfo is the data used to persist an entity list in a file
   281  type CleanupInfo struct {
   282  	VcdIp      string          // IP of the vCD where the test runs
   283  	Info       string          // Information about this file
   284  	EntityList []CleanupEntity // List of entities to remove
   285  }
   286  
   287  // Internally used by the test suite to run tests based on TestVCD structures
   288  var _ = Suite(&TestVCD{})
   289  
   290  // The list holding the entities to be examined and eventually removed
   291  // at the end of the tests
   292  var cleanupEntityList []CleanupEntity
   293  
   294  // Lock for of cleanup entities persistent file
   295  var persistentCleanupListLock sync.Mutex
   296  
   297  // IP of the vCD being tested. It is initialized at the first client authentication
   298  var persistentCleanupIp string
   299  
   300  // Use this value to run a specific test that does not need a pre-created vApp.
   301  var skipVappCreation bool = os.Getenv("GOVCD_SKIP_VAPP_CREATION") != ""
   302  
   303  // vcdHelp shows command line options
   304  var vcdHelp bool
   305  
   306  // enableDebug enables debug output
   307  var enableDebug bool
   308  
   309  // ignoreCleanupFile prevents processing a previous cleanup file
   310  var ignoreCleanupFile bool
   311  
   312  // connectAsOrgUser connects as Org user instead of System administrator
   313  var connectAsOrgUser bool
   314  var connectTenantNum int
   315  
   316  // Makes the name for the cleanup entities persistent file
   317  // Using a name for each vCD allows us to run tests with different servers
   318  // and persist the cleanup list for all.
   319  func makePersistentCleanupFileName() string {
   320  	var persistentCleanupListMask = "test_cleanup_list-%s.%s"
   321  	if persistentCleanupIp == "" {
   322  		fmt.Printf("######## Persistent Cleanup IP was not set ########\n")
   323  		os.Exit(1)
   324  	}
   325  	reForbiddenChars := regexp.MustCompile(`[/]`)
   326  	fileName := fmt.Sprintf(persistentCleanupListMask,
   327  		reForbiddenChars.ReplaceAllString(persistentCleanupIp, ""), "json")
   328  	return fileName
   329  
   330  }
   331  
   332  // Removes the list of cleanup entities
   333  // To be called only after the list has been processed
   334  func removePersistentCleanupList() {
   335  	persistentCleanupListFile := makePersistentCleanupFileName()
   336  	persistentCleanupListLock.Lock()
   337  	defer persistentCleanupListLock.Unlock()
   338  	_, err := os.Stat(persistentCleanupListFile)
   339  	if os.IsNotExist(err) {
   340  		return
   341  	}
   342  	_ = os.Remove(persistentCleanupListFile)
   343  }
   344  
   345  // Reads a cleanup list from file
   346  func readCleanupList() ([]CleanupEntity, error) {
   347  	persistentCleanupListFile := makePersistentCleanupFileName()
   348  	persistentCleanupListLock.Lock()
   349  	defer persistentCleanupListLock.Unlock()
   350  	var cleanupInfo CleanupInfo
   351  	_, err := os.Stat(persistentCleanupListFile)
   352  	if os.IsNotExist(err) {
   353  		return nil, err
   354  	}
   355  	listText, err := os.ReadFile(filepath.Clean(persistentCleanupListFile))
   356  	if err != nil {
   357  		return nil, err
   358  	}
   359  	err = json.Unmarshal(listText, &cleanupInfo)
   360  	return cleanupInfo.EntityList, err
   361  }
   362  
   363  // Writes a cleanup list to file.
   364  // If the test suite terminates without having a chance to
   365  // clean up properly, the next run of the test suite will try to
   366  // remove the leftovers
   367  func writeCleanupList(cleanupList []CleanupEntity) error {
   368  	persistentCleanupListFile := makePersistentCleanupFileName()
   369  	persistentCleanupListLock.Lock()
   370  	defer persistentCleanupListLock.Unlock()
   371  	cleanupInfo := CleanupInfo{
   372  		VcdIp: persistentCleanupIp,
   373  		Info: "Persistent list of entities to be destroyed. " +
   374  			" If this file is found when starting the tests, its entities will be " +
   375  			" processed for removal before any other operation.",
   376  		EntityList: cleanupList,
   377  	}
   378  	listText, err := json.MarshalIndent(cleanupInfo, " ", " ")
   379  	if err != nil {
   380  		return err
   381  	}
   382  	file, err := os.Create(filepath.Clean(persistentCleanupListFile))
   383  	if err != nil {
   384  		return err
   385  	}
   386  	writer := bufio.NewWriter(file)
   387  	count, err := writer.Write(listText)
   388  	if err != nil || count == 0 {
   389  		return err
   390  	}
   391  	err = writer.Flush()
   392  	if err != nil {
   393  		return err
   394  	}
   395  	return file.Close()
   396  }
   397  
   398  // AddToCleanupList adds an entity to the cleanup list.
   399  // To be called by all tests when a new entity has been created, before
   400  // running any other operation.
   401  // Items in the list will be deleted at the end of the tests if they still exist.
   402  func AddToCleanupList(name, entityType, parent, createdBy string) {
   403  	for _, item := range cleanupEntityList {
   404  		// avoid adding the same item twice
   405  		if item.Name == name && item.EntityType == entityType {
   406  			return
   407  		}
   408  	}
   409  	cleanupEntityList = append(cleanupEntityList, CleanupEntity{Name: name, EntityType: entityType, Parent: parent, CreatedBy: createdBy})
   410  	err := writeCleanupList(cleanupEntityList)
   411  	if err != nil {
   412  		fmt.Printf("################ error writing cleanup list %s\n", err)
   413  	}
   414  }
   415  
   416  // PrependToCleanupList prepends an entity to the cleanup list.
   417  // To be called by all tests when a new entity has been created, before
   418  // running any other operation.
   419  // Items in the list will be deleted at the end of the tests if they still exist.
   420  func PrependToCleanupList(name, entityType, parent, createdBy string) {
   421  	for _, item := range cleanupEntityList {
   422  		// avoid adding the same item twice
   423  		if item.Name == name && item.EntityType == entityType {
   424  			return
   425  		}
   426  	}
   427  	cleanupEntityList = append([]CleanupEntity{{Name: name, EntityType: entityType, Parent: parent, CreatedBy: createdBy}}, cleanupEntityList...)
   428  	err := writeCleanupList(cleanupEntityList)
   429  	if err != nil {
   430  		fmt.Printf("################ error writing cleanup list %s\n", err)
   431  	}
   432  }
   433  
   434  // AddToCleanupListOpenApi adds an OpenAPI entity OpenApi objects `entityType=OpenApiEntity` and `openApiEndpoint`should
   435  // be set in format "types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointOrgVdcNetworks + ID"
   436  func AddToCleanupListOpenApi(name, createdBy, openApiEndpoint string) {
   437  	for _, item := range cleanupEntityList {
   438  		// avoid adding the same item twice
   439  		if item.OpenApiEndpoint == openApiEndpoint {
   440  			return
   441  		}
   442  	}
   443  	cleanupEntityList = append(cleanupEntityList, CleanupEntity{Name: name, EntityType: "OpenApiEntity", CreatedBy: createdBy, OpenApiEndpoint: openApiEndpoint})
   444  	err := writeCleanupList(cleanupEntityList)
   445  	if err != nil {
   446  		fmt.Printf("################ error writing cleanup list %s\n", err)
   447  	}
   448  }
   449  
   450  // PrependToCleanupListOpenApi prepends an OpenAPI entity OpenApi objects `entityType=OpenApiEntity` and
   451  // `openApiEndpoint`should be set in format "types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointOrgVdcNetworks + ID"
   452  func PrependToCleanupListOpenApi(name, createdBy, openApiEndpoint string) {
   453  	for _, item := range cleanupEntityList {
   454  		// avoid adding the same item twice
   455  		if item.OpenApiEndpoint == openApiEndpoint {
   456  			return
   457  		}
   458  	}
   459  	cleanupEntityList = append([]CleanupEntity{{Name: name, EntityType: "OpenApiEntity", CreatedBy: createdBy, OpenApiEndpoint: openApiEndpoint}}, cleanupEntityList...)
   460  	err := writeCleanupList(cleanupEntityList)
   461  	if err != nil {
   462  		fmt.Printf("################ error writing cleanup list %s\n", err)
   463  	}
   464  }
   465  
   466  // Users use the environmental variable GOVCD_CONFIG as
   467  // a config file for testing. Otherwise the default is govcd_test_config.yaml
   468  // in the current directory. Throws an error if it cannot find your
   469  // yaml file or if it cannot read it.
   470  func GetConfigStruct() (TestConfig, error) {
   471  	config := os.Getenv("GOVCD_CONFIG")
   472  	var configStruct TestConfig
   473  	if config == "" {
   474  		// Finds the current directory, through the path of this running test
   475  		_, currentFilename, _, _ := runtime.Caller(0)
   476  		currentDirectory := filepath.Dir(currentFilename)
   477  		config = currentDirectory + "/govcd_test_config.yaml"
   478  	}
   479  	// Looks if the configuration file exists before attempting to read it
   480  	_, err := os.Stat(config)
   481  	if os.IsNotExist(err) {
   482  		return TestConfig{}, fmt.Errorf("Configuration file %s not found: %s", config, err)
   483  	}
   484  	yamlFile, err := os.ReadFile(filepath.Clean(config))
   485  	if err != nil {
   486  		return TestConfig{}, fmt.Errorf("could not read config file %s: %s", config, err)
   487  	}
   488  	err = yaml.Unmarshal(yamlFile, &configStruct)
   489  	if err != nil {
   490  		return TestConfig{}, fmt.Errorf("could not unmarshal yaml file: %s", err)
   491  	}
   492  	if connectAsOrgUser {
   493  		if len(configStruct.Tenants) == 0 {
   494  			return TestConfig{}, fmt.Errorf("org user connection required, but 'tenants[%d]' is empty", connectTenantNum)
   495  		}
   496  		if connectTenantNum > len(configStruct.Tenants)-1 {
   497  			return TestConfig{}, fmt.Errorf("org user connection required, but tenant number %d is higher than the number of tenants ", connectTenantNum)
   498  		}
   499  		// Change configStruct.Provider, to reuse the global fields, such as URL
   500  		configStruct.Provider.User = configStruct.Tenants[connectTenantNum].User
   501  		configStruct.Provider.Password = configStruct.Tenants[connectTenantNum].Password
   502  		configStruct.Provider.SysOrg = configStruct.Tenants[connectTenantNum].SysOrg
   503  		configStruct.Provider.Token = configStruct.Tenants[connectTenantNum].Token
   504  		configStruct.Provider.ApiToken = configStruct.Tenants[connectTenantNum].ApiToken
   505  	}
   506  	return configStruct, nil
   507  }
   508  
   509  // Creates a VCDClient based on the endpoint given in the TestConfig argument.
   510  // TestConfig struct can be obtained by calling GetConfigStruct. Throws an error
   511  // if endpoint given is not a valid url.
   512  func GetTestVCDFromYaml(testConfig TestConfig, options ...VCDClientOption) (*VCDClient, error) {
   513  	configUrl, err := url.ParseRequestURI(testConfig.Provider.Url)
   514  	if err != nil {
   515  		return &VCDClient{}, fmt.Errorf("could not parse Url: %s", err)
   516  	}
   517  
   518  	if testConfig.Provider.MaxRetryTimeout != 0 {
   519  		options = append(options, WithMaxRetryTimeout(testConfig.Provider.MaxRetryTimeout))
   520  	}
   521  
   522  	if testConfig.Provider.HttpTimeout != 0 {
   523  		options = append(options, WithHttpTimeout(testConfig.Provider.HttpTimeout))
   524  	}
   525  
   526  	if testConfig.Provider.UseSamlAdfs {
   527  		options = append(options, WithSamlAdfs(true, testConfig.Provider.CustomAdfsRptId))
   528  	}
   529  
   530  	return NewVCDClient(*configUrl, true, options...), nil
   531  }
   532  
   533  // Necessary to enable the suite tests with TestVCD
   534  func Test(t *testing.T) { TestingT(t) }
   535  
   536  // Sets the org, vdc, vapp, and vcdClient for a
   537  // TestVCD struct. An error is thrown if something goes wrong
   538  // getting config file, creating vcd, during authentication, or
   539  // when creating a new vapp. If this method panics, no test
   540  // case that uses the TestVCD struct is run.
   541  func (vcd *TestVCD) SetUpSuite(check *C) {
   542  	flag.Parse()
   543  	setTestEnv()
   544  	if vcdHelp {
   545  		fmt.Println("vcd flags:")
   546  		fmt.Println()
   547  		// Prints only the flags defined in this package
   548  		flag.CommandLine.VisitAll(func(f *flag.Flag) {
   549  			if strings.HasPrefix(f.Name, "vcd-") {
   550  				fmt.Printf("  -%-40s %s (%v)\n", f.Name, f.Usage, f.Value)
   551  			}
   552  		})
   553  		fmt.Println()
   554  		// This will skip the whole suite.
   555  		// Instead, running os.Exit(0) will panic
   556  		check.Skip("--- showing help ---")
   557  	}
   558  	config, err := GetConfigStruct()
   559  	if config.Provider.Url == "" || err != nil {
   560  		panic(err)
   561  	}
   562  	vcd.config = config
   563  
   564  	// This library sets HTTP User-Agent to be `go-vcloud-director` by default and all HTTP calls
   565  	// expected to contain this header. An explicit test cannot capture future HTTP requests, but
   566  	// of them should use logging so this should be a good 'gate' to ensure ALL HTTP calls going out
   567  	// of this library do include HTTP User-Agent.
   568  	util.TogglePanicEmptyUserAgent(true)
   569  
   570  	if vcd.config.Logging.Enabled {
   571  		util.EnableLogging = true
   572  		if vcd.config.Logging.LogFileName != "" {
   573  			util.ApiLogFileName = vcd.config.Logging.LogFileName
   574  		}
   575  		if vcd.config.Logging.LogHttpRequest {
   576  			util.LogHttpRequest = true
   577  		}
   578  		if vcd.config.Logging.LogHttpResponse {
   579  			util.LogHttpResponse = true
   580  		}
   581  		if vcd.config.Logging.SkipResponseTags != "" {
   582  			util.SetSkipTags(vcd.config.Logging.SkipResponseTags)
   583  		}
   584  		if vcd.config.Logging.ApiLogFunctions != "" {
   585  			util.SetApiLogFunctions(vcd.config.Logging.ApiLogFunctions)
   586  		}
   587  	} else {
   588  		util.EnableLogging = false
   589  	}
   590  	util.SetLog()
   591  	vcdClient, err := GetTestVCDFromYaml(config)
   592  	if vcdClient == nil || err != nil {
   593  		panic(err)
   594  	}
   595  	vcd.client = vcdClient
   596  	token := os.Getenv("VCD_TOKEN")
   597  	if token == "" {
   598  		token = config.Provider.Token
   599  	}
   600  
   601  	apiToken := os.Getenv("VCD_API_TOKEN")
   602  	if apiToken == "" {
   603  		apiToken = config.Provider.ApiToken
   604  	}
   605  
   606  	authenticationMode := "password"
   607  	if apiToken != "" {
   608  		authenticationMode = "API-token"
   609  		err = vcd.client.SetToken(config.Provider.SysOrg, ApiTokenHeader, apiToken)
   610  	} else {
   611  		if token != "" {
   612  			authenticationMode = "token"
   613  			err = vcd.client.SetToken(config.Provider.SysOrg, AuthorizationHeader, token)
   614  		} else {
   615  			err = vcd.client.Authenticate(config.Provider.User, config.Provider.Password, config.Provider.SysOrg)
   616  		}
   617  	}
   618  	if config.Provider.UseSamlAdfs {
   619  		authenticationMode = "SAML password"
   620  	}
   621  	if err != nil {
   622  		panic(err)
   623  	}
   624  	versionInfo := ""
   625  	version, versionTime, err := vcd.client.Client.GetVcdVersion()
   626  	if err == nil {
   627  		versionInfo = fmt.Sprintf("version %s built at %s", version, versionTime)
   628  	}
   629  	fmt.Printf("Running on VCD %s (%s)\nas user %s@%s (using %s)\n", vcd.config.Provider.Url, versionInfo,
   630  		vcd.config.Provider.User, vcd.config.Provider.SysOrg, authenticationMode)
   631  	if !vcd.client.Client.IsSysAdmin {
   632  		vcd.skipAdminTests = true
   633  	}
   634  
   635  	// Sets the vCD IP value, removing the elements that would
   636  	// not be appropriate in a file name
   637  	reHttp := regexp.MustCompile(`^https?://`)
   638  	reApi := regexp.MustCompile(`/api/?`)
   639  	persistentCleanupIp = vcd.config.Provider.Url
   640  	persistentCleanupIp = reHttp.ReplaceAllString(persistentCleanupIp, "")
   641  	persistentCleanupIp = reApi.ReplaceAllString(persistentCleanupIp, "")
   642  	// set org
   643  	vcd.org, err = vcd.client.GetOrgByName(config.VCD.Org)
   644  	if err != nil {
   645  		fmt.Printf("error retrieving org %s: %s\n", config.VCD.Org, err)
   646  		os.Exit(1)
   647  	}
   648  	// set vdc
   649  	vcd.vdc, err = vcd.org.GetVDCByName(config.VCD.Vdc, false)
   650  	if err != nil || vcd.vdc == nil {
   651  		panic(err)
   652  	}
   653  
   654  	// configure NSX-T VDC for convenience if it is specified in configuration
   655  	if config.VCD.Nsxt.Vdc != "" {
   656  		vcd.nsxtVdc, err = vcd.org.GetVDCByName(config.VCD.Nsxt.Vdc, false)
   657  		if err != nil {
   658  			panic(fmt.Errorf("error geting NSX-T VDC '%s': %s", config.VCD.Nsxt.Vdc, err))
   659  		}
   660  	}
   661  
   662  	// If neither the vApp or VM tags are set, we also skip the
   663  	// creation of the default vApp
   664  	if !isTagSet("vapp") && !isTagSet("vm") {
   665  		// vcd.skipVappTests = true
   666  		skipVappCreation = true
   667  	}
   668  
   669  	// Gets the persistent cleanup list from file, if exists.
   670  	cleanupList, err := readCleanupList()
   671  	if len(cleanupList) > 0 && err == nil {
   672  		if !ignoreCleanupFile {
   673  			// If we found a cleanup file and we want to process it (default)
   674  			// We proceed to cleanup the leftovers before any other operation
   675  			fmt.Printf("*** Found cleanup file %s\n", makePersistentCleanupFileName())
   676  			for i, cleanupEntity := range cleanupList {
   677  				fmt.Printf("# %d ", i+1)
   678  				vcd.removeLeftoverEntities(cleanupEntity)
   679  			}
   680  		}
   681  		removePersistentCleanupList()
   682  	}
   683  
   684  	// creates a new VApp for vapp tests
   685  	if !skipVappCreation && config.VCD.Network.Net1 != "" && config.VCD.StorageProfile.SP1 != "" &&
   686  		config.VCD.Catalog.Name != "" && config.VCD.Catalog.CatalogItem != "" {
   687  		// deployVappForTest replaces the old createTestVapp() because it was using bad implemented method vdc.ComposeVApp
   688  		vcd.vapp, err = deployVappForTest(vcd, TestSetUpSuite)
   689  		// If no vApp is created, we skip all vApp tests
   690  		if err != nil {
   691  			fmt.Printf("%s\n", err)
   692  			panic("Creation failed - Bailing out")
   693  		}
   694  		if vcd.vapp == nil {
   695  			fmt.Printf("Creation of vApp %s failed unexpectedly. No error was reported, but vApp is empty\n", TestSetUpSuite)
   696  			panic("initial vApp is empty - bailing out")
   697  		}
   698  	} else {
   699  		vcd.skipVappTests = true
   700  		fmt.Println("Skipping all vapp tests because one of the following wasn't given: Network, StorageProfile, Catalog, Catalogitem")
   701  	}
   702  }
   703  
   704  // Shows the detail of cleanup operations only if the relevant verbosity
   705  // has been enabled
   706  func (vcd *TestVCD) infoCleanup(format string, args ...interface{}) {
   707  	if vcd.config.Logging.VerboseCleanup {
   708  		fmt.Printf(format, args...)
   709  	}
   710  }
   711  
   712  func getOrgVdcByNames(vcd *TestVCD, orgName, vdcName string) (*Org, *Vdc, error) {
   713  	if orgName == "" || vdcName == "" {
   714  		return nil, nil, fmt.Errorf("orgName, vdcName cant be empty")
   715  	}
   716  
   717  	org, _ := vcd.client.GetOrgByName(orgName)
   718  	if org == nil {
   719  		vcd.infoCleanup("could not find org '%s'", orgName)
   720  		return nil, nil, fmt.Errorf("can't find org")
   721  	}
   722  	vdc, err := org.GetVDCByName(vdcName, false)
   723  	if err != nil {
   724  		vcd.infoCleanup("could not find vdc '%s'", vdcName)
   725  		return nil, nil, fmt.Errorf("can't find vdc")
   726  	}
   727  
   728  	return org, vdc, nil
   729  }
   730  
   731  func getOrgVdcEdgeByNames(vcd *TestVCD, orgName, vdcName, edgeName string) (*Org, *Vdc, *EdgeGateway, error) {
   732  	if orgName == "" || vdcName == "" || edgeName == "" {
   733  		return nil, nil, nil, fmt.Errorf("orgName, vdcName, edgeName cant be empty")
   734  	}
   735  
   736  	org, vdc, err := getOrgVdcByNames(vcd, orgName, vdcName)
   737  	if err != nil {
   738  		return nil, nil, nil, err
   739  	}
   740  
   741  	edge, err := vdc.GetEdgeGatewayByName(edgeName, false)
   742  
   743  	if err != nil {
   744  		vcd.infoCleanup("could not find edge '%s': %s", edgeName, err)
   745  	}
   746  	return org, vdc, edge, nil
   747  }
   748  
   749  var splitParentNotFound string = "removeLeftoverEntries: [ERROR] missing parent info (%s). The parent fields must be defined with a separator '|'\n"
   750  var notFoundMsg string = "removeLeftoverEntries: [INFO] No action for %s '%s'\n"
   751  
   752  func (vcd *TestVCD) getAdminOrgAndVdcFromCleanupEntity(entity CleanupEntity) (org *AdminOrg, vdc *Vdc, err error) {
   753  	orgName, vdcName, _ := splitParent(entity.Parent, "|")
   754  	if orgName == "" || vdcName == "" {
   755  		vcd.infoCleanup(splitParentNotFound, entity.Parent)
   756  		return nil, nil, fmt.Errorf("can't find parents names")
   757  	}
   758  	org, err = vcd.client.GetAdminOrgByName(orgName)
   759  	if err != nil {
   760  		vcd.infoCleanup(notFoundMsg, "org", orgName)
   761  		return nil, nil, fmt.Errorf("can't find org")
   762  	}
   763  	vdc, err = org.GetVDCByName(vdcName, false)
   764  	if vdc == nil || err != nil {
   765  		vcd.infoCleanup(notFoundMsg, "vdc", vdcName)
   766  		return nil, nil, fmt.Errorf("can't find vdc")
   767  	}
   768  	return org, vdc, nil
   769  }
   770  
   771  // Removes leftover entities that may still exist after failed tests
   772  // or the ones that were explicitly created for several tests and
   773  // were relying on this procedure to clean up at the end.
   774  func (vcd *TestVCD) removeLeftoverEntities(entity CleanupEntity) {
   775  	var introMsg string = "removeLeftoverEntries: [INFO] Attempting cleanup of %s '%s' instantiated by %s\n"
   776  	var removedMsg string = "removeLeftoverEntries: [INFO] Removed %s '%s' created by %s\n"
   777  	var notDeletedMsg string = "removeLeftoverEntries: [ERROR] Error deleting %s '%s': %s\n"
   778  	// NOTE: this is a cleanup function that should continue even if errors are found.
   779  	// For this reason, the [ERROR] messages won't be followed by a program termination
   780  	vcd.infoCleanup(introMsg, entity.EntityType, entity.Name, entity.CreatedBy)
   781  	switch entity.EntityType {
   782  
   783  	// openApiEntity can be used to delete any OpenAPI entity due to the API being uniform and allowing the same
   784  	// low level OpenApiDeleteItem()
   785  	case "OpenApiEntity":
   786  		// entity.OpenApiEndpoint contains "endpoint/{ID}"
   787  		// (in format types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointOrgVdcNetworks + ID) but
   788  		// to lookup used API version this ID must not be present therefore below we remove suffix ID.
   789  		// This is done by splitting whole path by "/" and rebuilding path again without last element in slice (which is
   790  		// expected to be the ID)
   791  		// Sometimes API endpoint path might contain URNs in the middle (e.g. OpenApiEndpointNsxtNatRules). They are
   792  		// replaced back to string placeholder %s to match original definitions
   793  		endpointSlice := strings.Split(entity.OpenApiEndpoint, "/")
   794  		endpointWithUuid := strings.Join(endpointSlice[:len(endpointSlice)-1], "/") + "/"
   795  		// replace any "urns" (e.g. 'urn:vcloud:gateway:64966c36-e805-44e2-980b-c1077ab54956') with '%s' to match API definitions
   796  		re := regexp.MustCompile(`urn[^\/]+`) // Regexp matches from 'urn' up to next '/' in the path
   797  		endpointRemovedUuids := re.ReplaceAllString(endpointWithUuid, "%s")
   798  		apiVersion, _ := vcd.client.Client.checkOpenApiEndpointCompatibility(endpointRemovedUuids)
   799  
   800  		// Build UP complete endpoint address
   801  		urlRef, err := vcd.client.Client.OpenApiBuildEndpoint(entity.OpenApiEndpoint)
   802  		if err != nil {
   803  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
   804  			return
   805  		}
   806  
   807  		// Validate if the resource still exists
   808  		err = vcd.client.Client.OpenApiGetItem(apiVersion, urlRef, nil, nil, nil)
   809  
   810  		// RDE Framework has a bug in VCD 10.3.0 that causes "not found" errors to return as "400 bad request",
   811  		// so we need to amend them
   812  		if strings.Contains(entity.OpenApiEndpoint, types.OpenApiEndpointRdeInterfaces) {
   813  			err = amendRdeApiError(&vcd.client.Client, err)
   814  		}
   815  		// UI Plugin has a bug in VCD 10.4.x that causes "not found" errors to return a NullPointerException,
   816  		// so we need to amend them
   817  		if strings.Contains(entity.OpenApiEndpoint, types.OpenApiEndpointExtensionsUi) {
   818  			err = amendUIPluginGetByIdError(entity.Name, err)
   819  		}
   820  
   821  		if ContainsNotFound(err) {
   822  			vcd.infoCleanup(notFoundMsg, entity.EntityType, entity.Name)
   823  			return
   824  		}
   825  
   826  		if err != nil {
   827  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
   828  			return
   829  		}
   830  
   831  		if err != nil {
   832  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
   833  			return
   834  		}
   835  
   836  		// Attempt to use supplied path in entity.Parent for element deletion
   837  		err = vcd.client.Client.OpenApiDeleteItem(apiVersion, urlRef, nil, nil)
   838  		if err != nil {
   839  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
   840  			return
   841  		}
   842  
   843  		vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
   844  	// 	OpenApiEntityFirewall has different API structure therefore generic `OpenApiEntity` case does not fit cleanup
   845  	case "OpenApiEntityFirewall":
   846  		apiVersion, err := vcd.client.Client.checkOpenApiEndpointCompatibility(types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointNsxtFirewallRules)
   847  		if err != nil {
   848  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
   849  			return
   850  		}
   851  
   852  		urlRef, err := vcd.client.Client.OpenApiBuildEndpoint(entity.Name)
   853  		if err != nil {
   854  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
   855  			return
   856  		}
   857  
   858  		// Attempt to use supplied path in entity.Parent for element deletion
   859  		err = vcd.client.Client.OpenApiDeleteItem(apiVersion, urlRef, nil, nil)
   860  		if err != nil {
   861  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
   862  			return
   863  		}
   864  
   865  		vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
   866  	case "OpenApiEntityGlobalDefaultSegmentProfileTemplate":
   867  		// Check if any default settings are applied
   868  		gdSpt, err := vcd.client.GetGlobalDefaultSegmentProfileTemplates()
   869  		if err != nil {
   870  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
   871  			return
   872  		}
   873  
   874  		if gdSpt.VappNetworkSegmentProfileTemplateRef == nil && gdSpt.VdcNetworkSegmentProfileTemplateRef == nil {
   875  			vcd.infoCleanup(notFoundMsg, entity.EntityType, entity.Name)
   876  			return
   877  		}
   878  
   879  		_, err = vcd.client.UpdateGlobalDefaultSegmentProfileTemplates(&types.NsxtGlobalDefaultSegmentProfileTemplate{})
   880  		if err != nil {
   881  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
   882  			return
   883  		}
   884  
   885  		vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
   886  	// 	OpenApiEntityAlbSettingsDisable has different API structure therefore generic `OpenApiEntity` case does not fit cleanup
   887  	case "OpenApiEntityAlbSettingsDisable":
   888  		edge, err := vcd.nsxtVdc.GetNsxtEdgeGatewayByName(entity.Parent)
   889  		if err != nil {
   890  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
   891  			return
   892  		}
   893  
   894  		edgeAlbSettingsConfig, err := edge.GetAlbSettings()
   895  		if err != nil {
   896  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
   897  			return
   898  		}
   899  		if edgeAlbSettingsConfig.Enabled == false {
   900  			vcd.infoCleanup(notFoundMsg, entity.EntityType, entity.Name)
   901  			return
   902  		}
   903  
   904  		err = edge.DisableAlb()
   905  		if err != nil {
   906  			vcd.infoCleanup(notFoundMsg, entity.EntityType, entity.Name)
   907  			return
   908  		}
   909  
   910  		vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
   911  	case "vapp":
   912  		vdc := vcd.vdc
   913  		var err error
   914  
   915  		// Check if parent VDC was specified. If not - use the default NSX-V VDC
   916  		if entity.Parent != "" {
   917  			vdc, err = vcd.org.GetVDCByName(entity.Parent, true)
   918  			if err != nil {
   919  				vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
   920  				return
   921  			}
   922  		}
   923  
   924  		vapp, err := vdc.GetVAppByName(entity.Name, true)
   925  		if err != nil {
   926  			vcd.infoCleanup(notFoundMsg, entity.EntityType, entity.Name)
   927  			return
   928  		}
   929  
   930  		task, _ := vapp.Undeploy()
   931  		_ = task.WaitTaskCompletion()
   932  		// Detach all Org networks during vApp removal because network removal errors if it happens
   933  		// very quickly (as the next task) after vApp removal
   934  		task, _ = vapp.RemoveAllNetworks()
   935  		_ = task.WaitTaskCompletion()
   936  		task, err = vapp.Delete()
   937  		_ = task.WaitTaskCompletion()
   938  
   939  		if err != nil {
   940  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
   941  			return
   942  		}
   943  		vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
   944  		return
   945  
   946  	case "catalog":
   947  		if entity.Parent == "" {
   948  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] No Org provided for catalog '%s'\n", entity.Name)
   949  			return
   950  		}
   951  		org, err := vcd.client.GetAdminOrgByName(entity.Parent)
   952  		if err != nil {
   953  			vcd.infoCleanup("removeLeftoverEntries: [INFO] organization '%s' not found\n", entity.Parent)
   954  			return
   955  		}
   956  		catalog, err := org.GetAdminCatalogByName(entity.Name, false)
   957  		if catalog == nil || err != nil {
   958  			vcd.infoCleanup(notFoundMsg, entity.EntityType, entity.Name)
   959  			return
   960  		}
   961  		err = catalog.Delete(true, true)
   962  		if err != nil {
   963  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
   964  			return
   965  		}
   966  		vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
   967  		return
   968  
   969  	case "org":
   970  		org, err := vcd.client.GetAdminOrgByName(entity.Name)
   971  		if err != nil {
   972  			vcd.infoCleanup(notFoundMsg, entity.EntityType, entity.Name)
   973  			return
   974  		}
   975  		err = org.Delete(true, true)
   976  		if err != nil {
   977  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
   978  			return
   979  		}
   980  		vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
   981  		return
   982  	case "provider_vdc":
   983  		pvdc, err := vcd.client.GetProviderVdcExtendedByName(entity.Name)
   984  		if err != nil {
   985  			vcd.infoCleanup(notFoundMsg, entity.EntityType, entity.Name)
   986  			return
   987  		}
   988  		err = pvdc.Disable()
   989  		if err != nil {
   990  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
   991  			return
   992  		}
   993  		task, err := pvdc.Delete()
   994  		if err != nil {
   995  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
   996  			return
   997  		}
   998  		err = task.WaitTaskCompletion()
   999  		if err != nil {
  1000  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
  1001  			return
  1002  		}
  1003  		vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
  1004  		return
  1005  	case "catalogItem":
  1006  		if entity.Parent == "" {
  1007  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] No Org provided for catalogItem '%s'\n", strings.Split(entity.Parent, "|")[0])
  1008  			return
  1009  		}
  1010  		org, err := vcd.client.GetAdminOrgByName(strings.Split(entity.Parent, "|")[0])
  1011  		if err != nil {
  1012  			vcd.infoCleanup("removeLeftoverEntries: [INFO] organization '%s' not found\n", entity.Parent)
  1013  			return
  1014  		}
  1015  		catalog, err := org.GetCatalogByName(strings.Split(entity.Parent, "|")[1], false)
  1016  		if catalog == nil || err != nil {
  1017  			vcd.infoCleanup(notFoundMsg, entity.EntityType, entity.Name)
  1018  			return
  1019  		}
  1020  		catalogItem, err := catalog.GetCatalogItemByName(entity.Name, false)
  1021  		if err != nil {
  1022  			vcd.infoCleanup(notFoundMsg, entity.EntityType, entity.Name)
  1023  			return
  1024  		}
  1025  		err = catalogItem.Delete()
  1026  		if err != nil {
  1027  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
  1028  		}
  1029  		vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
  1030  		return
  1031  	case "edgegateway":
  1032  		_, vdc, err := vcd.getAdminOrgAndVdcFromCleanupEntity(entity)
  1033  		if err != nil {
  1034  			return
  1035  		}
  1036  		edge, err := vdc.GetEdgeGatewayByName(entity.Name, false)
  1037  		if err != nil {
  1038  			vcd.infoCleanup("removeLeftoverEntries: [INFO] edge gateway '%s' not found\n", entity.Name)
  1039  			return
  1040  		}
  1041  		err = edge.Delete(true, true)
  1042  		if err != nil {
  1043  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
  1044  			return
  1045  		}
  1046  		vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
  1047  		return
  1048  	case "network":
  1049  		_, vdc, err := vcd.getAdminOrgAndVdcFromCleanupEntity(entity)
  1050  		if err != nil {
  1051  			vcd.infoCleanup("%s", err)
  1052  		}
  1053  		_, errExists := vdc.GetOrgVdcNetworkByName(entity.Name, false)
  1054  		networkExists := errExists == nil
  1055  
  1056  		err = RemoveOrgVdcNetworkIfExists(*vdc, entity.Name)
  1057  		if err == nil {
  1058  			if networkExists {
  1059  				vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
  1060  			} else {
  1061  				vcd.infoCleanup(notFoundMsg, entity.EntityType, entity.Name)
  1062  			}
  1063  		} else {
  1064  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
  1065  		}
  1066  		return
  1067  	case "affinity_rule":
  1068  		_, vdc, err := vcd.getAdminOrgAndVdcFromCleanupEntity(entity)
  1069  		if err != nil {
  1070  			vcd.infoCleanup("adminOrg + VDC: %s", err)
  1071  			return
  1072  		}
  1073  		affinityRule, err := vdc.GetVmAffinityRuleById(entity.Name)
  1074  		if err != nil {
  1075  			if ContainsNotFound(err) {
  1076  				vcd.infoCleanup(notFoundMsg, entity.EntityType, entity.Name)
  1077  				return
  1078  			} else {
  1079  				vcd.infoCleanup("retrieving affinity rule %s", err)
  1080  				return
  1081  			}
  1082  		}
  1083  		err = affinityRule.Delete()
  1084  		if err != nil {
  1085  			vcd.infoCleanup("affinity rule deletion: %s", err)
  1086  		}
  1087  		return
  1088  
  1089  	case "externalNetwork":
  1090  		externalNetwork, err := vcd.client.GetExternalNetworkByName(entity.Name)
  1091  		if err != nil {
  1092  			vcd.infoCleanup(notFoundMsg, "externalNetwork", entity.Name)
  1093  			return
  1094  		}
  1095  		err = externalNetwork.DeleteWait()
  1096  		if err == nil {
  1097  			vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
  1098  		} else {
  1099  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
  1100  		}
  1101  		return
  1102  	case "mediaImage":
  1103  		if entity.Parent == "" {
  1104  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] No VDC and ORG provided for media '%s'\n", entity.Name)
  1105  			return
  1106  		}
  1107  		_, vdc, err := vcd.getAdminOrgAndVdcFromCleanupEntity(entity)
  1108  		if err != nil {
  1109  			vcd.infoCleanup("%s", err)
  1110  		}
  1111  		err = RemoveMediaImageIfExists(*vdc, entity.Name)
  1112  		if err == nil {
  1113  			vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
  1114  		} else {
  1115  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
  1116  		}
  1117  		return
  1118  	case "mediaCatalogImage":
  1119  		if entity.Parent == "" {
  1120  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] No Catalog provided for media '%s'\n", entity.Name)
  1121  			return
  1122  		}
  1123  		orgName, catalogName, _ := splitParent(entity.Parent, "|")
  1124  		if orgName == "" || catalogName == "" {
  1125  			vcd.infoCleanup(splitParentNotFound, entity.Parent)
  1126  		}
  1127  
  1128  		org, err := vcd.client.GetAdminOrgByName(orgName)
  1129  		if err != nil {
  1130  			vcd.infoCleanup("removeLeftoverEntries: [INFO] organization '%s' not found\n", entity.Parent)
  1131  			return
  1132  		}
  1133  		adminCatalog, err := org.GetAdminCatalogByName(catalogName, false)
  1134  		if err != nil {
  1135  			vcd.infoCleanup(notFoundMsg, entity.EntityType, entity.Name)
  1136  			return
  1137  		}
  1138  
  1139  		_, err = adminCatalog.GetMediaByName(entity.Name, true)
  1140  		if ContainsNotFound(err) {
  1141  			vcd.infoCleanup(notFoundMsg, entity.EntityType, entity.Name)
  1142  			return
  1143  		}
  1144  		err = adminCatalog.RemoveMediaIfExists(entity.Name)
  1145  		if err == nil {
  1146  			vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
  1147  		} else {
  1148  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
  1149  		}
  1150  		return
  1151  	case "user":
  1152  		if entity.Parent == "" {
  1153  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] No ORG provided for user '%s'\n", entity.Name)
  1154  			return
  1155  		}
  1156  		org, err := vcd.client.GetAdminOrgByName(entity.Parent)
  1157  		if err != nil {
  1158  			vcd.infoCleanup(notFoundMsg, "org", entity.Parent)
  1159  			return
  1160  		}
  1161  		user, err := org.GetUserByName(entity.Name, true)
  1162  		if err != nil {
  1163  			vcd.infoCleanup(notFoundMsg, "user", entity.Name)
  1164  			return
  1165  		}
  1166  		err = user.Delete(true)
  1167  		if err == nil {
  1168  			vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
  1169  		} else {
  1170  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
  1171  		}
  1172  		return
  1173  	case "group":
  1174  		if entity.Parent == "" {
  1175  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] No ORG provided for group '%s'\n", entity.Name)
  1176  			return
  1177  		}
  1178  		org, err := vcd.client.GetAdminOrgByName(entity.Parent)
  1179  		if err != nil {
  1180  			vcd.infoCleanup(notFoundMsg, "org", entity.Parent)
  1181  			return
  1182  		}
  1183  		group, err := org.GetGroupByName(entity.Name, true)
  1184  		if err != nil {
  1185  			vcd.infoCleanup(notFoundMsg, "group", entity.Name)
  1186  			return
  1187  		}
  1188  		err = group.Delete()
  1189  		if err == nil {
  1190  			vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
  1191  		} else {
  1192  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
  1193  		}
  1194  		return
  1195  	case "vdc":
  1196  		if entity.Parent == "" {
  1197  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] No ORG provided for VDC '%s'\n", entity.Name)
  1198  			return
  1199  		}
  1200  		org, err := vcd.client.GetAdminOrgByName(entity.Parent)
  1201  		if err != nil {
  1202  			vcd.infoCleanup(notFoundMsg, "org", entity.Parent)
  1203  			return
  1204  		}
  1205  		vdc, err := org.GetVDCByName(entity.Name, false)
  1206  		if vdc == nil || err != nil {
  1207  			vcd.infoCleanup(notFoundMsg, "vdc", entity.Name)
  1208  			return
  1209  		}
  1210  		err = vdc.DeleteWait(true, true)
  1211  		if err == nil {
  1212  			vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
  1213  		} else {
  1214  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
  1215  		}
  1216  		return
  1217  	case "nsxv_dfw":
  1218  		if entity.Parent == "" {
  1219  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] No ORG provided for VDC '%s'\n", entity.Name)
  1220  			return
  1221  		}
  1222  		org, err := vcd.client.GetAdminOrgByName(entity.Parent)
  1223  		if err != nil {
  1224  			vcd.infoCleanup(notFoundMsg, "org", entity.Parent)
  1225  			return
  1226  		}
  1227  		vdc, err := org.GetVDCByName(entity.Name, false)
  1228  		if vdc == nil || err != nil {
  1229  			vcd.infoCleanup(notFoundMsg, "vdc", entity.Name)
  1230  			return
  1231  		}
  1232  		dfw := NewNsxvDistributedFirewall(vdc.client, vdc.Vdc.ID)
  1233  		enabled, err := dfw.IsEnabled()
  1234  		if err != nil {
  1235  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] checking distributed firewall from VCD '%s': %s", entity.Name, err)
  1236  			return
  1237  		}
  1238  		if !enabled {
  1239  			vcd.infoCleanup(notFoundMsg, entity.EntityType, entity.Name)
  1240  			return
  1241  		}
  1242  		err = dfw.Disable()
  1243  		if err == nil {
  1244  			vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
  1245  		} else {
  1246  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] removing distributed firewall from VCD '%s': %s", entity.Name, err)
  1247  			return
  1248  		}
  1249  	case "standaloneVm":
  1250  		vm, err := vcd.org.QueryVmById(entity.Name) // The VM ID must be passed as Name
  1251  		if IsNotFound(err) {
  1252  			vcd.infoCleanup(notFoundMsg, entity.EntityType, entity.Name)
  1253  			return
  1254  		}
  1255  		if err != nil {
  1256  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] retrieving standalone VM '%s'. %s\n",
  1257  				entity.Name, err)
  1258  			return
  1259  		}
  1260  		err = vm.Delete()
  1261  		if err != nil {
  1262  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] deleting VM '%s' : %s\n",
  1263  				entity.Name, err)
  1264  			return
  1265  		}
  1266  		vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
  1267  	case "vm":
  1268  		vapp, err := vcd.vdc.GetVAppByName(entity.Parent, true)
  1269  		if err != nil {
  1270  			vcd.infoCleanup(notFoundMsg, entity.EntityType, entity.Name)
  1271  			return
  1272  		}
  1273  
  1274  		vm, err := vapp.GetVMByName(entity.Name, false)
  1275  
  1276  		if err != nil {
  1277  			vcd.infoCleanup(notFoundMsg, entity.EntityType, entity.Name)
  1278  			return
  1279  		}
  1280  
  1281  		// Try to undeploy and ignore errors if it doesn't work (VM may already be powered off)
  1282  		task, _ := vm.Undeploy()
  1283  		_ = task.WaitTaskCompletion()
  1284  
  1285  		err = vapp.RemoveVM(*vm)
  1286  		if err != nil {
  1287  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] Deleting VM '%s' in vApp '%s': %s\n",
  1288  				entity.Name, vapp.VApp.Name, err)
  1289  			return
  1290  		}
  1291  
  1292  		vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
  1293  		return
  1294  
  1295  	case "disk":
  1296  		// Find disk by href rather than find disk by name, because disk name can be duplicated in VDC,
  1297  		// so the unique href is required for finding the disk.
  1298  		disk, err := vcd.vdc.GetDiskByHref(entity.Name)
  1299  		if err != nil {
  1300  			// If the disk is not found, we just need to show that it was not found, as
  1301  			// it was likely deleted during the regular tests
  1302  			vcd.infoCleanup(notFoundMsg, entity.Name, err)
  1303  			return
  1304  		}
  1305  
  1306  		// See if the disk is attached to the VM
  1307  		vmRef, err := disk.AttachedVM()
  1308  		if err != nil {
  1309  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] Deleting %s '%s', cannot find attached VM: %s\n",
  1310  				entity.EntityType, entity.Name, err)
  1311  			return
  1312  		}
  1313  		// If the disk is attached to the VM, detach disk from the VM
  1314  		if vmRef != nil {
  1315  			vcd.infoCleanup("removeLeftoverEntries: [INFO] Deleting %s '%s', VM: '%s|%s', disk is attached, detaching disk\n",
  1316  				entity.EntityType, entity.Name, vmRef.Name, vmRef.HREF)
  1317  
  1318  			vm, err := vcd.client.Client.GetVMByHref(vmRef.HREF)
  1319  			if err != nil {
  1320  				vcd.infoCleanup(
  1321  					"removeLeftoverEntries: [ERROR] Deleting %s '%s', VM: '%s|%s', cannot find the VM details: %s\n",
  1322  					entity.EntityType, entity.Name, vmRef.Name, vmRef.HREF, err)
  1323  				return
  1324  			}
  1325  
  1326  			// Detach the disk from VM
  1327  			task, err := vm.DetachDisk(&types.DiskAttachOrDetachParams{
  1328  				Disk: &types.Reference{
  1329  					HREF: disk.Disk.HREF,
  1330  				},
  1331  			})
  1332  			if err != nil {
  1333  				vcd.infoCleanup(
  1334  					"removeLeftoverEntries: [ERROR] Detaching %s '%s', VM: '%s|%s': %s\n",
  1335  					entity.EntityType, entity.Name, vmRef.Name, vmRef.HREF, err)
  1336  				return
  1337  			}
  1338  			err = task.WaitTaskCompletion()
  1339  			if err != nil {
  1340  				vcd.infoCleanup(
  1341  					"removeLeftoverEntries: [ERROR] Deleting %s '%s', VM: '%s|%s', waitTaskCompletion of detach disk is failed: %s\n",
  1342  					entity.EntityType, entity.Name, vmRef.Name, vmRef.HREF, err)
  1343  				return
  1344  			}
  1345  
  1346  			// We need to refresh the disk info to obtain remove href for delete disk
  1347  			// because attached disk is not showing remove disk href in disk.Disk.Link
  1348  			err = disk.Refresh()
  1349  			if err != nil {
  1350  				vcd.infoCleanup(
  1351  					"removeLeftoverEntries: [ERROR] Deleting %s '%s', cannot refresh disk: %s\n",
  1352  					entity.EntityType, entity.Name, err)
  1353  				return
  1354  			}
  1355  		}
  1356  
  1357  		// Delete disk
  1358  		deleteDiskTask, err := disk.Delete()
  1359  		if err != nil {
  1360  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] Deleting %s '%s', cannot delete disk: %s\n",
  1361  				entity.EntityType, entity.Name, err)
  1362  			return
  1363  		}
  1364  		err = deleteDiskTask.WaitTaskCompletion()
  1365  		if err != nil {
  1366  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] Deleting %s '%s', waitTaskCompletion of delete disk is failed: %s\n",
  1367  				entity.EntityType, entity.Name, err)
  1368  			return
  1369  		}
  1370  
  1371  		vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
  1372  		return
  1373  	case "lbServiceMonitor":
  1374  		if entity.Parent == "" {
  1375  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] No parent specified '%s'\n", entity.Name)
  1376  			return
  1377  		}
  1378  
  1379  		orgName, vdcName, edgeName := splitParent(entity.Parent, "|")
  1380  
  1381  		_, _, edge, err := getOrgVdcEdgeByNames(vcd, orgName, vdcName, edgeName)
  1382  		if err != nil {
  1383  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] %s \n", err)
  1384  		}
  1385  
  1386  		err = edge.DeleteLbServiceMonitorByName(entity.Name)
  1387  		if err != nil && strings.Contains(err.Error(), ErrorEntityNotFound.Error()) {
  1388  			vcd.infoCleanup(notFoundMsg, entity.EntityType, entity.Name)
  1389  			return
  1390  		}
  1391  		if err != nil {
  1392  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
  1393  		}
  1394  
  1395  		vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
  1396  		return
  1397  
  1398  	case "lbServerPool":
  1399  		if entity.Parent == "" {
  1400  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] No parent specified '%s'\n", entity.Name)
  1401  			return
  1402  		}
  1403  
  1404  		orgName, vdcName, edgeName := splitParent(entity.Parent, "|")
  1405  
  1406  		_, _, edge, err := getOrgVdcEdgeByNames(vcd, orgName, vdcName, edgeName)
  1407  		if err != nil {
  1408  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] %s \n", err)
  1409  		}
  1410  
  1411  		err = edge.DeleteLbServerPoolByName(entity.Name)
  1412  		if err != nil && strings.Contains(err.Error(), ErrorEntityNotFound.Error()) {
  1413  			vcd.infoCleanup(notFoundMsg, entity.EntityType, entity.Name)
  1414  			return
  1415  		}
  1416  		if err != nil {
  1417  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
  1418  		}
  1419  
  1420  		vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
  1421  		return
  1422  	case "lbAppProfile":
  1423  		if entity.Parent == "" {
  1424  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] No parent specified '%s'\n", entity.Name)
  1425  			return
  1426  		}
  1427  
  1428  		orgName, vdcName, edgeName := splitParent(entity.Parent, "|")
  1429  
  1430  		_, _, edge, err := getOrgVdcEdgeByNames(vcd, orgName, vdcName, edgeName)
  1431  		if err != nil {
  1432  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] %s \n", err)
  1433  		}
  1434  
  1435  		err = edge.DeleteLbAppProfileByName(entity.Name)
  1436  		if err != nil && strings.Contains(err.Error(), ErrorEntityNotFound.Error()) {
  1437  			vcd.infoCleanup(notFoundMsg, entity.EntityType, entity.Name)
  1438  			return
  1439  		}
  1440  		if err != nil {
  1441  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
  1442  		}
  1443  
  1444  		vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
  1445  		return
  1446  
  1447  	case "lbVirtualServer":
  1448  		if entity.Parent == "" {
  1449  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] No parent specified '%s'\n", entity.Name)
  1450  			return
  1451  		}
  1452  
  1453  		orgName, vdcName, edgeName := splitParent(entity.Parent, "|")
  1454  
  1455  		_, _, edge, err := getOrgVdcEdgeByNames(vcd, orgName, vdcName, edgeName)
  1456  		if err != nil {
  1457  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] %s \n", err)
  1458  		}
  1459  
  1460  		err = edge.DeleteLbVirtualServerByName(entity.Name)
  1461  		if err != nil && strings.Contains(err.Error(), ErrorEntityNotFound.Error()) {
  1462  			vcd.infoCleanup(notFoundMsg, entity.EntityType, entity.Name)
  1463  			return
  1464  		}
  1465  		if err != nil {
  1466  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
  1467  		}
  1468  
  1469  		vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
  1470  		return
  1471  	case "lbAppRule":
  1472  		if entity.Parent == "" {
  1473  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] No parent specified '%s'\n", entity.Name)
  1474  			return
  1475  		}
  1476  
  1477  		orgName, vdcName, edgeName := splitParent(entity.Parent, "|")
  1478  
  1479  		_, _, edge, err := getOrgVdcEdgeByNames(vcd, orgName, vdcName, edgeName)
  1480  		if err != nil {
  1481  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] %s \n", err)
  1482  		}
  1483  
  1484  		err = edge.DeleteLbAppRuleByName(entity.Name)
  1485  		if err != nil && strings.Contains(err.Error(), ErrorEntityNotFound.Error()) {
  1486  			vcd.infoCleanup(notFoundMsg, entity.EntityType, entity.Name)
  1487  			return
  1488  		}
  1489  		if err != nil {
  1490  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
  1491  		}
  1492  
  1493  		vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
  1494  		return
  1495  	// Edge gateway DHCP relay settings cannot actually be "deleted". They can only be unset and this is
  1496  	// what this cleanup case does.
  1497  	case "dhcpRelayConfig":
  1498  		if entity.Parent == "" {
  1499  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] No parent specified '%s'\n", entity.Name)
  1500  			return
  1501  		}
  1502  
  1503  		orgName, vdcName, edgeName := splitParent(entity.Parent, "|")
  1504  
  1505  		_, _, edge, err := getOrgVdcEdgeByNames(vcd, orgName, vdcName, edgeName)
  1506  		if err != nil {
  1507  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] %s \n", err)
  1508  		}
  1509  
  1510  		err = edge.ResetDhcpRelay()
  1511  		if err != nil {
  1512  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
  1513  		}
  1514  
  1515  		vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
  1516  		return
  1517  
  1518  	case "vdcMetaData":
  1519  		if entity.Parent == "" {
  1520  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] No VDC and ORG provided for vDC meta data '%s'\n", entity.Name)
  1521  			return
  1522  		}
  1523  		_, vdc, err := vcd.getAdminOrgAndVdcFromCleanupEntity(entity)
  1524  		if err != nil {
  1525  			vcd.infoCleanup("%s", err)
  1526  		}
  1527  		_, err = vdc.DeleteMetadata(entity.Name)
  1528  		if err == nil {
  1529  			vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
  1530  		} else {
  1531  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
  1532  		}
  1533  		return
  1534  
  1535  	case "nsxvNatRule":
  1536  		if entity.Parent == "" {
  1537  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] No parent specified '%s'\n", entity.Name)
  1538  			return
  1539  		}
  1540  
  1541  		orgName, vdcName, edgeName := splitParent(entity.Parent, "|")
  1542  
  1543  		_, _, edge, err := getOrgVdcEdgeByNames(vcd, orgName, vdcName, edgeName)
  1544  		if err != nil {
  1545  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] %s \n", err)
  1546  		}
  1547  
  1548  		err = edge.DeleteNsxvNatRuleById(entity.Name)
  1549  		if IsNotFound(err) {
  1550  			vcd.infoCleanup(notFoundMsg, entity.EntityType, entity.Name)
  1551  			return
  1552  		}
  1553  		if err != nil {
  1554  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
  1555  		}
  1556  
  1557  		vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
  1558  		return
  1559  
  1560  	case "nsxvFirewallRule":
  1561  		if entity.Parent == "" {
  1562  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] No parent specified '%s'\n", entity.Name)
  1563  			return
  1564  		}
  1565  
  1566  		orgName, vdcName, edgeName := splitParent(entity.Parent, "|")
  1567  
  1568  		_, _, edge, err := getOrgVdcEdgeByNames(vcd, orgName, vdcName, edgeName)
  1569  		if err != nil {
  1570  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] %s \n", err)
  1571  		}
  1572  
  1573  		err = edge.DeleteNsxvFirewallRuleById(entity.Name)
  1574  		if IsNotFound(err) {
  1575  			vcd.infoCleanup(notFoundMsg, entity.EntityType, entity.Name)
  1576  			return
  1577  		}
  1578  		if err != nil {
  1579  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
  1580  		}
  1581  
  1582  		vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
  1583  		return
  1584  	case "ipSet":
  1585  		if entity.Parent == "" {
  1586  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] No parent specified '%s'\n", entity.Name)
  1587  			return
  1588  		}
  1589  
  1590  		orgName, vdcName, _ := splitParent(entity.Parent, "|")
  1591  
  1592  		_, vdc, err := getOrgVdcByNames(vcd, orgName, vdcName)
  1593  		if err != nil {
  1594  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] %s \n", err)
  1595  		}
  1596  
  1597  		err = vdc.DeleteNsxvIpSetByName(entity.Name)
  1598  		if IsNotFound(err) {
  1599  			vcd.infoCleanup(notFoundMsg, entity.EntityType, entity.Name)
  1600  			return
  1601  		}
  1602  		if err != nil {
  1603  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
  1604  		}
  1605  	case "fastProvisioning":
  1606  		orgName, vdcName, _ := splitParent(entity.Parent, "|")
  1607  		if orgName == "" || vdcName == "" {
  1608  			vcd.infoCleanup(splitParentNotFound, entity.Parent)
  1609  		}
  1610  		org, err := vcd.client.GetAdminOrgByName(orgName)
  1611  		if err != nil {
  1612  			vcd.infoCleanup(notFoundMsg, "org", orgName)
  1613  		}
  1614  		adminVdc, err := org.GetAdminVDCByName(vdcName, false)
  1615  		if adminVdc == nil || err != nil {
  1616  			vcd.infoCleanup(notFoundMsg, "vdc", vdcName)
  1617  		}
  1618  		fastProvisioningValue := false
  1619  		if entity.Name == "enable" {
  1620  			fastProvisioningValue = true
  1621  		}
  1622  
  1623  		if adminVdc != nil && *adminVdc.AdminVdc.UsesFastProvisioning != fastProvisioningValue {
  1624  			adminVdc.AdminVdc.UsesFastProvisioning = &fastProvisioningValue
  1625  			_, err = adminVdc.Update()
  1626  			if err != nil {
  1627  				vcd.infoCleanup("updateLeftoverEntries: [INFO] revert back VDC fast provisioning value % s failed\n", entity.Name)
  1628  				return
  1629  			}
  1630  			vcd.infoCleanup("updateLeftoverEntries: [INFO] reverted back VDC fast provisioning value %s \n", entity.Name)
  1631  		} else {
  1632  			vcd.infoCleanup("updateLeftoverEntries: [INFO] VDC fast provisioning left as it is %s \n", entity.Name)
  1633  		}
  1634  		return
  1635  	case "orgLdapSettings":
  1636  		org, err := vcd.client.GetAdminOrgByName(entity.Parent)
  1637  		if err != nil {
  1638  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] Clearing LDAP settings for Org '%s': %s",
  1639  				entity.Parent, err)
  1640  			return
  1641  		}
  1642  
  1643  		ldapConfig, err := org.GetLdapConfiguration()
  1644  		if err != nil {
  1645  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] Couldn't get LDAP settings for Org '%s': %s",
  1646  				entity.Parent, err)
  1647  			return
  1648  		}
  1649  
  1650  		// This is done to avoid calling LdapDisable() if it has been unconfigured, due to bug with Org catalog publish settings
  1651  		if ldapConfig.OrgLdapMode != types.LdapModeNone {
  1652  			err = org.LdapDisable()
  1653  			if err != nil {
  1654  				vcd.infoCleanup("removeLeftoverEntries: [ERROR] Could not clear LDAP settings for Org '%s': %s",
  1655  					entity.Parent, err)
  1656  				vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
  1657  				return
  1658  			}
  1659  		}
  1660  
  1661  		vcd.infoCleanup(notFoundMsg, entity.EntityType, entity.Name)
  1662  		return
  1663  
  1664  	case "vdcComputePolicy":
  1665  		policy, err := vcd.client.GetVdcComputePolicyV2ById(entity.Name)
  1666  		if policy == nil || err != nil {
  1667  			vcd.infoCleanup(notFoundMsg, "vdcComputePolicy", entity.Name)
  1668  			return
  1669  		}
  1670  		err = policy.Delete()
  1671  		if err == nil {
  1672  			vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
  1673  		} else {
  1674  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
  1675  		}
  1676  		return
  1677  
  1678  	case "logicalVmGroup":
  1679  		logicalVmGroup, err := vcd.client.GetLogicalVmGroupById(entity.Name)
  1680  		if logicalVmGroup == nil || err != nil {
  1681  			vcd.infoCleanup(notFoundMsg, "logicalVmGroup", entity.Name)
  1682  			return
  1683  		}
  1684  		err = logicalVmGroup.Delete()
  1685  		if err == nil {
  1686  			vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
  1687  		} else {
  1688  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
  1689  		}
  1690  		return
  1691  
  1692  	case "nsxtDhcpForwarder":
  1693  		edge, err := vcd.nsxtVdc.GetNsxtEdgeGatewayByName(entity.Name)
  1694  		if err != nil {
  1695  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] %s \n", err)
  1696  		}
  1697  
  1698  		dhcpForwarder, err := edge.GetDhcpForwarder()
  1699  		if err != nil {
  1700  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] %s \n", err)
  1701  		}
  1702  
  1703  		if dhcpForwarder.Enabled == false && len(dhcpForwarder.DhcpServers) == 0 {
  1704  			vcd.infoCleanup(notFoundMsg, "dhcpForwarder", entity.Name)
  1705  			return
  1706  		}
  1707  
  1708  		_, err = edge.UpdateDhcpForwarder(&types.NsxtEdgeGatewayDhcpForwarder{})
  1709  		if err != nil {
  1710  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
  1711  		}
  1712  
  1713  		vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
  1714  		return
  1715  	case "nsxtEdgeGatewayDns":
  1716  		edge, err := vcd.nsxtVdc.GetNsxtEdgeGatewayByName(entity.Name)
  1717  		if err != nil {
  1718  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] %s \n", err)
  1719  		}
  1720  
  1721  		dns, err := edge.GetDnsConfig()
  1722  		if err != nil {
  1723  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] %s \n", err)
  1724  		}
  1725  
  1726  		if dns.NsxtEdgeGatewayDns.Enabled == false && dns.NsxtEdgeGatewayDns.DefaultForwarderZone == nil {
  1727  			vcd.infoCleanup(notFoundMsg, entity.EntityType, entity.Name)
  1728  			return
  1729  		}
  1730  
  1731  		err = dns.Delete()
  1732  		if err != nil {
  1733  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
  1734  		}
  1735  
  1736  		vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
  1737  		return
  1738  	case "slaacProfile":
  1739  		edge, err := vcd.nsxtVdc.GetNsxtEdgeGatewayByName(entity.Name)
  1740  		if err != nil {
  1741  			vcd.infoCleanup("removeLeftoverEntries: [ERROR] %s \n", err)
  1742  		}
  1743  
  1744  		_, err = edge.UpdateSlaacProfile(&types.NsxtEdgeGatewaySlaacProfile{Enabled: false, Mode: "SLAAC"})
  1745  		if err != nil {
  1746  			vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
  1747  		}
  1748  
  1749  		vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
  1750  		return
  1751  
  1752  	default:
  1753  		// If we reach this point, we are trying to clean up an entity that
  1754  		// we aren't prepared for yet.
  1755  		fmt.Printf("removeLeftoverEntries: [ERROR] Unrecognized type %s for entity '%s'\n",
  1756  			entity.EntityType, entity.Name)
  1757  	}
  1758  }
  1759  
  1760  func (vcd *TestVCD) TearDownSuite(check *C) {
  1761  	// We will try to remove every entity that has been registered into
  1762  	// CleanupEntityList. Entities that have already been cleaned up by their
  1763  	// functions will be ignored.
  1764  	for i, cleanupEntity := range cleanupEntityList {
  1765  		fmt.Printf("# %d of %d - ", i+1, len(cleanupEntityList))
  1766  		vcd.removeLeftoverEntities(cleanupEntity)
  1767  		removePersistentCleanupList()
  1768  	}
  1769  }
  1770  
  1771  // Tests getloginurl with the endpoint given
  1772  // in the config file.
  1773  func (vcd *TestVCD) TestClient_getloginurl(check *C) {
  1774  	if os.Getenv("GOVCD_API_VERSION") != "" {
  1775  		check.Skip("custom API version is being used")
  1776  	}
  1777  	config, err := GetConfigStruct()
  1778  	if err != nil {
  1779  		check.Fatalf("err: %s", err)
  1780  	}
  1781  	client, err := GetTestVCDFromYaml(config)
  1782  	if err != nil {
  1783  		check.Fatalf("err: %s", err)
  1784  	}
  1785  
  1786  	err = client.vcdloginurl()
  1787  	if err != nil {
  1788  		check.Fatalf("err: %s", err)
  1789  	}
  1790  
  1791  	if client.sessionHREF.Path != "/cloudapi/1.0.0/sessions" {
  1792  		check.Fatalf("Getting LoginUrl failed, url: %s", client.sessionHREF.Path)
  1793  	}
  1794  }
  1795  
  1796  // Tests Authenticate with the vcd credentials (or token) given in the config file
  1797  func (vcd *TestVCD) TestVCDClient_Authenticate(check *C) {
  1798  	config, err := GetConfigStruct()
  1799  	if err != nil {
  1800  		check.Fatalf("err: %s", err)
  1801  	}
  1802  	client, err := GetTestVCDFromYaml(config)
  1803  	if err != nil {
  1804  		check.Fatalf("err: %s", err)
  1805  	}
  1806  	apiToken := os.Getenv("VCD_API_TOKEN")
  1807  	if apiToken == "" {
  1808  		apiToken = config.Provider.ApiToken
  1809  	}
  1810  	if apiToken != "" {
  1811  		err = client.SetToken(config.Provider.SysOrg, ApiTokenHeader, apiToken)
  1812  	} else {
  1813  		token := os.Getenv("VCD_TOKEN")
  1814  		if token == "" {
  1815  			token = config.Provider.Token
  1816  		}
  1817  		if token != "" {
  1818  			err = client.SetToken(config.Provider.SysOrg, AuthorizationHeader, token)
  1819  		} else {
  1820  			err = client.Authenticate(config.Provider.User, config.Provider.Password, config.Provider.SysOrg)
  1821  		}
  1822  	}
  1823  
  1824  	if err != nil {
  1825  		check.Fatalf("Error authenticating: %s", err)
  1826  	}
  1827  }
  1828  
  1829  func (vcd *TestVCD) TestVCDClient_AuthenticateInvalidPassword(check *C) {
  1830  	config, err := GetConfigStruct()
  1831  	if err != nil {
  1832  		check.Fatalf("err: %s", err)
  1833  	}
  1834  	client, err := GetTestVCDFromYaml(config)
  1835  	if err != nil {
  1836  		check.Fatalf("error getting client structure: %s", err)
  1837  	}
  1838  
  1839  	err = client.Authenticate(config.Provider.User, "INVALID-PASSWORD", config.Provider.SysOrg)
  1840  	if err == nil || !strings.Contains(err.Error(), "401") {
  1841  		check.Fatalf("expected error for invalid credentials")
  1842  	}
  1843  }
  1844  
  1845  func (vcd *TestVCD) TestVCDClient_AuthenticateInvalidToken(check *C) {
  1846  	config, err := GetConfigStruct()
  1847  	if err != nil {
  1848  		check.Fatalf("err: %s", err)
  1849  	}
  1850  	client, err := GetTestVCDFromYaml(config)
  1851  	if err != nil {
  1852  		check.Fatalf("error getting client structure: %s", err)
  1853  	}
  1854  
  1855  	err = client.SetToken(config.Provider.SysOrg, AuthorizationHeader, "invalid-token")
  1856  	if err == nil || !strings.Contains(err.Error(), "401") {
  1857  		check.Fatalf("expected error for invalid credentials")
  1858  	}
  1859  }
  1860  
  1861  func (vcd *TestVCD) findFirstVm(vapp VApp) (types.Vm, string) {
  1862  	for _, vm := range vapp.VApp.Children.VM {
  1863  		if vm.Name != "" {
  1864  			return *vm, vm.Name
  1865  		}
  1866  	}
  1867  	return types.Vm{}, ""
  1868  }
  1869  
  1870  func (vcd *TestVCD) findFirstVapp() VApp {
  1871  	client := vcd.client
  1872  	config := vcd.config
  1873  	org, err := client.GetOrgByName(config.VCD.Org)
  1874  	if err != nil {
  1875  		fmt.Println(err)
  1876  		return VApp{}
  1877  	}
  1878  	vdc, err := org.GetVDCByName(config.VCD.Vdc, false)
  1879  	if err != nil {
  1880  		fmt.Println(err)
  1881  		return VApp{}
  1882  	}
  1883  	wantedVapp := vcd.vapp.VApp.Name
  1884  	if wantedVapp == "" {
  1885  		// As no vApp is defined in config, we search for one randomly
  1886  		for _, res := range vdc.Vdc.ResourceEntities {
  1887  			for _, item := range res.ResourceEntity {
  1888  				if item.Type == "application/vnd.vmware.vcloud.vApp+xml" {
  1889  					wantedVapp = item.Name
  1890  					break
  1891  				}
  1892  			}
  1893  		}
  1894  	}
  1895  	vapp, err := vdc.GetVAppByName(wantedVapp, false)
  1896  	if err != nil {
  1897  		return VApp{}
  1898  	}
  1899  	return *vapp
  1900  }
  1901  
  1902  // Test_NewRequestWitNotEncodedParamsWithApiVersion verifies that api version override works
  1903  func (vcd *TestVCD) Test_NewRequestWitNotEncodedParamsWithApiVersion(check *C) {
  1904  	fmt.Printf("Running: %s\n", check.TestName())
  1905  	queryUlr := vcd.client.Client.VCDHREF
  1906  	queryUlr.Path += "/query"
  1907  
  1908  	apiVersion, err := vcd.client.Client.MaxSupportedVersion()
  1909  	check.Assert(err, IsNil)
  1910  
  1911  	req := vcd.client.Client.NewRequestWitNotEncodedParamsWithApiVersion(nil, map[string]string{"type": "media",
  1912  		"filter": "name==any"}, http.MethodGet, queryUlr, nil, apiVersion)
  1913  
  1914  	check.Assert(req.Header.Get("User-Agent"), Equals, vcd.client.Client.UserAgent)
  1915  
  1916  	resp, err := checkResp(vcd.client.Client.Http.Do(req))
  1917  	check.Assert(err, IsNil)
  1918  
  1919  	check.Assert(resp.Header.Get("Content-Type"), Equals, types.MimeQueryRecords+";version="+apiVersion)
  1920  
  1921  	// Repeats the call without API version change
  1922  	req = vcd.client.Client.NewRequestWitNotEncodedParams(nil, map[string]string{"type": "media",
  1923  		"filter": "name==any"}, http.MethodGet, queryUlr, nil)
  1924  
  1925  	resp, err = checkResp(vcd.client.Client.Http.Do(req))
  1926  	check.Assert(err, IsNil)
  1927  
  1928  	// Checks that the regularAPI version was not affected by the previous call
  1929  	check.Assert(resp.Header.Get("Content-Type"), Equals, types.MimeQueryRecords+";version="+vcd.client.Client.APIVersion)
  1930  
  1931  	fmt.Printf("Test: %s run with api Version: %s\n", check.TestName(), apiVersion)
  1932  }
  1933  
  1934  // setBoolFlag binds a flag to a boolean variable (passed as pointer)
  1935  // it also uses an optional environment variable that, if set, will
  1936  // update the variable before binding it to the flag.
  1937  func setBoolFlag(varPointer *bool, name, envVar, help string) {
  1938  	if envVar != "" && os.Getenv(envVar) != "" {
  1939  		*varPointer = true
  1940  	}
  1941  	flag.BoolVar(varPointer, name, *varPointer, help)
  1942  }
  1943  
  1944  // setTestEnv enables environment variables that are also used in non-test code
  1945  func setTestEnv() {
  1946  	if enableDebug {
  1947  		_ = os.Setenv("GOVCD_DEBUG", "1")
  1948  	}
  1949  	if debugShowRequestEnabled {
  1950  		_ = os.Setenv("GOVCD_SHOW_REQ", "1")
  1951  	}
  1952  	if debugShowResponseEnabled {
  1953  		_ = os.Setenv("GOVCD_SHOW_RESP", "1")
  1954  	}
  1955  }
  1956  
  1957  func skipWhenMediaPathMissing(vcd *TestVCD, check *C) {
  1958  	if vcd.config.Media.MediaPath == "" {
  1959  		check.Skip("Skipping test because no iso path given")
  1960  	}
  1961  }
  1962  
  1963  func skipNoNsxtConfiguration(vcd *TestVCD, check *C) {
  1964  	generalMessage := "Missing NSX-T config: "
  1965  	if vcd.config.VCD.NsxtProviderVdc.Name == "" {
  1966  		check.Skip(generalMessage + "No provider vdc specified")
  1967  	}
  1968  	if vcd.config.VCD.NsxtProviderVdc.NetworkPool == "" {
  1969  		check.Skip(generalMessage + "No network pool specified")
  1970  	}
  1971  
  1972  	if vcd.config.VCD.Nsxt.Vdc == "" {
  1973  		check.Skip(generalMessage + "No NSX-T VDC specified")
  1974  	}
  1975  
  1976  	if vcd.config.VCD.Nsxt.NsxtImportSegment == "" {
  1977  		check.Skip(generalMessage + "No NSX-T Unused segment (for imported Org VDC network) specified")
  1978  	}
  1979  
  1980  	if vcd.config.VCD.NsxtProviderVdc.StorageProfile == "" {
  1981  		check.Skip(generalMessage + "No storage profile specified")
  1982  	}
  1983  
  1984  	if vcd.config.VCD.Nsxt.Manager == "" {
  1985  		check.Skip(generalMessage + "No NSX-T manager specified")
  1986  	}
  1987  
  1988  	if vcd.config.VCD.Nsxt.Tier0router == "" {
  1989  		check.Skip(generalMessage + "No NSX-T Tier-0 router specified")
  1990  	}
  1991  
  1992  	if vcd.config.VCD.Nsxt.Tier0routerVrf == "" {
  1993  		check.Skip(generalMessage + "No VRF NSX-T Tier-0 router specified")
  1994  	}
  1995  
  1996  	if vcd.config.VCD.Nsxt.EdgeGateway == "" {
  1997  		check.Skip(generalMessage + "No NSX-T Edge Gateway specified in configuration")
  1998  	}
  1999  
  2000  	if vcd.config.VCD.Nsxt.IpDiscoveryProfile == "" ||
  2001  		vcd.config.VCD.Nsxt.MacDiscoveryProfile == "" ||
  2002  		vcd.config.VCD.Nsxt.SpoofGuardProfile == "" ||
  2003  		vcd.config.VCD.Nsxt.QosProfile == "" ||
  2004  		vcd.config.VCD.Nsxt.SegmentSecurityProfile == "" {
  2005  		check.Skip(generalMessage + "NSX-T Segment Profiles are not specified in configuration")
  2006  	}
  2007  }
  2008  
  2009  func skipNoNsxtAlbConfiguration(vcd *TestVCD, check *C) {
  2010  	skipNoNsxtConfiguration(vcd, check)
  2011  	generalMessage := "Missing NSX-T ALB config: "
  2012  
  2013  	if vcd.config.VCD.Nsxt.NsxtAlbControllerUrl == "" {
  2014  		check.Skip(generalMessage + "No NSX-T ALB Controller URL specified in configuration")
  2015  	}
  2016  
  2017  	if vcd.config.VCD.Nsxt.NsxtAlbControllerUser == "" {
  2018  		check.Skip(generalMessage + "No NSX-T ALB Controller Name specified in configuration")
  2019  	}
  2020  
  2021  	if vcd.config.VCD.Nsxt.NsxtAlbControllerPassword == "" {
  2022  		check.Skip(generalMessage + "No NSX-T ALB Controller Password specified in configuration")
  2023  	}
  2024  
  2025  	if vcd.config.VCD.Nsxt.NsxtAlbImportableCloud == "" {
  2026  		check.Skip(generalMessage + "No NSX-T ALB Controller Importable Cloud Name")
  2027  	}
  2028  	if vcd.config.VCD.Nsxt.NsxtAlbServiceEngineGroup == "" {
  2029  		check.Skip(generalMessage + "No NSX-T ALB Service Engine Group name specified in configuration")
  2030  	}
  2031  }
  2032  
  2033  // skipOpenApiEndpointTest is a helper to skip tests for particular unsupported OpenAPI endpoints
  2034  func skipOpenApiEndpointTest(vcd *TestVCD, check *C, endpoint string) {
  2035  	minimumRequiredApiVersion := endpointMinApiVersions[endpoint]
  2036  
  2037  	constraint := ">= " + minimumRequiredApiVersion
  2038  	if !vcd.client.Client.APIVCDMaxVersionIs(constraint) {
  2039  		maxSupportedVersion, err := vcd.client.Client.MaxSupportedVersion()
  2040  		if err != nil {
  2041  			panic(fmt.Sprintf("Could not get maximum supported version: %s", err))
  2042  		}
  2043  		skipText := fmt.Sprintf("Skipping test because OpenAPI endpoint '%s' must satisfy API version constraint '%s'. Maximum supported version is %s",
  2044  			endpoint, constraint, maxSupportedVersion)
  2045  		check.Skip(skipText)
  2046  	}
  2047  }
  2048  
  2049  // newOrgUserConnection creates a new Org User and returns a connection to it.
  2050  // Attention: Set the user to use only lowercase letters. If you put upper case letters the function fails on waiting
  2051  // because VCD creates the user with lowercase letters.
  2052  func newOrgUserConnection(adminOrg *AdminOrg, userName, password, href string, insecure bool) (*VCDClient, *OrgUser, error) {
  2053  	u, err := url.ParseRequestURI(href)
  2054  	if err != nil {
  2055  		return nil, nil, fmt.Errorf("[newOrgUserConnection] unable to pass url: %s", err)
  2056  	}
  2057  
  2058  	_, err = adminOrg.GetUserByName(userName, false)
  2059  	if err == nil {
  2060  		// user exists
  2061  		return nil, nil, fmt.Errorf("user %s already exists", userName)
  2062  	}
  2063  	_, err = adminOrg.CreateUserSimple(OrgUserConfiguration{
  2064  		Name:            userName,
  2065  		Password:        password,
  2066  		RoleName:        OrgUserRoleOrganizationAdministrator,
  2067  		ProviderType:    OrgUserProviderIntegrated,
  2068  		IsEnabled:       true,
  2069  		DeployedVmQuota: 0,
  2070  		StoredVmQuota:   0,
  2071  		FullName:        userName,
  2072  		Description:     "Test user created by newOrgUserConnection",
  2073  	})
  2074  	if err != nil {
  2075  		return nil, nil, err
  2076  	}
  2077  
  2078  	AddToCleanupList(userName, "user", adminOrg.AdminOrg.Name, "newOrgUserConnection")
  2079  
  2080  	_ = adminOrg.Refresh()
  2081  	vcdClient := NewVCDClient(*u, insecure)
  2082  	err = vcdClient.Authenticate(userName, password, adminOrg.AdminOrg.Name)
  2083  	if err != nil {
  2084  		return nil, nil, fmt.Errorf("[newOrgUserConnection] unable to authenticate: %s", err)
  2085  	}
  2086  
  2087  	// return newUser
  2088  	newUser, err := adminOrg.GetUserByName(userName, false)
  2089  	if err != nil {
  2090  		return nil, nil, fmt.Errorf("[newOrgUserConnection] unable to retrieve newly created user: %s", err)
  2091  	}
  2092  
  2093  	return vcdClient, newUser, nil
  2094  }
  2095  
  2096  func (vcd *TestVCD) skipIfNotSysAdmin(check *C) {
  2097  	if !vcd.client.Client.IsSysAdmin {
  2098  		check.Skip(fmt.Sprintf("Skipping %s: requires system administrator privileges", check.TestName()))
  2099  	}
  2100  }
  2101  
  2102  // retryOnError is a function that will attempt to execute function with signature `func() error`
  2103  // multiple times (until maxRetries) and waiting given retryInterval between tries. It will return
  2104  // original deletion error for troubleshooting.
  2105  func retryOnError(operation func() error, maxRetries int, retryInterval time.Duration) error {
  2106  	var err error
  2107  	for attempt := 0; attempt < maxRetries; attempt++ {
  2108  		err = operation()
  2109  		if err == nil {
  2110  			return nil
  2111  		}
  2112  
  2113  		fmt.Printf("# retrying after %v (Attempt %d/%d)\n", retryInterval, attempt+1, maxRetries)
  2114  		fmt.Printf("# error was: %s", err)
  2115  		time.Sleep(retryInterval)
  2116  	}
  2117  
  2118  	return fmt.Errorf("exceeded maximum retries, final error: %s", err)
  2119  }