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