Joe Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 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. |
| 3 | package v2 |
| 4 | |
| 5 | import ( |
| 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/v1/volumes" |
| 15 | "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/bootfromvolume" |
| 16 | dsr "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/defsecrules" |
| 17 | "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips" |
| 18 | "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs" |
| 19 | "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/networks" |
| 20 | "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/quotasets" |
| 21 | "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/schedulerhints" |
| 22 | "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups" |
| 23 | "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups" |
| 24 | "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/tenantnetworks" |
| 25 | "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach" |
| 26 | "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors" |
| 27 | "github.com/gophercloud/gophercloud/openstack/compute/v2/images" |
| 28 | "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" |
| 29 | |
| 30 | "golang.org/x/crypto/ssh" |
| 31 | ) |
| 32 | |
| 33 | // AssociateFloatingIP will associate a floating IP with an instance. An error |
| 34 | // will be returned if the floating IP was unable to be associated. |
| 35 | func AssociateFloatingIP(t *testing.T, client *gophercloud.ServiceClient, floatingIP *floatingips.FloatingIP, server *servers.Server) error { |
| 36 | associateOpts := floatingips.AssociateOpts{ |
| 37 | FloatingIP: floatingIP.IP, |
| 38 | } |
| 39 | |
| 40 | t.Logf("Attempting to associate floating IP %s to instance %s", floatingIP.IP, server.ID) |
| 41 | err := floatingips.AssociateInstance(client, server.ID, associateOpts).ExtractErr() |
| 42 | if err != nil { |
| 43 | return err |
| 44 | } |
| 45 | |
| 46 | return nil |
| 47 | } |
| 48 | |
| 49 | // AssociateFloatingIPWithFixedIP will associate a floating IP with an |
| 50 | // instance's specific fixed IP. An error will be returend if the floating IP |
| 51 | // was unable to be associated. |
| 52 | func AssociateFloatingIPWithFixedIP(t *testing.T, client *gophercloud.ServiceClient, floatingIP *floatingips.FloatingIP, server *servers.Server, fixedIP string) error { |
| 53 | associateOpts := floatingips.AssociateOpts{ |
| 54 | FloatingIP: floatingIP.IP, |
| 55 | FixedIP: fixedIP, |
| 56 | } |
| 57 | |
| 58 | t.Logf("Attempting to associate floating IP %s to fixed IP %s on instance %s", floatingIP.IP, fixedIP, server.ID) |
| 59 | err := floatingips.AssociateInstance(client, server.ID, associateOpts).ExtractErr() |
| 60 | if err != nil { |
| 61 | return err |
| 62 | } |
| 63 | |
| 64 | return nil |
| 65 | } |
| 66 | |
| 67 | // CreateBootableVolumeServer works like CreateServer but is configured with |
| 68 | // one or more block devices defined by passing in []bootfromvolume.BlockDevice. |
| 69 | // An error will be returned if a server was unable to be created. |
| 70 | func CreateBootableVolumeServer(t *testing.T, client *gophercloud.ServiceClient, blockDevices []bootfromvolume.BlockDevice, choices *clients.AcceptanceTestChoices) (*servers.Server, error) { |
| 71 | if testing.Short() { |
| 72 | t.Skip("Skipping test that requires server creation in short mode.") |
| 73 | } |
| 74 | |
| 75 | var server *servers.Server |
| 76 | |
| 77 | networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName) |
| 78 | if err != nil { |
| 79 | return server, err |
| 80 | } |
| 81 | |
| 82 | name := tools.RandomString("ACPTTEST", 16) |
| 83 | t.Logf("Attempting to create bootable volume server: %s", name) |
| 84 | |
| 85 | serverCreateOpts := servers.CreateOpts{ |
| 86 | Name: name, |
| 87 | FlavorRef: choices.FlavorID, |
Joe Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 88 | Networks: []servers.Network{ |
| 89 | servers.Network{UUID: networkID}, |
| 90 | }, |
| 91 | } |
| 92 | |
Joe Topjian | f1f4041 | 2016-10-13 17:42:25 -0600 | [diff] [blame^] | 93 | if blockDevices[0].SourceType == bootfromvolume.SourceImage && blockDevices[0].DestinationType == bootfromvolume.DestinationLocal { |
| 94 | serverCreateOpts.ImageRef = blockDevices[0].UUID |
| 95 | } |
| 96 | |
Joe Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 97 | server, err = bootfromvolume.Create(client, bootfromvolume.CreateOptsExt{ |
| 98 | serverCreateOpts, |
| 99 | blockDevices, |
| 100 | }).Extract() |
| 101 | |
| 102 | if err != nil { |
| 103 | return server, err |
| 104 | } |
| 105 | |
Joe Topjian | 50cdddf | 2016-09-16 10:56:09 -0600 | [diff] [blame] | 106 | if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil { |
| 107 | return server, err |
| 108 | } |
| 109 | |
| 110 | newServer, err := servers.Get(client, server.ID).Extract() |
| 111 | |
| 112 | return newServer, nil |
Joe Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 113 | } |
| 114 | |
| 115 | // CreateDefaultRule will create a default security group rule with a |
| 116 | // random port range between 80 and 90. An error will be returned if |
| 117 | // a default rule was unable to be created. |
| 118 | func CreateDefaultRule(t *testing.T, client *gophercloud.ServiceClient) (dsr.DefaultRule, error) { |
| 119 | createOpts := dsr.CreateOpts{ |
| 120 | FromPort: tools.RandomInt(80, 89), |
| 121 | ToPort: tools.RandomInt(90, 99), |
| 122 | IPProtocol: "TCP", |
| 123 | CIDR: "0.0.0.0/0", |
| 124 | } |
| 125 | |
| 126 | defaultRule, err := dsr.Create(client, createOpts).Extract() |
| 127 | if err != nil { |
| 128 | return *defaultRule, err |
| 129 | } |
| 130 | |
| 131 | t.Logf("Created default rule: %s", defaultRule.ID) |
| 132 | |
| 133 | return *defaultRule, nil |
| 134 | } |
| 135 | |
| 136 | // CreateFloatingIP will allocate a floating IP. |
| 137 | // An error will be returend if one was unable to be allocated. |
| 138 | func CreateFloatingIP(t *testing.T, client *gophercloud.ServiceClient, choices *clients.AcceptanceTestChoices) (*floatingips.FloatingIP, error) { |
| 139 | createOpts := floatingips.CreateOpts{ |
| 140 | Pool: choices.FloatingIPPoolName, |
| 141 | } |
| 142 | floatingIP, err := floatingips.Create(client, createOpts).Extract() |
| 143 | if err != nil { |
| 144 | return floatingIP, err |
| 145 | } |
| 146 | |
| 147 | t.Logf("Created floating IP: %s", floatingIP.ID) |
| 148 | return floatingIP, nil |
| 149 | } |
| 150 | |
| 151 | func createKey() (string, error) { |
| 152 | privateKey, err := rsa.GenerateKey(rand.Reader, 2048) |
| 153 | if err != nil { |
| 154 | return "", err |
| 155 | } |
| 156 | |
| 157 | publicKey := privateKey.PublicKey |
| 158 | pub, err := ssh.NewPublicKey(&publicKey) |
| 159 | if err != nil { |
| 160 | return "", err |
| 161 | } |
| 162 | |
| 163 | pubBytes := ssh.MarshalAuthorizedKey(pub) |
| 164 | pk := string(pubBytes) |
| 165 | return pk, nil |
| 166 | } |
| 167 | |
| 168 | // CreateKeyPair will create a KeyPair with a random name. An error will occur |
| 169 | // if the keypair failed to be created. An error will be returned if the |
| 170 | // keypair was unable to be created. |
| 171 | func CreateKeyPair(t *testing.T, client *gophercloud.ServiceClient) (*keypairs.KeyPair, error) { |
| 172 | keyPairName := tools.RandomString("keypair_", 5) |
| 173 | |
| 174 | t.Logf("Attempting to create keypair: %s", keyPairName) |
| 175 | createOpts := keypairs.CreateOpts{ |
| 176 | Name: keyPairName, |
| 177 | } |
| 178 | keyPair, err := keypairs.Create(client, createOpts).Extract() |
| 179 | if err != nil { |
| 180 | return keyPair, err |
| 181 | } |
| 182 | |
| 183 | t.Logf("Created keypair: %s", keyPairName) |
| 184 | return keyPair, nil |
| 185 | } |
| 186 | |
Joe Topjian | 50cdddf | 2016-09-16 10:56:09 -0600 | [diff] [blame] | 187 | // CreateMultiEphemeralServer works like CreateServer but is configured with |
| 188 | // one or more block devices defined by passing in []bootfromvolume.BlockDevice. |
| 189 | // These block devices act like block devices when booting from a volume but |
| 190 | // are actually local ephemeral disks. |
| 191 | // An error will be returned if a server was unable to be created. |
| 192 | func CreateMultiEphemeralServer(t *testing.T, client *gophercloud.ServiceClient, blockDevices []bootfromvolume.BlockDevice, choices *clients.AcceptanceTestChoices) (*servers.Server, error) { |
| 193 | if testing.Short() { |
| 194 | t.Skip("Skipping test that requires server creation in short mode.") |
| 195 | } |
| 196 | |
| 197 | var server *servers.Server |
| 198 | |
| 199 | networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName) |
| 200 | if err != nil { |
| 201 | return server, err |
| 202 | } |
| 203 | |
| 204 | name := tools.RandomString("ACPTTEST", 16) |
| 205 | t.Logf("Attempting to create bootable volume server: %s", name) |
| 206 | |
| 207 | serverCreateOpts := servers.CreateOpts{ |
| 208 | Name: name, |
| 209 | FlavorRef: choices.FlavorID, |
| 210 | ImageRef: choices.ImageID, |
| 211 | Networks: []servers.Network{ |
| 212 | servers.Network{UUID: networkID}, |
| 213 | }, |
| 214 | } |
| 215 | |
| 216 | server, err = bootfromvolume.Create(client, bootfromvolume.CreateOptsExt{ |
| 217 | serverCreateOpts, |
| 218 | blockDevices, |
| 219 | }).Extract() |
| 220 | |
| 221 | if err != nil { |
| 222 | return server, err |
| 223 | } |
| 224 | |
| 225 | if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil { |
| 226 | return server, err |
| 227 | } |
| 228 | |
| 229 | newServer, err := servers.Get(client, server.ID).Extract() |
| 230 | |
| 231 | return newServer, nil |
| 232 | } |
| 233 | |
Joe Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 234 | // CreateSecurityGroup will create a security group with a random name. |
| 235 | // An error will be returned if one was failed to be created. |
| 236 | func CreateSecurityGroup(t *testing.T, client *gophercloud.ServiceClient) (secgroups.SecurityGroup, error) { |
| 237 | createOpts := secgroups.CreateOpts{ |
| 238 | Name: tools.RandomString("secgroup_", 5), |
| 239 | Description: "something", |
| 240 | } |
| 241 | |
| 242 | securityGroup, err := secgroups.Create(client, createOpts).Extract() |
| 243 | if err != nil { |
| 244 | return *securityGroup, err |
| 245 | } |
| 246 | |
| 247 | t.Logf("Created security group: %s", securityGroup.ID) |
| 248 | return *securityGroup, nil |
| 249 | } |
| 250 | |
| 251 | // CreateSecurityGroupRule will create a security group rule with a random name |
| 252 | // and a random TCP port range between port 80 and 99. An error will be |
| 253 | // returned if the rule failed to be created. |
| 254 | func CreateSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, securityGroupID string) (secgroups.Rule, error) { |
| 255 | createOpts := secgroups.CreateRuleOpts{ |
| 256 | ParentGroupID: securityGroupID, |
| 257 | FromPort: tools.RandomInt(80, 89), |
| 258 | ToPort: tools.RandomInt(90, 99), |
| 259 | IPProtocol: "TCP", |
| 260 | CIDR: "0.0.0.0/0", |
| 261 | } |
| 262 | |
| 263 | rule, err := secgroups.CreateRule(client, createOpts).Extract() |
| 264 | if err != nil { |
| 265 | return *rule, err |
| 266 | } |
| 267 | |
| 268 | t.Logf("Created security group rule: %s", rule.ID) |
| 269 | return *rule, nil |
| 270 | } |
| 271 | |
| 272 | // CreateServer creates a basic instance with a randomly generated name. |
| 273 | // The flavor of the instance will be the value of the OS_FLAVOR_ID environment variable. |
| 274 | // The image will be the value of the OS_IMAGE_ID environment variable. |
| 275 | // The instance will be launched on the network specified in OS_NETWORK_NAME. |
| 276 | // An error will be returned if the instance was unable to be created. |
| 277 | func CreateServer(t *testing.T, client *gophercloud.ServiceClient, choices *clients.AcceptanceTestChoices) (*servers.Server, error) { |
| 278 | if testing.Short() { |
| 279 | t.Skip("Skipping test that requires server creation in short mode.") |
| 280 | } |
| 281 | |
| 282 | var server *servers.Server |
| 283 | |
| 284 | networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName) |
| 285 | if err != nil { |
| 286 | return server, err |
| 287 | } |
| 288 | |
| 289 | name := tools.RandomString("ACPTTEST", 16) |
| 290 | t.Logf("Attempting to create server: %s", name) |
| 291 | |
| 292 | pwd := tools.MakeNewPassword("") |
| 293 | |
| 294 | server, err = servers.Create(client, servers.CreateOpts{ |
| 295 | Name: name, |
| 296 | FlavorRef: choices.FlavorID, |
| 297 | ImageRef: choices.ImageID, |
| 298 | AdminPass: pwd, |
| 299 | Networks: []servers.Network{ |
| 300 | servers.Network{UUID: networkID}, |
| 301 | }, |
Joe Topjian | f464c96 | 2016-09-12 08:02:43 -0600 | [diff] [blame] | 302 | Metadata: map[string]string{ |
| 303 | "abc": "def", |
| 304 | }, |
Joe Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 305 | Personality: servers.Personality{ |
| 306 | &servers.File{ |
| 307 | Path: "/etc/test", |
| 308 | Contents: []byte("hello world"), |
| 309 | }, |
| 310 | }, |
| 311 | }).Extract() |
| 312 | if err != nil { |
| 313 | return server, err |
| 314 | } |
| 315 | |
Joe Topjian | 50cdddf | 2016-09-16 10:56:09 -0600 | [diff] [blame] | 316 | if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil { |
| 317 | return server, err |
| 318 | } |
| 319 | |
| 320 | return server, nil |
| 321 | } |
| 322 | |
| 323 | // CreateServerWithoutImageRef creates a basic instance with a randomly generated name. |
| 324 | // The flavor of the instance will be the value of the OS_FLAVOR_ID environment variable. |
| 325 | // The image is intentionally missing to trigger an error. |
| 326 | // The instance will be launched on the network specified in OS_NETWORK_NAME. |
| 327 | // An error will be returned if the instance was unable to be created. |
| 328 | func CreateServerWithoutImageRef(t *testing.T, client *gophercloud.ServiceClient, choices *clients.AcceptanceTestChoices) (*servers.Server, error) { |
| 329 | if testing.Short() { |
| 330 | t.Skip("Skipping test that requires server creation in short mode.") |
| 331 | } |
| 332 | |
| 333 | var server *servers.Server |
| 334 | |
| 335 | networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName) |
| 336 | if err != nil { |
| 337 | return server, err |
| 338 | } |
| 339 | |
| 340 | name := tools.RandomString("ACPTTEST", 16) |
| 341 | t.Logf("Attempting to create server: %s", name) |
| 342 | |
| 343 | pwd := tools.MakeNewPassword("") |
| 344 | |
| 345 | server, err = servers.Create(client, servers.CreateOpts{ |
| 346 | Name: name, |
| 347 | FlavorRef: choices.FlavorID, |
| 348 | AdminPass: pwd, |
| 349 | Networks: []servers.Network{ |
| 350 | servers.Network{UUID: networkID}, |
| 351 | }, |
| 352 | Personality: servers.Personality{ |
| 353 | &servers.File{ |
| 354 | Path: "/etc/test", |
| 355 | Contents: []byte("hello world"), |
| 356 | }, |
| 357 | }, |
| 358 | }).Extract() |
| 359 | if err != nil { |
| 360 | return server, err |
| 361 | } |
| 362 | |
| 363 | if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil { |
Joe Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 364 | return server, err |
| 365 | } |
| 366 | |
| 367 | return server, nil |
| 368 | } |
| 369 | |
| 370 | // CreateServerGroup will create a server with a random name. An error will be |
| 371 | // returned if the server group failed to be created. |
| 372 | func CreateServerGroup(t *testing.T, client *gophercloud.ServiceClient, policy string) (*servergroups.ServerGroup, error) { |
| 373 | sg, err := servergroups.Create(client, &servergroups.CreateOpts{ |
| 374 | Name: "test", |
| 375 | Policies: []string{policy}, |
| 376 | }).Extract() |
| 377 | |
| 378 | if err != nil { |
| 379 | return sg, err |
| 380 | } |
| 381 | |
| 382 | return sg, nil |
| 383 | } |
| 384 | |
| 385 | // CreateServerInServerGroup works like CreateServer but places the instance in |
| 386 | // a specified Server Group. |
| 387 | func CreateServerInServerGroup(t *testing.T, client *gophercloud.ServiceClient, choices *clients.AcceptanceTestChoices, serverGroup *servergroups.ServerGroup) (*servers.Server, error) { |
| 388 | if testing.Short() { |
| 389 | t.Skip("Skipping test that requires server creation in short mode.") |
| 390 | } |
| 391 | |
| 392 | var server *servers.Server |
| 393 | |
| 394 | networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName) |
| 395 | if err != nil { |
| 396 | return server, err |
| 397 | } |
| 398 | |
| 399 | name := tools.RandomString("ACPTTEST", 16) |
| 400 | t.Logf("Attempting to create server: %s", name) |
| 401 | |
| 402 | pwd := tools.MakeNewPassword("") |
| 403 | |
| 404 | serverCreateOpts := servers.CreateOpts{ |
| 405 | Name: name, |
| 406 | FlavorRef: choices.FlavorID, |
| 407 | ImageRef: choices.ImageID, |
| 408 | AdminPass: pwd, |
| 409 | Networks: []servers.Network{ |
| 410 | servers.Network{UUID: networkID}, |
| 411 | }, |
| 412 | } |
| 413 | |
| 414 | schedulerHintsOpts := schedulerhints.CreateOptsExt{ |
| 415 | serverCreateOpts, |
| 416 | schedulerhints.SchedulerHints{ |
| 417 | Group: serverGroup.ID, |
| 418 | }, |
| 419 | } |
| 420 | server, err = servers.Create(client, schedulerHintsOpts).Extract() |
| 421 | if err != nil { |
| 422 | return server, err |
| 423 | } |
| 424 | |
| 425 | return server, nil |
| 426 | } |
| 427 | |
| 428 | // CreateServerWithPublicKey works the same as CreateServer, but additionally |
| 429 | // configures the server with a specified Key Pair name. |
| 430 | func CreateServerWithPublicKey(t *testing.T, client *gophercloud.ServiceClient, choices *clients.AcceptanceTestChoices, keyPairName string) (*servers.Server, error) { |
| 431 | if testing.Short() { |
| 432 | t.Skip("Skipping test that requires server creation in short mode.") |
| 433 | } |
| 434 | |
| 435 | var server *servers.Server |
| 436 | |
| 437 | networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName) |
| 438 | if err != nil { |
| 439 | return server, err |
| 440 | } |
| 441 | |
| 442 | name := tools.RandomString("ACPTTEST", 16) |
| 443 | t.Logf("Attempting to create server: %s", name) |
| 444 | |
| 445 | serverCreateOpts := servers.CreateOpts{ |
| 446 | Name: name, |
| 447 | FlavorRef: choices.FlavorID, |
| 448 | ImageRef: choices.ImageID, |
| 449 | Networks: []servers.Network{ |
| 450 | servers.Network{UUID: networkID}, |
| 451 | }, |
| 452 | } |
| 453 | |
| 454 | server, err = servers.Create(client, keypairs.CreateOptsExt{ |
| 455 | serverCreateOpts, |
| 456 | keyPairName, |
| 457 | }).Extract() |
| 458 | if err != nil { |
| 459 | return server, err |
| 460 | } |
| 461 | |
Joe Topjian | 50cdddf | 2016-09-16 10:56:09 -0600 | [diff] [blame] | 462 | if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil { |
Joe Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 463 | return server, err |
| 464 | } |
| 465 | |
| 466 | return server, nil |
| 467 | } |
| 468 | |
Gleb | 37b56e8 | 2016-09-06 19:07:58 +0300 | [diff] [blame] | 469 | // CreateVolumeAttachment will attach a volume to a server. An error will be |
Joe Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 470 | // returned if the volume failed to attach. |
| 471 | func CreateVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, blockClient *gophercloud.ServiceClient, server *servers.Server, volume *volumes.Volume) (*volumeattach.VolumeAttachment, error) { |
| 472 | volumeAttachOptions := volumeattach.CreateOpts{ |
| 473 | VolumeID: volume.ID, |
| 474 | } |
| 475 | |
| 476 | t.Logf("Attempting to attach volume %s to server %s", volume.ID, server.ID) |
| 477 | volumeAttachment, err := volumeattach.Create(client, server.ID, volumeAttachOptions).Extract() |
| 478 | if err != nil { |
| 479 | return volumeAttachment, err |
| 480 | } |
| 481 | |
Joe Topjian | 50cdddf | 2016-09-16 10:56:09 -0600 | [diff] [blame] | 482 | if err := volumes.WaitForStatus(blockClient, volume.ID, "in-use", 60); err != nil { |
Joe Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 483 | return volumeAttachment, err |
| 484 | } |
| 485 | |
| 486 | return volumeAttachment, nil |
| 487 | } |
| 488 | |
| 489 | // DeleteDefaultRule deletes a default security group rule. |
| 490 | // A fatal error will occur if the rule failed to delete. This works best when |
| 491 | // using it as a deferred function. |
| 492 | func DeleteDefaultRule(t *testing.T, client *gophercloud.ServiceClient, defaultRule dsr.DefaultRule) { |
| 493 | err := dsr.Delete(client, defaultRule.ID).ExtractErr() |
| 494 | if err != nil { |
| 495 | t.Fatalf("Unable to delete default rule %s: %v", defaultRule.ID, err) |
| 496 | } |
| 497 | |
| 498 | t.Logf("Deleted default rule: %s", defaultRule.ID) |
| 499 | } |
| 500 | |
| 501 | // DeleteFloatingIP will de-allocate a floating IP. A fatal error will occur if |
| 502 | // the floating IP failed to de-allocate. This works best when using it as a |
| 503 | // deferred function. |
| 504 | func DeleteFloatingIP(t *testing.T, client *gophercloud.ServiceClient, floatingIP *floatingips.FloatingIP) { |
| 505 | err := floatingips.Delete(client, floatingIP.ID).ExtractErr() |
| 506 | if err != nil { |
| 507 | t.Fatalf("Unable to delete floating IP %s: %v", floatingIP.ID, err) |
| 508 | } |
| 509 | |
| 510 | t.Logf("Deleted floating IP: %s", floatingIP.ID) |
| 511 | } |
| 512 | |
| 513 | // DeleteKeyPair will delete a specified keypair. A fatal error will occur if |
| 514 | // the keypair failed to be deleted. This works best when used as a deferred |
| 515 | // function. |
| 516 | func DeleteKeyPair(t *testing.T, client *gophercloud.ServiceClient, keyPair *keypairs.KeyPair) { |
| 517 | err := keypairs.Delete(client, keyPair.Name).ExtractErr() |
| 518 | if err != nil { |
| 519 | t.Fatalf("Unable to delete keypair %s: %v", keyPair.Name, err) |
| 520 | } |
| 521 | |
| 522 | t.Logf("Deleted keypair: %s", keyPair.Name) |
| 523 | } |
| 524 | |
| 525 | // DeleteSecurityGroup will delete a security group. A fatal error will occur |
| 526 | // if the group failed to be deleted. This works best as a deferred function. |
| 527 | func DeleteSecurityGroup(t *testing.T, client *gophercloud.ServiceClient, securityGroup secgroups.SecurityGroup) { |
| 528 | err := secgroups.Delete(client, securityGroup.ID).ExtractErr() |
| 529 | if err != nil { |
| 530 | t.Fatalf("Unable to delete security group %s: %s", securityGroup.ID, err) |
| 531 | } |
| 532 | |
| 533 | t.Logf("Deleted security group: %s", securityGroup.ID) |
| 534 | } |
| 535 | |
| 536 | // DeleteSecurityGroupRule will delete a security group rule. A fatal error |
| 537 | // will occur if the rule failed to be deleted. This works best when used |
| 538 | // as a deferred function. |
| 539 | func DeleteSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, rule secgroups.Rule) { |
| 540 | err := secgroups.DeleteRule(client, rule.ID).ExtractErr() |
| 541 | if err != nil { |
| 542 | t.Fatalf("Unable to delete rule: %v", err) |
| 543 | } |
| 544 | |
| 545 | t.Logf("Deleted security group rule: %s", rule.ID) |
| 546 | } |
| 547 | |
| 548 | // DeleteServer deletes an instance via its UUID. |
| 549 | // A fatal error will occur if the instance failed to be destroyed. This works |
| 550 | // best when using it as a deferred function. |
| 551 | func DeleteServer(t *testing.T, client *gophercloud.ServiceClient, server *servers.Server) { |
| 552 | err := servers.Delete(client, server.ID).ExtractErr() |
| 553 | if err != nil { |
| 554 | t.Fatalf("Unable to delete server %s: %s", server.ID, err) |
| 555 | } |
| 556 | |
| 557 | t.Logf("Deleted server: %s", server.ID) |
| 558 | } |
| 559 | |
| 560 | // DeleteServerGroup will delete a server group. A fatal error will occur if |
| 561 | // the server group failed to be deleted. This works best when used as a |
| 562 | // deferred function. |
| 563 | func DeleteServerGroup(t *testing.T, client *gophercloud.ServiceClient, serverGroup *servergroups.ServerGroup) { |
| 564 | err := servergroups.Delete(client, serverGroup.ID).ExtractErr() |
| 565 | if err != nil { |
| 566 | t.Fatalf("Unable to delete server group %s: %v", serverGroup.ID, err) |
| 567 | } |
| 568 | |
| 569 | t.Logf("Deleted server group %s", serverGroup.ID) |
| 570 | } |
| 571 | |
| 572 | // DeleteVolumeAttachment will disconnect a volume from an instance. A fatal |
| 573 | // error will occur if the volume failed to detach. This works best when used |
Gleb | 37b56e8 | 2016-09-06 19:07:58 +0300 | [diff] [blame] | 574 | // as a deferred function. |
Joe Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 575 | func DeleteVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, blockClient *gophercloud.ServiceClient, server *servers.Server, volumeAttachment *volumeattach.VolumeAttachment) { |
| 576 | |
| 577 | err := volumeattach.Delete(client, server.ID, volumeAttachment.VolumeID).ExtractErr() |
| 578 | if err != nil { |
| 579 | t.Fatalf("Unable to detach volume: %v", err) |
| 580 | } |
| 581 | |
Joe Topjian | 50cdddf | 2016-09-16 10:56:09 -0600 | [diff] [blame] | 582 | if err := volumes.WaitForStatus(blockClient, volumeAttachment.ID, "available", 60); err != nil { |
Joe Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 583 | t.Fatalf("Unable to wait for volume: %v", err) |
| 584 | } |
| 585 | t.Logf("Deleted volume: %s", volumeAttachment.VolumeID) |
| 586 | } |
| 587 | |
| 588 | // DisassociateFloatingIP will disassociate a floating IP from an instance. A |
| 589 | // fatal error will occur if the floating IP failed to disassociate. This works |
| 590 | // best when using it as a deferred function. |
| 591 | func DisassociateFloatingIP(t *testing.T, client *gophercloud.ServiceClient, floatingIP *floatingips.FloatingIP, server *servers.Server) { |
| 592 | disassociateOpts := floatingips.DisassociateOpts{ |
| 593 | FloatingIP: floatingIP.IP, |
| 594 | } |
| 595 | |
| 596 | err := floatingips.DisassociateInstance(client, server.ID, disassociateOpts).ExtractErr() |
| 597 | if err != nil { |
| 598 | t.Fatalf("Unable to disassociate floating IP %s from server %s: %v", floatingIP.IP, server.ID, err) |
| 599 | } |
| 600 | |
| 601 | t.Logf("Disassociated floating IP %s from server %s", floatingIP.IP, server.ID) |
| 602 | } |
| 603 | |
| 604 | // GetNetworkIDFromNetworks will return the network ID from a specified network |
| 605 | // UUID using the os-networks API extension. An error will be returned if the |
| 606 | // network could not be retrieved. |
| 607 | func GetNetworkIDFromNetworks(t *testing.T, client *gophercloud.ServiceClient, networkName string) (string, error) { |
| 608 | allPages, err := networks.List(client).AllPages() |
| 609 | if err != nil { |
| 610 | t.Fatalf("Unable to list networks: %v", err) |
| 611 | } |
| 612 | |
| 613 | networkList, err := networks.ExtractNetworks(allPages) |
| 614 | if err != nil { |
| 615 | t.Fatalf("Unable to list networks: %v", err) |
| 616 | } |
| 617 | |
| 618 | networkID := "" |
| 619 | for _, network := range networkList { |
| 620 | t.Logf("Network: %v", network) |
| 621 | if network.Label == networkName { |
| 622 | networkID = network.ID |
| 623 | } |
| 624 | } |
| 625 | |
| 626 | t.Logf("Found network ID for %s: %s", networkName, networkID) |
| 627 | |
| 628 | return networkID, nil |
| 629 | } |
| 630 | |
| 631 | // GetNetworkIDFromTenantNetworks will return the network UUID for a given |
| 632 | // network name using the os-tenant-networks API extension. An error will be |
| 633 | // returned if the network could not be retrieved. |
| 634 | func GetNetworkIDFromTenantNetworks(t *testing.T, client *gophercloud.ServiceClient, networkName string) (string, error) { |
| 635 | allPages, err := tenantnetworks.List(client).AllPages() |
| 636 | if err != nil { |
| 637 | return "", err |
| 638 | } |
| 639 | |
| 640 | allTenantNetworks, err := tenantnetworks.ExtractNetworks(allPages) |
| 641 | if err != nil { |
| 642 | return "", err |
| 643 | } |
| 644 | |
| 645 | for _, network := range allTenantNetworks { |
| 646 | if network.Name == networkName { |
| 647 | return network.ID, nil |
| 648 | } |
| 649 | } |
| 650 | |
| 651 | return "", fmt.Errorf("Failed to obtain network ID for network %s", networkName) |
| 652 | } |
| 653 | |
| 654 | // ImportPublicKey will create a KeyPair with a random name and a specified |
| 655 | // public key. An error will be returned if the keypair failed to be created. |
| 656 | func ImportPublicKey(t *testing.T, client *gophercloud.ServiceClient, publicKey string) (*keypairs.KeyPair, error) { |
| 657 | keyPairName := tools.RandomString("keypair_", 5) |
| 658 | |
| 659 | t.Logf("Attempting to create keypair: %s", keyPairName) |
| 660 | createOpts := keypairs.CreateOpts{ |
| 661 | Name: keyPairName, |
| 662 | PublicKey: publicKey, |
| 663 | } |
| 664 | keyPair, err := keypairs.Create(client, createOpts).Extract() |
| 665 | if err != nil { |
| 666 | return keyPair, err |
| 667 | } |
| 668 | |
| 669 | t.Logf("Created keypair: %s", keyPairName) |
| 670 | return keyPair, nil |
| 671 | } |
| 672 | |
| 673 | // ResizeServer performs a resize action on an instance. An error will be |
| 674 | // returned if the instance failed to resize. |
| 675 | // The new flavor that the instance will be resized to is specified in OS_FLAVOR_ID_RESIZE. |
| 676 | func ResizeServer(t *testing.T, client *gophercloud.ServiceClient, server *servers.Server, choices *clients.AcceptanceTestChoices) error { |
| 677 | opts := &servers.ResizeOpts{ |
| 678 | FlavorRef: choices.FlavorIDResize, |
| 679 | } |
| 680 | if res := servers.Resize(client, server.ID, opts); res.Err != nil { |
| 681 | return res.Err |
| 682 | } |
| 683 | |
| 684 | if err := WaitForComputeStatus(client, server, "VERIFY_RESIZE"); err != nil { |
| 685 | return err |
| 686 | } |
| 687 | |
| 688 | return nil |
| 689 | } |
| 690 | |
| 691 | // WaitForComputeStatus will poll an instance's status until it either matches |
| 692 | // the specified status or the status becomes ERROR. |
| 693 | func WaitForComputeStatus(client *gophercloud.ServiceClient, server *servers.Server, status string) error { |
| 694 | return tools.WaitFor(func() (bool, error) { |
| 695 | latest, err := servers.Get(client, server.ID).Extract() |
| 696 | if err != nil { |
| 697 | return false, err |
| 698 | } |
| 699 | |
| 700 | if latest.Status == status { |
| 701 | // Success! |
| 702 | return true, nil |
| 703 | } |
| 704 | |
| 705 | if latest.Status == "ERROR" { |
| 706 | return false, fmt.Errorf("Instance in ERROR state") |
| 707 | } |
| 708 | |
| 709 | return false, nil |
| 710 | }) |
| 711 | } |
| 712 | |
| 713 | // PrintServer will print an instance and all of its attributes. |
| 714 | func PrintServer(t *testing.T, server *servers.Server) { |
| 715 | t.Logf("ID: %s", server.ID) |
| 716 | t.Logf("TenantID: %s", server.TenantID) |
| 717 | t.Logf("UserID: %s", server.UserID) |
| 718 | t.Logf("Name: %s", server.Name) |
| 719 | t.Logf("Updated: %s", server.Updated) |
| 720 | t.Logf("Created: %s", server.Created) |
| 721 | t.Logf("HostID: %s", server.HostID) |
| 722 | t.Logf("Status: %s", server.Status) |
| 723 | t.Logf("Progress: %d", server.Progress) |
| 724 | t.Logf("AccessIPv4: %s", server.AccessIPv4) |
| 725 | t.Logf("AccessIPv6: %s", server.AccessIPv6) |
| 726 | t.Logf("Image: %s", server.Image) |
| 727 | t.Logf("Flavor: %s", server.Flavor) |
| 728 | t.Logf("Addresses: %#v", server.Addresses) |
| 729 | t.Logf("Metadata: %#v", server.Metadata) |
| 730 | t.Logf("Links: %#v", server.Links) |
| 731 | t.Logf("KeyName: %s", server.KeyName) |
| 732 | t.Logf("AdminPass: %s", server.AdminPass) |
| 733 | t.Logf("SecurityGroups: %#v", server.SecurityGroups) |
| 734 | } |
| 735 | |
| 736 | // PrintDefaultRule will print a default security group rule and all of its attributes. |
| 737 | func PrintDefaultRule(t *testing.T, defaultRule *dsr.DefaultRule) { |
| 738 | t.Logf("\tID: %s", defaultRule.ID) |
| 739 | t.Logf("\tFrom Port: %d", defaultRule.FromPort) |
| 740 | t.Logf("\tTo Port: %d", defaultRule.ToPort) |
| 741 | t.Logf("\tIP Protocol: %s", defaultRule.IPProtocol) |
| 742 | t.Logf("\tIP Range: %s", defaultRule.IPRange.CIDR) |
| 743 | t.Logf("\tParent Group ID: %s", defaultRule.ParentGroupID) |
| 744 | t.Logf("\tGroup Tenant ID: %s", defaultRule.Group.TenantID) |
| 745 | t.Logf("\tGroup Name: %s", defaultRule.Group.Name) |
| 746 | } |
| 747 | |
| 748 | // PrintFlavor will print a flavor and all of its attributes. |
| 749 | func PrintFlavor(t *testing.T, flavor *flavors.Flavor) { |
| 750 | t.Logf("ID: %s", flavor.ID) |
| 751 | t.Logf("Name: %s", flavor.Name) |
| 752 | t.Logf("RAM: %d", flavor.RAM) |
| 753 | t.Logf("Disk: %d", flavor.Disk) |
| 754 | t.Logf("Swap: %d", flavor.Swap) |
| 755 | t.Logf("RxTxFactor: %f", flavor.RxTxFactor) |
| 756 | } |
| 757 | |
| 758 | // PrintFloatingIP will print a floating IP and all of its attributes. |
| 759 | func PrintFloatingIP(t *testing.T, floatingIP *floatingips.FloatingIP) { |
| 760 | t.Logf("ID: %s", floatingIP.ID) |
| 761 | t.Logf("Fixed IP: %s", floatingIP.FixedIP) |
| 762 | t.Logf("Instance ID: %s", floatingIP.InstanceID) |
| 763 | t.Logf("IP: %s", floatingIP.IP) |
| 764 | t.Logf("Pool: %s", floatingIP.Pool) |
| 765 | } |
| 766 | |
| 767 | // PrintImage will print an image and all of its attributes. |
| 768 | func PrintImage(t *testing.T, image images.Image) { |
| 769 | t.Logf("ID: %s", image.ID) |
| 770 | t.Logf("Name: %s", image.Name) |
| 771 | t.Logf("MinDisk: %d", image.MinDisk) |
| 772 | t.Logf("MinRAM: %d", image.MinRAM) |
| 773 | t.Logf("Status: %s", image.Status) |
| 774 | t.Logf("Progress: %d", image.Progress) |
| 775 | t.Logf("Metadata: %#v", image.Metadata) |
| 776 | t.Logf("Created: %s", image.Created) |
| 777 | t.Logf("Updated: %s", image.Updated) |
| 778 | } |
| 779 | |
| 780 | // PrintKeyPair will print keypair and all of its attributes. |
| 781 | func PrintKeyPair(t *testing.T, keypair *keypairs.KeyPair) { |
| 782 | t.Logf("Name: %s", keypair.Name) |
| 783 | t.Logf("Fingerprint: %s", keypair.Fingerprint) |
| 784 | t.Logf("Public Key: %s", keypair.PublicKey) |
| 785 | t.Logf("Private Key: %s", keypair.PrivateKey) |
| 786 | t.Logf("UserID: %s", keypair.UserID) |
| 787 | } |
| 788 | |
| 789 | // PrintNetwork will print an os-networks based network and all of its attributes. |
| 790 | func PrintNetwork(t *testing.T, network *networks.Network) { |
| 791 | t.Logf("Bridge: %s", network.Bridge) |
| 792 | t.Logf("BridgeInterface: %s", network.BridgeInterface) |
| 793 | t.Logf("Broadcast: %s", network.Broadcast) |
| 794 | t.Logf("CIDR: %s", network.CIDR) |
| 795 | t.Logf("CIDRv6: %s", network.CIDRv6) |
| 796 | t.Logf("CreatedAt: %v", network.CreatedAt) |
| 797 | t.Logf("Deleted: %t", network.Deleted) |
| 798 | t.Logf("DeletedAt: %v", network.DeletedAt) |
| 799 | t.Logf("DHCPStart: %s", network.DHCPStart) |
| 800 | t.Logf("DNS1: %s", network.DNS1) |
| 801 | t.Logf("DNS2: %s", network.DNS2) |
| 802 | t.Logf("Gateway: %s", network.Gateway) |
| 803 | t.Logf("Gatewayv6: %s", network.Gatewayv6) |
| 804 | t.Logf("Host: %s", network.Host) |
| 805 | t.Logf("ID: %s", network.ID) |
| 806 | t.Logf("Injected: %t", network.Injected) |
| 807 | t.Logf("Label: %s", network.Label) |
| 808 | t.Logf("MultiHost: %t", network.MultiHost) |
| 809 | t.Logf("Netmask: %s", network.Netmask) |
| 810 | t.Logf("Netmaskv6: %s", network.Netmaskv6) |
| 811 | t.Logf("Priority: %d", network.Priority) |
| 812 | t.Logf("ProjectID: %s", network.ProjectID) |
| 813 | t.Logf("RXTXBase: %d", network.RXTXBase) |
| 814 | t.Logf("UpdatedAt: %v", network.UpdatedAt) |
| 815 | t.Logf("VLAN: %d", network.VLAN) |
| 816 | t.Logf("VPNPrivateAddress: %s", network.VPNPrivateAddress) |
| 817 | t.Logf("VPNPublicAddress: %s", network.VPNPublicAddress) |
| 818 | t.Logf("VPNPublicPort: %d", network.VPNPublicPort) |
| 819 | } |
| 820 | |
| 821 | // PrintQuotaSet will print a quota set and all of its attributes. |
| 822 | func PrintQuotaSet(t *testing.T, quotaSet *quotasets.QuotaSet) { |
| 823 | t.Logf("instances: %d\n", quotaSet.Instances) |
| 824 | t.Logf("cores: %d\n", quotaSet.Cores) |
| 825 | t.Logf("ram: %d\n", quotaSet.Ram) |
| 826 | t.Logf("key_pairs: %d\n", quotaSet.KeyPairs) |
| 827 | t.Logf("metadata_items: %d\n", quotaSet.MetadataItems) |
| 828 | t.Logf("security_groups: %d\n", quotaSet.SecurityGroups) |
| 829 | t.Logf("security_group_rules: %d\n", quotaSet.SecurityGroupRules) |
| 830 | t.Logf("fixed_ips: %d\n", quotaSet.FixedIps) |
| 831 | t.Logf("floating_ips: %d\n", quotaSet.FloatingIps) |
| 832 | t.Logf("injected_file_content_bytes: %d\n", quotaSet.InjectedFileContentBytes) |
| 833 | t.Logf("injected_file_path_bytes: %d\n", quotaSet.InjectedFilePathBytes) |
| 834 | t.Logf("injected_files: %d\n", quotaSet.InjectedFiles) |
| 835 | } |
| 836 | |
| 837 | // PrintSecurityGroup will print a security group and all of its attributes and rules. |
| 838 | func PrintSecurityGroup(t *testing.T, securityGroup *secgroups.SecurityGroup) { |
| 839 | t.Logf("ID: %s", securityGroup.ID) |
| 840 | t.Logf("Name: %s", securityGroup.Name) |
| 841 | t.Logf("Description: %s", securityGroup.Description) |
| 842 | t.Logf("Tenant ID: %s", securityGroup.TenantID) |
| 843 | t.Logf("Rules:") |
| 844 | |
| 845 | for _, rule := range securityGroup.Rules { |
| 846 | t.Logf("\tID: %s", rule.ID) |
| 847 | t.Logf("\tFrom Port: %d", rule.FromPort) |
| 848 | t.Logf("\tTo Port: %d", rule.ToPort) |
| 849 | t.Logf("\tIP Protocol: %s", rule.IPProtocol) |
| 850 | t.Logf("\tIP Range: %s", rule.IPRange.CIDR) |
| 851 | t.Logf("\tParent Group ID: %s", rule.ParentGroupID) |
| 852 | t.Logf("\tGroup Tenant ID: %s", rule.Group.TenantID) |
| 853 | t.Logf("\tGroup Name: %s", rule.Group.Name) |
| 854 | } |
| 855 | } |
| 856 | |
| 857 | // PrintServerGroup will print a server group and all of its attributes. |
| 858 | func PrintServerGroup(t *testing.T, serverGroup *servergroups.ServerGroup) { |
| 859 | t.Logf("ID: %s", serverGroup.ID) |
| 860 | t.Logf("Name: %s", serverGroup.Name) |
| 861 | t.Logf("Policies: %#v", serverGroup.Policies) |
| 862 | t.Logf("Members: %#v", serverGroup.Members) |
| 863 | t.Logf("Metadata: %#v", serverGroup.Metadata) |
| 864 | } |
| 865 | |
| 866 | // PrintTenantNetwork will print an os-tenant-networks based network and all of its attributes. |
| 867 | func PrintTenantNetwork(t *testing.T, network *tenantnetworks.Network) { |
| 868 | t.Logf("ID: %s", network.ID) |
| 869 | t.Logf("Name: %s", network.Name) |
| 870 | t.Logf("CIDR: %s", network.CIDR) |
| 871 | } |
| 872 | |
| 873 | // PrintVolumeAttachment will print a volume attachment and all of its attributes. |
| 874 | func PrintVolumeAttachment(t *testing.T, volumeAttachment *volumeattach.VolumeAttachment) { |
| 875 | t.Logf("ID: %s", volumeAttachment.ID) |
| 876 | t.Logf("Device: %s", volumeAttachment.Device) |
| 877 | t.Logf("VolumeID: %s", volumeAttachment.VolumeID) |
| 878 | t.Logf("ServerID: %s", volumeAttachment.ServerID) |
| 879 | } |