1// Package v2 contains common functions for creating compute-based resources 2// for use in acceptance tests. See the `*_test.go` files for example usages. 3package v2 4 5import ( 6 "crypto/rand" 7 "crypto/rsa" 8 "fmt" 9 "testing" 10 11 "github.com/gophercloud/gophercloud" 12 "github.com/gophercloud/gophercloud/acceptance/clients" 13 "github.com/gophercloud/gophercloud/acceptance/tools" 14 "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes" 15 "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/aggregates" 16 "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/attachinterfaces" 17 "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/bootfromvolume" 18 dsr "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/defsecrules" 19 "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips" 20 "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs" 21 "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/networks" 22 "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/quotasets" 23 "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/remoteconsoles" 24 "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/rescueunrescue" 25 "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/schedulerhints" 26 "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups" 27 "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups" 28 "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/tenantnetworks" 29 "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach" 30 "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors" 31 "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" 32 neutron "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" 33 th "github.com/gophercloud/gophercloud/testhelper" 34 35 "golang.org/x/crypto/ssh" 36) 37 38// AssociateFloatingIP will associate a floating IP with an instance. An error 39// will be returned if the floating IP was unable to be associated. 40func AssociateFloatingIP(t *testing.T, client *gophercloud.ServiceClient, floatingIP *floatingips.FloatingIP, server *servers.Server) error { 41 associateOpts := floatingips.AssociateOpts{ 42 FloatingIP: floatingIP.IP, 43 } 44 45 t.Logf("Attempting to associate floating IP %s to instance %s", floatingIP.IP, server.ID) 46 err := floatingips.AssociateInstance(client, server.ID, associateOpts).ExtractErr() 47 if err != nil { 48 return err 49 } 50 51 return nil 52} 53 54// AssociateFloatingIPWithFixedIP will associate a floating IP with an 55// instance's specific fixed IP. An error will be returend if the floating IP 56// was unable to be associated. 57func AssociateFloatingIPWithFixedIP(t *testing.T, client *gophercloud.ServiceClient, floatingIP *floatingips.FloatingIP, server *servers.Server, fixedIP string) error { 58 associateOpts := floatingips.AssociateOpts{ 59 FloatingIP: floatingIP.IP, 60 FixedIP: fixedIP, 61 } 62 63 t.Logf("Attempting to associate floating IP %s to fixed IP %s on instance %s", floatingIP.IP, fixedIP, server.ID) 64 err := floatingips.AssociateInstance(client, server.ID, associateOpts).ExtractErr() 65 if err != nil { 66 return err 67 } 68 69 return nil 70} 71 72// AttachInterface will create and attach an interface on a given server. 73// An error will returned if the interface could not be created. 74func AttachInterface(t *testing.T, client *gophercloud.ServiceClient, serverID string) (*attachinterfaces.Interface, error) { 75 t.Logf("Attempting to attach interface to server %s", serverID) 76 77 choices, err := clients.AcceptanceTestChoicesFromEnv() 78 if err != nil { 79 t.Fatal(err) 80 } 81 82 networkID, err := GetNetworkIDFromNetworks(t, client, choices.NetworkName) 83 if err != nil { 84 return nil, err 85 } 86 87 createOpts := attachinterfaces.CreateOpts{ 88 NetworkID: networkID, 89 } 90 91 iface, err := attachinterfaces.Create(client, serverID, createOpts).Extract() 92 if err != nil { 93 return nil, err 94 } 95 96 t.Logf("Successfully created interface %s on server %s", iface.PortID, serverID) 97 98 return iface, nil 99} 100 101// CreateAggregate will create an aggregate with random name and available zone. 102// An error will be returned if the aggregate could not be created. 103func CreateAggregate(t *testing.T, client *gophercloud.ServiceClient) (*aggregates.Aggregate, error) { 104 aggregateName := tools.RandomString("aggregate_", 5) 105 availabilityZone := tools.RandomString("zone_", 5) 106 t.Logf("Attempting to create aggregate %s", aggregateName) 107 108 createOpts := aggregates.CreateOpts{ 109 Name: aggregateName, 110 AvailabilityZone: availabilityZone, 111 } 112 113 aggregate, err := aggregates.Create(client, createOpts).Extract() 114 if err != nil { 115 return nil, err 116 } 117 118 t.Logf("Successfully created aggregate %d", aggregate.ID) 119 120 aggregate, err = aggregates.Get(client, aggregate.ID).Extract() 121 if err != nil { 122 return nil, err 123 } 124 125 th.AssertEquals(t, aggregate.Name, aggregateName) 126 th.AssertEquals(t, aggregate.AvailabilityZone, availabilityZone) 127 128 return aggregate, nil 129} 130 131// CreateBootableVolumeServer works like CreateServer but is configured with 132// one or more block devices defined by passing in []bootfromvolume.BlockDevice. 133// An error will be returned if a server was unable to be created. 134func CreateBootableVolumeServer(t *testing.T, client *gophercloud.ServiceClient, blockDevices []bootfromvolume.BlockDevice) (*servers.Server, error) { 135 var server *servers.Server 136 137 choices, err := clients.AcceptanceTestChoicesFromEnv() 138 if err != nil { 139 t.Fatal(err) 140 } 141 142 networkID, err := GetNetworkIDFromNetworks(t, client, choices.NetworkName) 143 if err != nil { 144 return server, err 145 } 146 147 name := tools.RandomString("ACPTTEST", 16) 148 t.Logf("Attempting to create bootable volume server: %s", name) 149 150 serverCreateOpts := servers.CreateOpts{ 151 Name: name, 152 FlavorRef: choices.FlavorID, 153 Networks: []servers.Network{ 154 {UUID: networkID}, 155 }, 156 } 157 158 if blockDevices[0].SourceType == bootfromvolume.SourceImage && blockDevices[0].DestinationType == bootfromvolume.DestinationLocal { 159 serverCreateOpts.ImageRef = blockDevices[0].UUID 160 } 161 162 server, err = bootfromvolume.Create(client, bootfromvolume.CreateOptsExt{ 163 CreateOptsBuilder: serverCreateOpts, 164 BlockDevice: blockDevices, 165 }).Extract() 166 167 if err != nil { 168 return server, err 169 } 170 171 if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil { 172 return server, err 173 } 174 175 newServer, err := servers.Get(client, server.ID).Extract() 176 if err != nil { 177 return nil, err 178 } 179 180 th.AssertEquals(t, newServer.Name, name) 181 th.AssertEquals(t, newServer.Flavor["id"], choices.FlavorID) 182 183 return newServer, nil 184} 185 186// CreateDefaultRule will create a default security group rule with a 187// random port range between 80 and 90. An error will be returned if 188// a default rule was unable to be created. 189func CreateDefaultRule(t *testing.T, client *gophercloud.ServiceClient) (dsr.DefaultRule, error) { 190 createOpts := dsr.CreateOpts{ 191 FromPort: tools.RandomInt(80, 89), 192 ToPort: tools.RandomInt(90, 99), 193 IPProtocol: "TCP", 194 CIDR: "0.0.0.0/0", 195 } 196 197 defaultRule, err := dsr.Create(client, createOpts).Extract() 198 if err != nil { 199 return *defaultRule, err 200 } 201 202 t.Logf("Created default rule: %s", defaultRule.ID) 203 204 return *defaultRule, nil 205} 206 207// CreateFlavor will create a flavor with a random name. 208// An error will be returned if the flavor could not be created. 209func CreateFlavor(t *testing.T, client *gophercloud.ServiceClient) (*flavors.Flavor, error) { 210 flavorName := tools.RandomString("flavor_", 5) 211 t.Logf("Attempting to create flavor %s", flavorName) 212 213 isPublic := true 214 createOpts := flavors.CreateOpts{ 215 Name: flavorName, 216 RAM: 1, 217 VCPUs: 1, 218 Disk: gophercloud.IntToPointer(1), 219 IsPublic: &isPublic, 220 } 221 222 flavor, err := flavors.Create(client, createOpts).Extract() 223 if err != nil { 224 return nil, err 225 } 226 227 t.Logf("Successfully created flavor %s", flavor.ID) 228 229 th.AssertEquals(t, flavor.Name, flavorName) 230 th.AssertEquals(t, flavor.RAM, 1) 231 th.AssertEquals(t, flavor.Disk, 1) 232 th.AssertEquals(t, flavor.VCPUs, 1) 233 th.AssertEquals(t, flavor.IsPublic, true) 234 235 return flavor, nil 236} 237 238// CreateFloatingIP will allocate a floating IP. 239// An error will be returend if one was unable to be allocated. 240func CreateFloatingIP(t *testing.T, client *gophercloud.ServiceClient) (*floatingips.FloatingIP, error) { 241 choices, err := clients.AcceptanceTestChoicesFromEnv() 242 if err != nil { 243 t.Fatal(err) 244 } 245 246 createOpts := floatingips.CreateOpts{ 247 Pool: choices.FloatingIPPoolName, 248 } 249 floatingIP, err := floatingips.Create(client, createOpts).Extract() 250 if err != nil { 251 return floatingIP, err 252 } 253 254 t.Logf("Created floating IP: %s", floatingIP.ID) 255 return floatingIP, nil 256} 257 258func createKey() (string, error) { 259 privateKey, err := rsa.GenerateKey(rand.Reader, 2048) 260 if err != nil { 261 return "", err 262 } 263 264 publicKey := privateKey.PublicKey 265 pub, err := ssh.NewPublicKey(&publicKey) 266 if err != nil { 267 return "", err 268 } 269 270 pubBytes := ssh.MarshalAuthorizedKey(pub) 271 pk := string(pubBytes) 272 return pk, nil 273} 274 275// CreateKeyPair will create a KeyPair with a random name. An error will occur 276// if the keypair failed to be created. An error will be returned if the 277// keypair was unable to be created. 278func CreateKeyPair(t *testing.T, client *gophercloud.ServiceClient) (*keypairs.KeyPair, error) { 279 keyPairName := tools.RandomString("keypair_", 5) 280 281 t.Logf("Attempting to create keypair: %s", keyPairName) 282 createOpts := keypairs.CreateOpts{ 283 Name: keyPairName, 284 } 285 keyPair, err := keypairs.Create(client, createOpts).Extract() 286 if err != nil { 287 return keyPair, err 288 } 289 290 t.Logf("Created keypair: %s", keyPairName) 291 292 th.AssertEquals(t, keyPair.Name, keyPairName) 293 294 return keyPair, nil 295} 296 297// CreateMultiEphemeralServer works like CreateServer but is configured with 298// one or more block devices defined by passing in []bootfromvolume.BlockDevice. 299// These block devices act like block devices when booting from a volume but 300// are actually local ephemeral disks. 301// An error will be returned if a server was unable to be created. 302func CreateMultiEphemeralServer(t *testing.T, client *gophercloud.ServiceClient, blockDevices []bootfromvolume.BlockDevice) (*servers.Server, error) { 303 var server *servers.Server 304 305 choices, err := clients.AcceptanceTestChoicesFromEnv() 306 if err != nil { 307 t.Fatal(err) 308 } 309 310 networkID, err := GetNetworkIDFromNetworks(t, client, choices.NetworkName) 311 if err != nil { 312 return server, err 313 } 314 315 name := tools.RandomString("ACPTTEST", 16) 316 t.Logf("Attempting to create bootable volume server: %s", name) 317 318 serverCreateOpts := servers.CreateOpts{ 319 Name: name, 320 FlavorRef: choices.FlavorID, 321 ImageRef: choices.ImageID, 322 Networks: []servers.Network{ 323 {UUID: networkID}, 324 }, 325 } 326 327 server, err = bootfromvolume.Create(client, bootfromvolume.CreateOptsExt{ 328 CreateOptsBuilder: serverCreateOpts, 329 BlockDevice: blockDevices, 330 }).Extract() 331 332 if err != nil { 333 return server, err 334 } 335 336 if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil { 337 return server, err 338 } 339 340 newServer, err := servers.Get(client, server.ID).Extract() 341 if err != nil { 342 return server, err 343 } 344 th.AssertEquals(t, newServer.Name, name) 345 th.AssertEquals(t, newServer.Flavor["id"], choices.FlavorID) 346 th.AssertEquals(t, newServer.Image["id"], choices.ImageID) 347 348 return newServer, nil 349} 350 351// CreatePrivateFlavor will create a private flavor with a random name. 352// An error will be returned if the flavor could not be created. 353func CreatePrivateFlavor(t *testing.T, client *gophercloud.ServiceClient) (*flavors.Flavor, error) { 354 flavorName := tools.RandomString("flavor_", 5) 355 t.Logf("Attempting to create flavor %s", flavorName) 356 357 isPublic := false 358 createOpts := flavors.CreateOpts{ 359 Name: flavorName, 360 RAM: 1, 361 VCPUs: 1, 362 Disk: gophercloud.IntToPointer(1), 363 IsPublic: &isPublic, 364 } 365 366 flavor, err := flavors.Create(client, createOpts).Extract() 367 if err != nil { 368 return nil, err 369 } 370 371 t.Logf("Successfully created flavor %s", flavor.ID) 372 373 th.AssertEquals(t, flavor.Name, flavorName) 374 th.AssertEquals(t, flavor.RAM, 1) 375 th.AssertEquals(t, flavor.Disk, 1) 376 th.AssertEquals(t, flavor.VCPUs, 1) 377 th.AssertEquals(t, flavor.IsPublic, false) 378 379 return flavor, nil 380} 381 382// CreateSecurityGroup will create a security group with a random name. 383// An error will be returned if one was failed to be created. 384func CreateSecurityGroup(t *testing.T, client *gophercloud.ServiceClient) (*secgroups.SecurityGroup, error) { 385 name := tools.RandomString("secgroup_", 5) 386 387 createOpts := secgroups.CreateOpts{ 388 Name: name, 389 Description: "something", 390 } 391 392 securityGroup, err := secgroups.Create(client, createOpts).Extract() 393 if err != nil { 394 return nil, err 395 } 396 397 t.Logf("Created security group: %s", securityGroup.ID) 398 399 th.AssertEquals(t, securityGroup.Name, name) 400 401 return securityGroup, nil 402} 403 404// CreateSecurityGroupRule will create a security group rule with a random name 405// and a random TCP port range between port 80 and 99. An error will be 406// returned if the rule failed to be created. 407func CreateSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, securityGroupID string) (*secgroups.Rule, error) { 408 fromPort := tools.RandomInt(80, 89) 409 toPort := tools.RandomInt(90, 99) 410 createOpts := secgroups.CreateRuleOpts{ 411 ParentGroupID: securityGroupID, 412 FromPort: fromPort, 413 ToPort: toPort, 414 IPProtocol: "TCP", 415 CIDR: "0.0.0.0/0", 416 } 417 418 rule, err := secgroups.CreateRule(client, createOpts).Extract() 419 if err != nil { 420 return nil, err 421 } 422 423 t.Logf("Created security group rule: %s", rule.ID) 424 425 th.AssertEquals(t, rule.FromPort, fromPort) 426 th.AssertEquals(t, rule.ToPort, toPort) 427 th.AssertEquals(t, rule.ParentGroupID, securityGroupID) 428 429 return rule, nil 430} 431 432// CreateServer creates a basic instance with a randomly generated name. 433// The flavor of the instance will be the value of the OS_FLAVOR_ID environment variable. 434// The image will be the value of the OS_IMAGE_ID environment variable. 435// The instance will be launched on the network specified in OS_NETWORK_NAME. 436// An error will be returned if the instance was unable to be created. 437func CreateServer(t *testing.T, client *gophercloud.ServiceClient) (*servers.Server, error) { 438 choices, err := clients.AcceptanceTestChoicesFromEnv() 439 if err != nil { 440 t.Fatal(err) 441 } 442 443 networkID, err := GetNetworkIDFromNetworks(t, client, choices.NetworkName) 444 if err != nil { 445 return nil, err 446 } 447 448 name := tools.RandomString("ACPTTEST", 16) 449 t.Logf("Attempting to create server: %s", name) 450 451 pwd := tools.MakeNewPassword("") 452 453 server, err := servers.Create(client, servers.CreateOpts{ 454 Name: name, 455 FlavorRef: choices.FlavorID, 456 ImageRef: choices.ImageID, 457 AdminPass: pwd, 458 Networks: []servers.Network{ 459 {UUID: networkID}, 460 }, 461 Metadata: map[string]string{ 462 "abc": "def", 463 }, 464 Personality: servers.Personality{ 465 &servers.File{ 466 Path: "/etc/test", 467 Contents: []byte("hello world"), 468 }, 469 }, 470 }).Extract() 471 if err != nil { 472 return server, err 473 } 474 475 if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil { 476 return nil, err 477 } 478 479 newServer, err := servers.Get(client, server.ID).Extract() 480 if err != nil { 481 return nil, err 482 } 483 484 th.AssertEquals(t, newServer.Name, name) 485 th.AssertEquals(t, newServer.Flavor["id"], choices.FlavorID) 486 th.AssertEquals(t, newServer.Image["id"], choices.ImageID) 487 488 return newServer, nil 489} 490 491// CreateMicroversionServer creates a basic instance compatible with 492// newer microversions with a randomly generated name. 493// The flavor of the instance will be the value of the OS_FLAVOR_ID environment variable. 494// The image will be the value of the OS_IMAGE_ID environment variable. 495// The instance will be launched on the network specified in OS_NETWORK_NAME. 496// An error will be returned if the instance was unable to be created. 497func CreateMicroversionServer(t *testing.T, client *gophercloud.ServiceClient) (*servers.Server, error) { 498 choices, err := clients.AcceptanceTestChoicesFromEnv() 499 if err != nil { 500 t.Fatal(err) 501 } 502 503 networkID, err := GetNetworkIDFromNetworks(t, client, choices.NetworkName) 504 if err != nil { 505 return nil, err 506 } 507 508 name := tools.RandomString("ACPTTEST", 16) 509 t.Logf("Attempting to create server: %s", name) 510 511 pwd := tools.MakeNewPassword("") 512 513 server, err := servers.Create(client, servers.CreateOpts{ 514 Name: name, 515 FlavorRef: choices.FlavorID, 516 ImageRef: choices.ImageID, 517 AdminPass: pwd, 518 Networks: []servers.Network{ 519 {UUID: networkID}, 520 }, 521 Metadata: map[string]string{ 522 "abc": "def", 523 }, 524 }).Extract() 525 if err != nil { 526 return server, err 527 } 528 529 if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil { 530 return nil, err 531 } 532 533 newServer, err := servers.Get(client, server.ID).Extract() 534 if err != nil { 535 return nil, err 536 } 537 538 th.AssertEquals(t, newServer.Name, name) 539 th.AssertEquals(t, newServer.Image["id"], choices.ImageID) 540 541 return newServer, nil 542} 543 544// CreateServerWithoutImageRef creates a basic instance with a randomly generated name. 545// The flavor of the instance will be the value of the OS_FLAVOR_ID environment variable. 546// The image is intentionally missing to trigger an error. 547// The instance will be launched on the network specified in OS_NETWORK_NAME. 548// An error will be returned if the instance was unable to be created. 549func CreateServerWithoutImageRef(t *testing.T, client *gophercloud.ServiceClient) (*servers.Server, error) { 550 choices, err := clients.AcceptanceTestChoicesFromEnv() 551 if err != nil { 552 t.Fatal(err) 553 } 554 555 networkID, err := GetNetworkIDFromNetworks(t, client, choices.NetworkName) 556 if err != nil { 557 return nil, err 558 } 559 560 name := tools.RandomString("ACPTTEST", 16) 561 t.Logf("Attempting to create server: %s", name) 562 563 pwd := tools.MakeNewPassword("") 564 565 server, err := servers.Create(client, servers.CreateOpts{ 566 Name: name, 567 FlavorRef: choices.FlavorID, 568 AdminPass: pwd, 569 Networks: []servers.Network{ 570 {UUID: networkID}, 571 }, 572 Personality: servers.Personality{ 573 &servers.File{ 574 Path: "/etc/test", 575 Contents: []byte("hello world"), 576 }, 577 }, 578 }).Extract() 579 if err != nil { 580 return nil, err 581 } 582 583 if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil { 584 return nil, err 585 } 586 587 return server, nil 588} 589 590// CreateServerWithTags creates a basic instance with a randomly generated name. 591// The flavor of the instance will be the value of the OS_FLAVOR_ID environment variable. 592// The image will be the value of the OS_IMAGE_ID environment variable. 593// The instance will be launched on the network specified in OS_NETWORK_NAME. 594// Two tags will be assigned to the server. 595// An error will be returned if the instance was unable to be created. 596func CreateServerWithTags(t *testing.T, client *gophercloud.ServiceClient, networkID string) (*servers.Server, error) { 597 choices, err := clients.AcceptanceTestChoicesFromEnv() 598 if err != nil { 599 t.Fatal(err) 600 } 601 602 name := tools.RandomString("ACPTTEST", 16) 603 t.Logf("Attempting to create server: %s", name) 604 605 pwd := tools.MakeNewPassword("") 606 607 server, err := servers.Create(client, servers.CreateOpts{ 608 Name: name, 609 FlavorRef: choices.FlavorID, 610 ImageRef: choices.ImageID, 611 AdminPass: pwd, 612 Networks: []servers.Network{ 613 {UUID: networkID}, 614 }, 615 Metadata: map[string]string{ 616 "abc": "def", 617 }, 618 Personality: servers.Personality{ 619 &servers.File{ 620 Path: "/etc/test", 621 Contents: []byte("hello world"), 622 }, 623 }, 624 Tags: []string{"tag1", "tag2"}, 625 }).Extract() 626 if err != nil { 627 return server, err 628 } 629 630 if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil { 631 return nil, err 632 } 633 634 res := servers.Get(client, server.ID) 635 if res.Err != nil { 636 return nil, res.Err 637 } 638 639 newServer, err := res.Extract() 640 th.AssertNoErr(t, err) 641 th.AssertEquals(t, newServer.Name, name) 642 th.AssertDeepEquals(t, *newServer.Tags, []string{"tag1", "tag2"}) 643 644 return newServer, nil 645} 646 647// CreateServerGroup will create a server with a random name. An error will be 648// returned if the server group failed to be created. 649func CreateServerGroup(t *testing.T, client *gophercloud.ServiceClient, policy string) (*servergroups.ServerGroup, error) { 650 name := tools.RandomString("ACPTTEST", 16) 651 652 t.Logf("Attempting to create server group %s", name) 653 654 sg, err := servergroups.Create(client, &servergroups.CreateOpts{ 655 Name: name, 656 Policies: []string{policy}, 657 }).Extract() 658 659 if err != nil { 660 return nil, err 661 } 662 663 t.Logf("Successfully created server group %s", name) 664 665 th.AssertEquals(t, sg.Name, name) 666 667 return sg, nil 668} 669 670// CreateServerGroupMicroversion will create a server with a random name using 2.64 microversion. An error will be 671// returned if the server group failed to be created. 672func CreateServerGroupMicroversion(t *testing.T, client *gophercloud.ServiceClient) (*servergroups.ServerGroup, error) { 673 name := tools.RandomString("ACPTTEST", 16) 674 policy := "anti-affinity" 675 maxServerPerHost := 3 676 677 t.Logf("Attempting to create %s server group with max server per host = %d: %s", policy, maxServerPerHost, name) 678 679 sg, err := servergroups.Create(client, &servergroups.CreateOpts{ 680 Name: name, 681 Policy: policy, 682 Rules: &servergroups.Rules{ 683 MaxServerPerHost: maxServerPerHost, 684 }, 685 }).Extract() 686 687 if err != nil { 688 return nil, err 689 } 690 691 t.Logf("Successfully created server group %s", name) 692 693 th.AssertEquals(t, sg.Name, name) 694 695 return sg, nil 696} 697 698// CreateServerInServerGroup works like CreateServer but places the instance in 699// a specified Server Group. 700func CreateServerInServerGroup(t *testing.T, client *gophercloud.ServiceClient, serverGroup *servergroups.ServerGroup) (*servers.Server, error) { 701 choices, err := clients.AcceptanceTestChoicesFromEnv() 702 if err != nil { 703 t.Fatal(err) 704 } 705 706 networkID, err := GetNetworkIDFromNetworks(t, client, choices.NetworkName) 707 if err != nil { 708 return nil, err 709 } 710 711 name := tools.RandomString("ACPTTEST", 16) 712 t.Logf("Attempting to create server: %s", name) 713 714 pwd := tools.MakeNewPassword("") 715 716 serverCreateOpts := servers.CreateOpts{ 717 Name: name, 718 FlavorRef: choices.FlavorID, 719 ImageRef: choices.ImageID, 720 AdminPass: pwd, 721 Networks: []servers.Network{ 722 {UUID: networkID}, 723 }, 724 } 725 726 schedulerHintsOpts := schedulerhints.CreateOptsExt{ 727 CreateOptsBuilder: serverCreateOpts, 728 SchedulerHints: schedulerhints.SchedulerHints{ 729 Group: serverGroup.ID, 730 }, 731 } 732 server, err := servers.Create(client, schedulerHintsOpts).Extract() 733 if err != nil { 734 return nil, err 735 } 736 737 if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil { 738 return nil, err 739 } 740 741 newServer, err := servers.Get(client, server.ID).Extract() 742 if err != nil { 743 return nil, err 744 } 745 746 th.AssertEquals(t, newServer.Name, name) 747 th.AssertEquals(t, newServer.Flavor["id"], choices.FlavorID) 748 th.AssertEquals(t, newServer.Image["id"], choices.ImageID) 749 750 return newServer, nil 751} 752 753// CreateServerWithPublicKey works the same as CreateServer, but additionally 754// configures the server with a specified Key Pair name. 755func CreateServerWithPublicKey(t *testing.T, client *gophercloud.ServiceClient, keyPairName string) (*servers.Server, error) { 756 choices, err := clients.AcceptanceTestChoicesFromEnv() 757 if err != nil { 758 t.Fatal(err) 759 } 760 761 networkID, err := GetNetworkIDFromNetworks(t, client, choices.NetworkName) 762 if err != nil { 763 return nil, err 764 } 765 766 name := tools.RandomString("ACPTTEST", 16) 767 t.Logf("Attempting to create server: %s", name) 768 769 serverCreateOpts := servers.CreateOpts{ 770 Name: name, 771 FlavorRef: choices.FlavorID, 772 ImageRef: choices.ImageID, 773 Networks: []servers.Network{ 774 {UUID: networkID}, 775 }, 776 } 777 778 server, err := servers.Create(client, keypairs.CreateOptsExt{ 779 CreateOptsBuilder: serverCreateOpts, 780 KeyName: keyPairName, 781 }).Extract() 782 if err != nil { 783 return nil, err 784 } 785 786 if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil { 787 return nil, err 788 } 789 790 newServer, err := servers.Get(client, server.ID).Extract() 791 if err != nil { 792 return nil, err 793 } 794 795 th.AssertEquals(t, newServer.Name, name) 796 th.AssertEquals(t, newServer.Flavor["id"], choices.FlavorID) 797 th.AssertEquals(t, newServer.Image["id"], choices.ImageID) 798 799 return newServer, nil 800} 801 802// CreateVolumeAttachment will attach a volume to a server. An error will be 803// returned if the volume failed to attach. 804func CreateVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, blockClient *gophercloud.ServiceClient, server *servers.Server, volume *volumes.Volume) (*volumeattach.VolumeAttachment, error) { 805 tag := tools.RandomString("ACPTTEST", 16) 806 dot := false 807 808 volumeAttachOptions := volumeattach.CreateOpts{ 809 VolumeID: volume.ID, 810 Tag: tag, 811 DeleteOnTermination: dot, 812 } 813 814 t.Logf("Attempting to attach volume %s to server %s", volume.ID, server.ID) 815 volumeAttachment, err := volumeattach.Create(client, server.ID, volumeAttachOptions).Extract() 816 if err != nil { 817 return volumeAttachment, err 818 } 819 820 if err := volumes.WaitForStatus(blockClient, volume.ID, "in-use", 60); err != nil { 821 return volumeAttachment, err 822 } 823 824 return volumeAttachment, nil 825} 826 827// DeleteAggregate will delete a given host aggregate. A fatal error will occur if 828// the aggregate deleting is failed. This works best when using it as a 829// deferred function. 830func DeleteAggregate(t *testing.T, client *gophercloud.ServiceClient, aggregate *aggregates.Aggregate) { 831 err := aggregates.Delete(client, aggregate.ID).ExtractErr() 832 if err != nil { 833 t.Fatalf("Unable to delete aggregate %d", aggregate.ID) 834 } 835 836 t.Logf("Deleted aggregate: %d", aggregate.ID) 837} 838 839// DeleteDefaultRule deletes a default security group rule. 840// A fatal error will occur if the rule failed to delete. This works best when 841// using it as a deferred function. 842func DeleteDefaultRule(t *testing.T, client *gophercloud.ServiceClient, defaultRule dsr.DefaultRule) { 843 err := dsr.Delete(client, defaultRule.ID).ExtractErr() 844 if err != nil { 845 t.Fatalf("Unable to delete default rule %s: %v", defaultRule.ID, err) 846 } 847 848 t.Logf("Deleted default rule: %s", defaultRule.ID) 849} 850 851// DeleteFlavor will delete a flavor. A fatal error will occur if the flavor 852// could not be deleted. This works best when using it as a deferred function. 853func DeleteFlavor(t *testing.T, client *gophercloud.ServiceClient, flavor *flavors.Flavor) { 854 err := flavors.Delete(client, flavor.ID).ExtractErr() 855 if err != nil { 856 t.Fatalf("Unable to delete flavor %s", flavor.ID) 857 } 858 859 t.Logf("Deleted flavor: %s", flavor.ID) 860} 861 862// DeleteFloatingIP will de-allocate a floating IP. A fatal error will occur if 863// the floating IP failed to de-allocate. This works best when using it as a 864// deferred function. 865func DeleteFloatingIP(t *testing.T, client *gophercloud.ServiceClient, floatingIP *floatingips.FloatingIP) { 866 err := floatingips.Delete(client, floatingIP.ID).ExtractErr() 867 if err != nil { 868 t.Fatalf("Unable to delete floating IP %s: %v", floatingIP.ID, err) 869 } 870 871 t.Logf("Deleted floating IP: %s", floatingIP.ID) 872} 873 874// DeleteKeyPair will delete a specified keypair. A fatal error will occur if 875// the keypair failed to be deleted. This works best when used as a deferred 876// function. 877func DeleteKeyPair(t *testing.T, client *gophercloud.ServiceClient, keyPair *keypairs.KeyPair) { 878 err := keypairs.Delete(client, keyPair.Name, nil).ExtractErr() 879 if err != nil { 880 t.Fatalf("Unable to delete keypair %s: %v", keyPair.Name, err) 881 } 882 883 t.Logf("Deleted keypair: %s", keyPair.Name) 884} 885 886// DeleteSecurityGroup will delete a security group. A fatal error will occur 887// if the group failed to be deleted. This works best as a deferred function. 888func DeleteSecurityGroup(t *testing.T, client *gophercloud.ServiceClient, securityGroupID string) { 889 err := secgroups.Delete(client, securityGroupID).ExtractErr() 890 if err != nil { 891 t.Fatalf("Unable to delete security group %s: %s", securityGroupID, err) 892 } 893 894 t.Logf("Deleted security group: %s", securityGroupID) 895} 896 897// DeleteSecurityGroupRule will delete a security group rule. A fatal error 898// will occur if the rule failed to be deleted. This works best when used 899// as a deferred function. 900func DeleteSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, ruleID string) { 901 err := secgroups.DeleteRule(client, ruleID).ExtractErr() 902 if err != nil { 903 t.Fatalf("Unable to delete rule: %v", err) 904 } 905 906 t.Logf("Deleted security group rule: %s", ruleID) 907} 908 909// DeleteServer deletes an instance via its UUID. 910// A fatal error will occur if the instance failed to be destroyed. This works 911// best when using it as a deferred function. 912func DeleteServer(t *testing.T, client *gophercloud.ServiceClient, server *servers.Server) { 913 err := servers.Delete(client, server.ID).ExtractErr() 914 if err != nil { 915 t.Fatalf("Unable to delete server %s: %s", server.ID, err) 916 } 917 918 if err := WaitForComputeStatus(client, server, "DELETED"); err != nil { 919 if _, ok := err.(gophercloud.ErrDefault404); ok { 920 t.Logf("Deleted server: %s", server.ID) 921 return 922 } 923 t.Fatalf("Error deleting server %s: %s", server.ID, err) 924 } 925 926 // If we reach this point, the API returned an actual DELETED status 927 // which is a very short window of time, but happens occasionally. 928 t.Logf("Deleted server: %s", server.ID) 929} 930 931// DeleteServerGroup will delete a server group. A fatal error will occur if 932// the server group failed to be deleted. This works best when used as a 933// deferred function. 934func DeleteServerGroup(t *testing.T, client *gophercloud.ServiceClient, serverGroup *servergroups.ServerGroup) { 935 err := servergroups.Delete(client, serverGroup.ID).ExtractErr() 936 if err != nil { 937 t.Fatalf("Unable to delete server group %s: %v", serverGroup.ID, err) 938 } 939 940 t.Logf("Deleted server group %s", serverGroup.ID) 941} 942 943// DeleteVolumeAttachment will disconnect a volume from an instance. A fatal 944// error will occur if the volume failed to detach. This works best when used 945// as a deferred function. 946func DeleteVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, blockClient *gophercloud.ServiceClient, server *servers.Server, volumeAttachment *volumeattach.VolumeAttachment) { 947 948 err := volumeattach.Delete(client, server.ID, volumeAttachment.VolumeID).ExtractErr() 949 if err != nil { 950 t.Fatalf("Unable to detach volume: %v", err) 951 } 952 953 if err := volumes.WaitForStatus(blockClient, volumeAttachment.ID, "available", 60); err != nil { 954 t.Fatalf("Unable to wait for volume: %v", err) 955 } 956 t.Logf("Deleted volume: %s", volumeAttachment.VolumeID) 957} 958 959// DetachInterface will detach an interface from a server. A fatal 960// error will occur if the interface could not be detached. This works best 961// when used as a deferred function. 962func DetachInterface(t *testing.T, client *gophercloud.ServiceClient, serverID, portID string) { 963 t.Logf("Attempting to detach interface %s from server %s", portID, serverID) 964 965 err := attachinterfaces.Delete(client, serverID, portID).ExtractErr() 966 if err != nil { 967 t.Fatalf("Unable to detach interface %s from server %s", portID, serverID) 968 } 969 970 t.Logf("Detached interface %s from server %s", portID, serverID) 971} 972 973// DisassociateFloatingIP will disassociate a floating IP from an instance. A 974// fatal error will occur if the floating IP failed to disassociate. This works 975// best when using it as a deferred function. 976func DisassociateFloatingIP(t *testing.T, client *gophercloud.ServiceClient, floatingIP *floatingips.FloatingIP, server *servers.Server) { 977 disassociateOpts := floatingips.DisassociateOpts{ 978 FloatingIP: floatingIP.IP, 979 } 980 981 err := floatingips.DisassociateInstance(client, server.ID, disassociateOpts).ExtractErr() 982 if err != nil { 983 t.Fatalf("Unable to disassociate floating IP %s from server %s: %v", floatingIP.IP, server.ID, err) 984 } 985 986 t.Logf("Disassociated floating IP %s from server %s", floatingIP.IP, server.ID) 987} 988 989// GetNetworkIDFromOSNetworks will return the network ID from a specified network 990// UUID using the os-networks API extension. An error will be returned if the 991// network could not be retrieved. 992func GetNetworkIDFromOSNetworks(t *testing.T, client *gophercloud.ServiceClient, networkName string) (string, error) { 993 allPages, err := networks.List(client).AllPages() 994 if err != nil { 995 t.Fatalf("Unable to list networks: %v", err) 996 } 997 998 networkList, err := networks.ExtractNetworks(allPages) 999 if err != nil { 1000 t.Fatalf("Unable to list networks: %v", err) 1001 } 1002 1003 networkID := "" 1004 for _, network := range networkList { 1005 t.Logf("Network: %v", network) 1006 if network.Label == networkName { 1007 networkID = network.ID 1008 } 1009 } 1010 1011 t.Logf("Found network ID for %s: %s", networkName, networkID) 1012 1013 return networkID, nil 1014} 1015 1016// GetNetworkIDFromTenantNetworks will return the network UUID for a given 1017// network name using the os-tenant-networks API extension. An error will be 1018// returned if the network could not be retrieved. 1019func GetNetworkIDFromTenantNetworks(t *testing.T, client *gophercloud.ServiceClient, networkName string) (string, error) { 1020 allPages, err := tenantnetworks.List(client).AllPages() 1021 if err != nil { 1022 return "", err 1023 } 1024 1025 allTenantNetworks, err := tenantnetworks.ExtractNetworks(allPages) 1026 if err != nil { 1027 return "", err 1028 } 1029 1030 for _, network := range allTenantNetworks { 1031 if network.Name == networkName { 1032 return network.ID, nil 1033 } 1034 } 1035 1036 return "", fmt.Errorf("Failed to obtain network ID for network %s", networkName) 1037} 1038 1039// GetNetworkIDFromNetworks will return the network UUID for a given network 1040// name using either the os-tenant-networks API extension or Neutron API. 1041// An error will be returned if the network could not be retrieved. 1042func GetNetworkIDFromNetworks(t *testing.T, client *gophercloud.ServiceClient, networkName string) (string, error) { 1043 allPages, err := tenantnetworks.List(client).AllPages() 1044 if err == nil { 1045 allTenantNetworks, err := tenantnetworks.ExtractNetworks(allPages) 1046 if err != nil { 1047 return "", err 1048 } 1049 1050 for _, network := range allTenantNetworks { 1051 if network.Name == networkName { 1052 return network.ID, nil 1053 } 1054 } 1055 } 1056 1057 networkClient, err := clients.NewNetworkV2Client() 1058 th.AssertNoErr(t, err) 1059 1060 allPages2, err := neutron.List(networkClient, nil).AllPages() 1061 th.AssertNoErr(t, err) 1062 1063 allNetworks, err := neutron.ExtractNetworks(allPages2) 1064 th.AssertNoErr(t, err) 1065 1066 for _, network := range allNetworks { 1067 if network.Name == networkName { 1068 return network.ID, nil 1069 } 1070 } 1071 1072 return "", fmt.Errorf("Failed to obtain network ID for network %s", networkName) 1073} 1074 1075// ImportPublicKey will create a KeyPair with a random name and a specified 1076// public key. An error will be returned if the keypair failed to be created. 1077func ImportPublicKey(t *testing.T, client *gophercloud.ServiceClient, publicKey string) (*keypairs.KeyPair, error) { 1078 keyPairName := tools.RandomString("keypair_", 5) 1079 1080 t.Logf("Attempting to create keypair: %s", keyPairName) 1081 createOpts := keypairs.CreateOpts{ 1082 Name: keyPairName, 1083 PublicKey: publicKey, 1084 } 1085 keyPair, err := keypairs.Create(client, createOpts).Extract() 1086 if err != nil { 1087 return keyPair, err 1088 } 1089 1090 t.Logf("Created keypair: %s", keyPairName) 1091 1092 th.AssertEquals(t, keyPair.Name, keyPairName) 1093 th.AssertEquals(t, keyPair.PublicKey, publicKey) 1094 1095 return keyPair, nil 1096} 1097 1098// ResizeServer performs a resize action on an instance. An error will be 1099// returned if the instance failed to resize. 1100// The new flavor that the instance will be resized to is specified in OS_FLAVOR_ID_RESIZE. 1101func ResizeServer(t *testing.T, client *gophercloud.ServiceClient, server *servers.Server) error { 1102 choices, err := clients.AcceptanceTestChoicesFromEnv() 1103 if err != nil { 1104 t.Fatal(err) 1105 } 1106 1107 opts := &servers.ResizeOpts{ 1108 FlavorRef: choices.FlavorIDResize, 1109 } 1110 if res := servers.Resize(client, server.ID, opts); res.Err != nil { 1111 return res.Err 1112 } 1113 1114 if err := WaitForComputeStatus(client, server, "VERIFY_RESIZE"); err != nil { 1115 return err 1116 } 1117 1118 return nil 1119} 1120 1121// WaitForComputeStatus will poll an instance's status until it either matches 1122// the specified status or the status becomes ERROR. 1123func WaitForComputeStatus(client *gophercloud.ServiceClient, server *servers.Server, status string) error { 1124 return tools.WaitFor(func() (bool, error) { 1125 latest, err := servers.Get(client, server.ID).Extract() 1126 if err != nil { 1127 return false, err 1128 } 1129 1130 if latest.Status == status { 1131 // Success! 1132 return true, nil 1133 } 1134 1135 if latest.Status == "ERROR" { 1136 return false, fmt.Errorf("Instance in ERROR state") 1137 } 1138 1139 return false, nil 1140 }) 1141} 1142 1143//Convenience method to fill an QuotaSet-UpdateOpts-struct from a QuotaSet-struct 1144func FillUpdateOptsFromQuotaSet(src quotasets.QuotaSet, dest *quotasets.UpdateOpts) { 1145 dest.FixedIPs = &src.FixedIPs 1146 dest.FloatingIPs = &src.FloatingIPs 1147 dest.InjectedFileContentBytes = &src.InjectedFileContentBytes 1148 dest.InjectedFilePathBytes = &src.InjectedFilePathBytes 1149 dest.InjectedFiles = &src.InjectedFiles 1150 dest.KeyPairs = &src.KeyPairs 1151 dest.RAM = &src.RAM 1152 dest.SecurityGroupRules = &src.SecurityGroupRules 1153 dest.SecurityGroups = &src.SecurityGroups 1154 dest.Cores = &src.Cores 1155 dest.Instances = &src.Instances 1156 dest.ServerGroups = &src.ServerGroups 1157 dest.ServerGroupMembers = &src.ServerGroupMembers 1158 dest.MetadataItems = &src.MetadataItems 1159} 1160 1161// RescueServer will place the specified server into rescue mode. 1162func RescueServer(t *testing.T, client *gophercloud.ServiceClient, server *servers.Server) error { 1163 t.Logf("Attempting to put server %s into rescue mode", server.ID) 1164 _, err := rescueunrescue.Rescue(client, server.ID, rescueunrescue.RescueOpts{}).Extract() 1165 if err != nil { 1166 return err 1167 } 1168 1169 if err := WaitForComputeStatus(client, server, "RESCUE"); err != nil { 1170 return err 1171 } 1172 1173 return nil 1174} 1175 1176// UnrescueServer will return server from rescue mode. 1177func UnrescueServer(t *testing.T, client *gophercloud.ServiceClient, server *servers.Server) error { 1178 t.Logf("Attempting to return server %s from rescue mode", server.ID) 1179 if err := rescueunrescue.Unrescue(client, server.ID).ExtractErr(); err != nil { 1180 return err 1181 } 1182 1183 if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil { 1184 return err 1185 } 1186 1187 return nil 1188} 1189 1190// CreateRemoteConsole will create a remote noVNC console for the specified server. 1191func CreateRemoteConsole(t *testing.T, client *gophercloud.ServiceClient, serverID string) (*remoteconsoles.RemoteConsole, error) { 1192 createOpts := remoteconsoles.CreateOpts{ 1193 Protocol: remoteconsoles.ConsoleProtocolVNC, 1194 Type: remoteconsoles.ConsoleTypeNoVNC, 1195 } 1196 1197 t.Logf("Attempting to create a %s console for the server %s", createOpts.Type, serverID) 1198 remoteConsole, err := remoteconsoles.Create(client, serverID, createOpts).Extract() 1199 if err != nil { 1200 return nil, err 1201 } 1202 1203 t.Logf("Successfully created console: %s", remoteConsole.URL) 1204 return remoteConsole, nil 1205} 1206 1207// CreateNoNetworkServer creates a basic instance with a randomly generated name. 1208// The flavor of the instance will be the value of the OS_FLAVOR_ID environment variable. 1209// The image will be the value of the OS_IMAGE_ID environment variable. 1210// The instance will be launched without network interfaces attached. 1211// An error will be returned if the instance was unable to be created. 1212func CreateServerNoNetwork(t *testing.T, client *gophercloud.ServiceClient) (*servers.Server, error) { 1213 choices, err := clients.AcceptanceTestChoicesFromEnv() 1214 if err != nil { 1215 t.Fatal(err) 1216 } 1217 1218 name := tools.RandomString("ACPTTEST", 16) 1219 t.Logf("Attempting to create server: %s", name) 1220 1221 pwd := tools.MakeNewPassword("") 1222 1223 server, err := servers.Create(client, servers.CreateOpts{ 1224 Name: name, 1225 FlavorRef: choices.FlavorID, 1226 ImageRef: choices.ImageID, 1227 AdminPass: pwd, 1228 Networks: "none", 1229 Metadata: map[string]string{ 1230 "abc": "def", 1231 }, 1232 Personality: servers.Personality{ 1233 &servers.File{ 1234 Path: "/etc/test", 1235 Contents: []byte("hello world"), 1236 }, 1237 }, 1238 }).Extract() 1239 if err != nil { 1240 return server, err 1241 } 1242 1243 if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil { 1244 return nil, err 1245 } 1246 1247 newServer, err := servers.Get(client, server.ID).Extract() 1248 if err != nil { 1249 return nil, err 1250 } 1251 1252 th.AssertEquals(t, newServer.Name, name) 1253 th.AssertEquals(t, newServer.Flavor["id"], choices.FlavorID) 1254 th.AssertEquals(t, newServer.Image["id"], choices.ImageID) 1255 1256 return newServer, nil 1257} 1258