blob: a4bcf5d59413a730b709d7aa2dd198d82af384d2 [file] [log] [blame]
Joe Topjian1c15e3f2016-08-08 10:48:38 -06001// 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
Krzysztof Szukiełojć3f41d082017-05-07 14:43:06 +020011 "gerrit.mcp.mirantis.net/debian/gophercloud.git"
Krzysztof Szukiełojć24a29ce2017-05-07 14:24:02 +020012 "gerrit.mcp.mirantis.net/debian/gophercloud.git/acceptance/clients"
13 "gerrit.mcp.mirantis.net/debian/gophercloud.git/acceptance/tools"
14 "gerrit.mcp.mirantis.net/debian/gophercloud.git/openstack/blockstorage/v1/volumes"
15 "gerrit.mcp.mirantis.net/debian/gophercloud.git/openstack/compute/v2/extensions/bootfromvolume"
16 dsr "gerrit.mcp.mirantis.net/debian/gophercloud.git/openstack/compute/v2/extensions/defsecrules"
17 "gerrit.mcp.mirantis.net/debian/gophercloud.git/openstack/compute/v2/extensions/floatingips"
18 "gerrit.mcp.mirantis.net/debian/gophercloud.git/openstack/compute/v2/extensions/keypairs"
19 "gerrit.mcp.mirantis.net/debian/gophercloud.git/openstack/compute/v2/extensions/networks"
20 "gerrit.mcp.mirantis.net/debian/gophercloud.git/openstack/compute/v2/extensions/quotasets"
21 "gerrit.mcp.mirantis.net/debian/gophercloud.git/openstack/compute/v2/extensions/schedulerhints"
22 "gerrit.mcp.mirantis.net/debian/gophercloud.git/openstack/compute/v2/extensions/secgroups"
23 "gerrit.mcp.mirantis.net/debian/gophercloud.git/openstack/compute/v2/extensions/servergroups"
24 "gerrit.mcp.mirantis.net/debian/gophercloud.git/openstack/compute/v2/extensions/tenantnetworks"
25 "gerrit.mcp.mirantis.net/debian/gophercloud.git/openstack/compute/v2/extensions/volumeattach"
26 "gerrit.mcp.mirantis.net/debian/gophercloud.git/openstack/compute/v2/servers"
Joe Topjian1c15e3f2016-08-08 10:48:38 -060027
28 "golang.org/x/crypto/ssh"
29)
30
31// AssociateFloatingIP will associate a floating IP with an instance. An error
32// will be returned if the floating IP was unable to be associated.
33func AssociateFloatingIP(t *testing.T, client *gophercloud.ServiceClient, floatingIP *floatingips.FloatingIP, server *servers.Server) error {
34 associateOpts := floatingips.AssociateOpts{
35 FloatingIP: floatingIP.IP,
36 }
37
38 t.Logf("Attempting to associate floating IP %s to instance %s", floatingIP.IP, server.ID)
39 err := floatingips.AssociateInstance(client, server.ID, associateOpts).ExtractErr()
40 if err != nil {
41 return err
42 }
43
44 return nil
45}
46
47// AssociateFloatingIPWithFixedIP will associate a floating IP with an
48// instance's specific fixed IP. An error will be returend if the floating IP
49// was unable to be associated.
50func AssociateFloatingIPWithFixedIP(t *testing.T, client *gophercloud.ServiceClient, floatingIP *floatingips.FloatingIP, server *servers.Server, fixedIP string) error {
51 associateOpts := floatingips.AssociateOpts{
52 FloatingIP: floatingIP.IP,
53 FixedIP: fixedIP,
54 }
55
56 t.Logf("Attempting to associate floating IP %s to fixed IP %s on instance %s", floatingIP.IP, fixedIP, server.ID)
57 err := floatingips.AssociateInstance(client, server.ID, associateOpts).ExtractErr()
58 if err != nil {
59 return err
60 }
61
62 return nil
63}
64
65// CreateBootableVolumeServer works like CreateServer but is configured with
66// one or more block devices defined by passing in []bootfromvolume.BlockDevice.
67// An error will be returned if a server was unable to be created.
Joe Topjian66a046c2017-01-19 22:07:26 -070068func CreateBootableVolumeServer(t *testing.T, client *gophercloud.ServiceClient, blockDevices []bootfromvolume.BlockDevice) (*servers.Server, error) {
Joe Topjian1c15e3f2016-08-08 10:48:38 -060069 if testing.Short() {
70 t.Skip("Skipping test that requires server creation in short mode.")
71 }
72
73 var server *servers.Server
74
Joe Topjian66a046c2017-01-19 22:07:26 -070075 choices, err := clients.AcceptanceTestChoicesFromEnv()
76 if err != nil {
77 t.Fatal(err)
78 }
79
Joe Topjian1c15e3f2016-08-08 10:48:38 -060080 networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName)
81 if err != nil {
82 return server, err
83 }
84
85 name := tools.RandomString("ACPTTEST", 16)
86 t.Logf("Attempting to create bootable volume server: %s", name)
87
88 serverCreateOpts := servers.CreateOpts{
89 Name: name,
90 FlavorRef: choices.FlavorID,
Joe Topjian1c15e3f2016-08-08 10:48:38 -060091 Networks: []servers.Network{
92 servers.Network{UUID: networkID},
93 },
94 }
95
Joe Topjianf1f40412016-10-13 17:42:25 -060096 if blockDevices[0].SourceType == bootfromvolume.SourceImage && blockDevices[0].DestinationType == bootfromvolume.DestinationLocal {
97 serverCreateOpts.ImageRef = blockDevices[0].UUID
98 }
99
Joe Topjian1c15e3f2016-08-08 10:48:38 -0600100 server, err = bootfromvolume.Create(client, bootfromvolume.CreateOptsExt{
101 serverCreateOpts,
102 blockDevices,
103 }).Extract()
104
105 if err != nil {
106 return server, err
107 }
108
Joe Topjian50cdddf2016-09-16 10:56:09 -0600109 if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil {
110 return server, err
111 }
112
113 newServer, err := servers.Get(client, server.ID).Extract()
114
115 return newServer, nil
Joe Topjian1c15e3f2016-08-08 10:48:38 -0600116}
117
118// CreateDefaultRule will create a default security group rule with a
119// random port range between 80 and 90. An error will be returned if
120// a default rule was unable to be created.
121func CreateDefaultRule(t *testing.T, client *gophercloud.ServiceClient) (dsr.DefaultRule, error) {
122 createOpts := dsr.CreateOpts{
123 FromPort: tools.RandomInt(80, 89),
124 ToPort: tools.RandomInt(90, 99),
125 IPProtocol: "TCP",
126 CIDR: "0.0.0.0/0",
127 }
128
129 defaultRule, err := dsr.Create(client, createOpts).Extract()
130 if err != nil {
131 return *defaultRule, err
132 }
133
134 t.Logf("Created default rule: %s", defaultRule.ID)
135
136 return *defaultRule, nil
137}
138
139// CreateFloatingIP will allocate a floating IP.
140// An error will be returend if one was unable to be allocated.
Joe Topjian66a046c2017-01-19 22:07:26 -0700141func CreateFloatingIP(t *testing.T, client *gophercloud.ServiceClient) (*floatingips.FloatingIP, error) {
142 choices, err := clients.AcceptanceTestChoicesFromEnv()
143 if err != nil {
144 t.Fatal(err)
145 }
146
Joe Topjian1c15e3f2016-08-08 10:48:38 -0600147 createOpts := floatingips.CreateOpts{
148 Pool: choices.FloatingIPPoolName,
149 }
150 floatingIP, err := floatingips.Create(client, createOpts).Extract()
151 if err != nil {
152 return floatingIP, err
153 }
154
155 t.Logf("Created floating IP: %s", floatingIP.ID)
156 return floatingIP, nil
157}
158
159func createKey() (string, error) {
160 privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
161 if err != nil {
162 return "", err
163 }
164
165 publicKey := privateKey.PublicKey
166 pub, err := ssh.NewPublicKey(&publicKey)
167 if err != nil {
168 return "", err
169 }
170
171 pubBytes := ssh.MarshalAuthorizedKey(pub)
172 pk := string(pubBytes)
173 return pk, nil
174}
175
176// CreateKeyPair will create a KeyPair with a random name. An error will occur
177// if the keypair failed to be created. An error will be returned if the
178// keypair was unable to be created.
179func CreateKeyPair(t *testing.T, client *gophercloud.ServiceClient) (*keypairs.KeyPair, error) {
180 keyPairName := tools.RandomString("keypair_", 5)
181
182 t.Logf("Attempting to create keypair: %s", keyPairName)
183 createOpts := keypairs.CreateOpts{
184 Name: keyPairName,
185 }
186 keyPair, err := keypairs.Create(client, createOpts).Extract()
187 if err != nil {
188 return keyPair, err
189 }
190
191 t.Logf("Created keypair: %s", keyPairName)
192 return keyPair, nil
193}
194
Joe Topjian50cdddf2016-09-16 10:56:09 -0600195// CreateMultiEphemeralServer works like CreateServer but is configured with
196// one or more block devices defined by passing in []bootfromvolume.BlockDevice.
197// These block devices act like block devices when booting from a volume but
198// are actually local ephemeral disks.
199// An error will be returned if a server was unable to be created.
Joe Topjian66a046c2017-01-19 22:07:26 -0700200func CreateMultiEphemeralServer(t *testing.T, client *gophercloud.ServiceClient, blockDevices []bootfromvolume.BlockDevice) (*servers.Server, error) {
Joe Topjian50cdddf2016-09-16 10:56:09 -0600201 if testing.Short() {
202 t.Skip("Skipping test that requires server creation in short mode.")
203 }
204
205 var server *servers.Server
206
Joe Topjian66a046c2017-01-19 22:07:26 -0700207 choices, err := clients.AcceptanceTestChoicesFromEnv()
208 if err != nil {
209 t.Fatal(err)
210 }
211
Joe Topjian50cdddf2016-09-16 10:56:09 -0600212 networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName)
213 if err != nil {
214 return server, err
215 }
216
217 name := tools.RandomString("ACPTTEST", 16)
218 t.Logf("Attempting to create bootable volume server: %s", name)
219
220 serverCreateOpts := servers.CreateOpts{
221 Name: name,
222 FlavorRef: choices.FlavorID,
223 ImageRef: choices.ImageID,
224 Networks: []servers.Network{
225 servers.Network{UUID: networkID},
226 },
227 }
228
229 server, err = bootfromvolume.Create(client, bootfromvolume.CreateOptsExt{
230 serverCreateOpts,
231 blockDevices,
232 }).Extract()
233
234 if err != nil {
235 return server, err
236 }
237
238 if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil {
239 return server, err
240 }
241
242 newServer, err := servers.Get(client, server.ID).Extract()
243
244 return newServer, nil
245}
246
Joe Topjian1c15e3f2016-08-08 10:48:38 -0600247// CreateSecurityGroup will create a security group with a random name.
248// An error will be returned if one was failed to be created.
249func CreateSecurityGroup(t *testing.T, client *gophercloud.ServiceClient) (secgroups.SecurityGroup, error) {
250 createOpts := secgroups.CreateOpts{
251 Name: tools.RandomString("secgroup_", 5),
252 Description: "something",
253 }
254
255 securityGroup, err := secgroups.Create(client, createOpts).Extract()
256 if err != nil {
257 return *securityGroup, err
258 }
259
260 t.Logf("Created security group: %s", securityGroup.ID)
261 return *securityGroup, nil
262}
263
264// CreateSecurityGroupRule will create a security group rule with a random name
265// and a random TCP port range between port 80 and 99. An error will be
266// returned if the rule failed to be created.
267func CreateSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, securityGroupID string) (secgroups.Rule, error) {
268 createOpts := secgroups.CreateRuleOpts{
269 ParentGroupID: securityGroupID,
270 FromPort: tools.RandomInt(80, 89),
271 ToPort: tools.RandomInt(90, 99),
272 IPProtocol: "TCP",
273 CIDR: "0.0.0.0/0",
274 }
275
276 rule, err := secgroups.CreateRule(client, createOpts).Extract()
277 if err != nil {
278 return *rule, err
279 }
280
281 t.Logf("Created security group rule: %s", rule.ID)
282 return *rule, nil
283}
284
285// CreateServer creates a basic instance with a randomly generated name.
286// The flavor of the instance will be the value of the OS_FLAVOR_ID environment variable.
287// The image will be the value of the OS_IMAGE_ID environment variable.
288// The instance will be launched on the network specified in OS_NETWORK_NAME.
289// An error will be returned if the instance was unable to be created.
Joe Topjian66a046c2017-01-19 22:07:26 -0700290func CreateServer(t *testing.T, client *gophercloud.ServiceClient) (*servers.Server, error) {
Joe Topjian1c15e3f2016-08-08 10:48:38 -0600291 if testing.Short() {
292 t.Skip("Skipping test that requires server creation in short mode.")
293 }
294
295 var server *servers.Server
296
Joe Topjian66a046c2017-01-19 22:07:26 -0700297 choices, err := clients.AcceptanceTestChoicesFromEnv()
298 if err != nil {
299 t.Fatal(err)
300 }
301
Joe Topjian1c15e3f2016-08-08 10:48:38 -0600302 networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName)
303 if err != nil {
304 return server, err
305 }
306
307 name := tools.RandomString("ACPTTEST", 16)
308 t.Logf("Attempting to create server: %s", name)
309
310 pwd := tools.MakeNewPassword("")
311
312 server, err = servers.Create(client, servers.CreateOpts{
313 Name: name,
314 FlavorRef: choices.FlavorID,
315 ImageRef: choices.ImageID,
316 AdminPass: pwd,
317 Networks: []servers.Network{
318 servers.Network{UUID: networkID},
319 },
Joe Topjianf464c962016-09-12 08:02:43 -0600320 Metadata: map[string]string{
321 "abc": "def",
322 },
Joe Topjian1c15e3f2016-08-08 10:48:38 -0600323 Personality: servers.Personality{
324 &servers.File{
325 Path: "/etc/test",
326 Contents: []byte("hello world"),
327 },
328 },
329 }).Extract()
330 if err != nil {
331 return server, err
332 }
333
Joe Topjian50cdddf2016-09-16 10:56:09 -0600334 if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil {
335 return server, err
336 }
337
338 return server, nil
339}
340
341// CreateServerWithoutImageRef creates a basic instance with a randomly generated name.
342// The flavor of the instance will be the value of the OS_FLAVOR_ID environment variable.
343// The image is intentionally missing to trigger an error.
344// The instance will be launched on the network specified in OS_NETWORK_NAME.
345// An error will be returned if the instance was unable to be created.
Joe Topjian66a046c2017-01-19 22:07:26 -0700346func CreateServerWithoutImageRef(t *testing.T, client *gophercloud.ServiceClient) (*servers.Server, error) {
Joe Topjian50cdddf2016-09-16 10:56:09 -0600347 if testing.Short() {
348 t.Skip("Skipping test that requires server creation in short mode.")
349 }
350
351 var server *servers.Server
352
Joe Topjian66a046c2017-01-19 22:07:26 -0700353 choices, err := clients.AcceptanceTestChoicesFromEnv()
354 if err != nil {
355 t.Fatal(err)
356 }
357
Joe Topjian50cdddf2016-09-16 10:56:09 -0600358 networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName)
359 if err != nil {
360 return server, err
361 }
362
363 name := tools.RandomString("ACPTTEST", 16)
364 t.Logf("Attempting to create server: %s", name)
365
366 pwd := tools.MakeNewPassword("")
367
368 server, err = servers.Create(client, servers.CreateOpts{
369 Name: name,
370 FlavorRef: choices.FlavorID,
371 AdminPass: pwd,
372 Networks: []servers.Network{
373 servers.Network{UUID: networkID},
374 },
375 Personality: servers.Personality{
376 &servers.File{
377 Path: "/etc/test",
378 Contents: []byte("hello world"),
379 },
380 },
381 }).Extract()
382 if err != nil {
383 return server, err
384 }
385
386 if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil {
Joe Topjian1c15e3f2016-08-08 10:48:38 -0600387 return server, err
388 }
389
390 return server, nil
391}
392
393// CreateServerGroup will create a server with a random name. An error will be
394// returned if the server group failed to be created.
395func CreateServerGroup(t *testing.T, client *gophercloud.ServiceClient, policy string) (*servergroups.ServerGroup, error) {
396 sg, err := servergroups.Create(client, &servergroups.CreateOpts{
397 Name: "test",
398 Policies: []string{policy},
399 }).Extract()
400
401 if err != nil {
402 return sg, err
403 }
404
405 return sg, nil
406}
407
408// CreateServerInServerGroup works like CreateServer but places the instance in
409// a specified Server Group.
Joe Topjian66a046c2017-01-19 22:07:26 -0700410func CreateServerInServerGroup(t *testing.T, client *gophercloud.ServiceClient, serverGroup *servergroups.ServerGroup) (*servers.Server, error) {
Joe Topjian1c15e3f2016-08-08 10:48:38 -0600411 if testing.Short() {
412 t.Skip("Skipping test that requires server creation in short mode.")
413 }
414
415 var server *servers.Server
416
Joe Topjian66a046c2017-01-19 22:07:26 -0700417 choices, err := clients.AcceptanceTestChoicesFromEnv()
418 if err != nil {
419 t.Fatal(err)
420 }
421
Joe Topjian1c15e3f2016-08-08 10:48:38 -0600422 networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName)
423 if err != nil {
424 return server, err
425 }
426
427 name := tools.RandomString("ACPTTEST", 16)
428 t.Logf("Attempting to create server: %s", name)
429
430 pwd := tools.MakeNewPassword("")
431
432 serverCreateOpts := servers.CreateOpts{
433 Name: name,
434 FlavorRef: choices.FlavorID,
435 ImageRef: choices.ImageID,
436 AdminPass: pwd,
437 Networks: []servers.Network{
438 servers.Network{UUID: networkID},
439 },
440 }
441
442 schedulerHintsOpts := schedulerhints.CreateOptsExt{
443 serverCreateOpts,
444 schedulerhints.SchedulerHints{
445 Group: serverGroup.ID,
446 },
447 }
448 server, err = servers.Create(client, schedulerHintsOpts).Extract()
449 if err != nil {
450 return server, err
451 }
452
453 return server, nil
454}
455
456// CreateServerWithPublicKey works the same as CreateServer, but additionally
457// configures the server with a specified Key Pair name.
Joe Topjian66a046c2017-01-19 22:07:26 -0700458func CreateServerWithPublicKey(t *testing.T, client *gophercloud.ServiceClient, keyPairName string) (*servers.Server, error) {
Joe Topjian1c15e3f2016-08-08 10:48:38 -0600459 if testing.Short() {
460 t.Skip("Skipping test that requires server creation in short mode.")
461 }
462
463 var server *servers.Server
464
Joe Topjian66a046c2017-01-19 22:07:26 -0700465 choices, err := clients.AcceptanceTestChoicesFromEnv()
466 if err != nil {
467 t.Fatal(err)
468 }
469
Joe Topjian1c15e3f2016-08-08 10:48:38 -0600470 networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName)
471 if err != nil {
472 return server, err
473 }
474
475 name := tools.RandomString("ACPTTEST", 16)
476 t.Logf("Attempting to create server: %s", name)
477
478 serverCreateOpts := servers.CreateOpts{
479 Name: name,
480 FlavorRef: choices.FlavorID,
481 ImageRef: choices.ImageID,
482 Networks: []servers.Network{
483 servers.Network{UUID: networkID},
484 },
485 }
486
487 server, err = servers.Create(client, keypairs.CreateOptsExt{
488 serverCreateOpts,
489 keyPairName,
490 }).Extract()
491 if err != nil {
492 return server, err
493 }
494
Joe Topjian50cdddf2016-09-16 10:56:09 -0600495 if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil {
Joe Topjian1c15e3f2016-08-08 10:48:38 -0600496 return server, err
497 }
498
499 return server, nil
500}
501
Gleb37b56e82016-09-06 19:07:58 +0300502// CreateVolumeAttachment will attach a volume to a server. An error will be
Joe Topjian1c15e3f2016-08-08 10:48:38 -0600503// returned if the volume failed to attach.
504func CreateVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, blockClient *gophercloud.ServiceClient, server *servers.Server, volume *volumes.Volume) (*volumeattach.VolumeAttachment, error) {
505 volumeAttachOptions := volumeattach.CreateOpts{
506 VolumeID: volume.ID,
507 }
508
509 t.Logf("Attempting to attach volume %s to server %s", volume.ID, server.ID)
510 volumeAttachment, err := volumeattach.Create(client, server.ID, volumeAttachOptions).Extract()
511 if err != nil {
512 return volumeAttachment, err
513 }
514
Joe Topjian50cdddf2016-09-16 10:56:09 -0600515 if err := volumes.WaitForStatus(blockClient, volume.ID, "in-use", 60); err != nil {
Joe Topjian1c15e3f2016-08-08 10:48:38 -0600516 return volumeAttachment, err
517 }
518
519 return volumeAttachment, nil
520}
521
522// DeleteDefaultRule deletes a default security group rule.
523// A fatal error will occur if the rule failed to delete. This works best when
524// using it as a deferred function.
525func DeleteDefaultRule(t *testing.T, client *gophercloud.ServiceClient, defaultRule dsr.DefaultRule) {
526 err := dsr.Delete(client, defaultRule.ID).ExtractErr()
527 if err != nil {
528 t.Fatalf("Unable to delete default rule %s: %v", defaultRule.ID, err)
529 }
530
531 t.Logf("Deleted default rule: %s", defaultRule.ID)
532}
533
534// DeleteFloatingIP will de-allocate a floating IP. A fatal error will occur if
535// the floating IP failed to de-allocate. This works best when using it as a
536// deferred function.
537func DeleteFloatingIP(t *testing.T, client *gophercloud.ServiceClient, floatingIP *floatingips.FloatingIP) {
538 err := floatingips.Delete(client, floatingIP.ID).ExtractErr()
539 if err != nil {
540 t.Fatalf("Unable to delete floating IP %s: %v", floatingIP.ID, err)
541 }
542
543 t.Logf("Deleted floating IP: %s", floatingIP.ID)
544}
545
546// DeleteKeyPair will delete a specified keypair. A fatal error will occur if
547// the keypair failed to be deleted. This works best when used as a deferred
548// function.
549func DeleteKeyPair(t *testing.T, client *gophercloud.ServiceClient, keyPair *keypairs.KeyPair) {
550 err := keypairs.Delete(client, keyPair.Name).ExtractErr()
551 if err != nil {
552 t.Fatalf("Unable to delete keypair %s: %v", keyPair.Name, err)
553 }
554
555 t.Logf("Deleted keypair: %s", keyPair.Name)
556}
557
558// DeleteSecurityGroup will delete a security group. A fatal error will occur
559// if the group failed to be deleted. This works best as a deferred function.
560func DeleteSecurityGroup(t *testing.T, client *gophercloud.ServiceClient, securityGroup secgroups.SecurityGroup) {
561 err := secgroups.Delete(client, securityGroup.ID).ExtractErr()
562 if err != nil {
563 t.Fatalf("Unable to delete security group %s: %s", securityGroup.ID, err)
564 }
565
566 t.Logf("Deleted security group: %s", securityGroup.ID)
567}
568
569// DeleteSecurityGroupRule will delete a security group rule. A fatal error
570// will occur if the rule failed to be deleted. This works best when used
571// as a deferred function.
572func DeleteSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, rule secgroups.Rule) {
573 err := secgroups.DeleteRule(client, rule.ID).ExtractErr()
574 if err != nil {
575 t.Fatalf("Unable to delete rule: %v", err)
576 }
577
578 t.Logf("Deleted security group rule: %s", rule.ID)
579}
580
581// DeleteServer deletes an instance via its UUID.
582// A fatal error will occur if the instance failed to be destroyed. This works
583// best when using it as a deferred function.
584func DeleteServer(t *testing.T, client *gophercloud.ServiceClient, server *servers.Server) {
585 err := servers.Delete(client, server.ID).ExtractErr()
586 if err != nil {
587 t.Fatalf("Unable to delete server %s: %s", server.ID, err)
588 }
589
590 t.Logf("Deleted server: %s", server.ID)
591}
592
593// DeleteServerGroup will delete a server group. A fatal error will occur if
594// the server group failed to be deleted. This works best when used as a
595// deferred function.
596func DeleteServerGroup(t *testing.T, client *gophercloud.ServiceClient, serverGroup *servergroups.ServerGroup) {
597 err := servergroups.Delete(client, serverGroup.ID).ExtractErr()
598 if err != nil {
599 t.Fatalf("Unable to delete server group %s: %v", serverGroup.ID, err)
600 }
601
602 t.Logf("Deleted server group %s", serverGroup.ID)
603}
604
605// DeleteVolumeAttachment will disconnect a volume from an instance. A fatal
606// error will occur if the volume failed to detach. This works best when used
Gleb37b56e82016-09-06 19:07:58 +0300607// as a deferred function.
Joe Topjian1c15e3f2016-08-08 10:48:38 -0600608func DeleteVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, blockClient *gophercloud.ServiceClient, server *servers.Server, volumeAttachment *volumeattach.VolumeAttachment) {
609
610 err := volumeattach.Delete(client, server.ID, volumeAttachment.VolumeID).ExtractErr()
611 if err != nil {
612 t.Fatalf("Unable to detach volume: %v", err)
613 }
614
Joe Topjian50cdddf2016-09-16 10:56:09 -0600615 if err := volumes.WaitForStatus(blockClient, volumeAttachment.ID, "available", 60); err != nil {
Joe Topjian1c15e3f2016-08-08 10:48:38 -0600616 t.Fatalf("Unable to wait for volume: %v", err)
617 }
618 t.Logf("Deleted volume: %s", volumeAttachment.VolumeID)
619}
620
621// DisassociateFloatingIP will disassociate a floating IP from an instance. A
622// fatal error will occur if the floating IP failed to disassociate. This works
623// best when using it as a deferred function.
624func DisassociateFloatingIP(t *testing.T, client *gophercloud.ServiceClient, floatingIP *floatingips.FloatingIP, server *servers.Server) {
625 disassociateOpts := floatingips.DisassociateOpts{
626 FloatingIP: floatingIP.IP,
627 }
628
629 err := floatingips.DisassociateInstance(client, server.ID, disassociateOpts).ExtractErr()
630 if err != nil {
631 t.Fatalf("Unable to disassociate floating IP %s from server %s: %v", floatingIP.IP, server.ID, err)
632 }
633
634 t.Logf("Disassociated floating IP %s from server %s", floatingIP.IP, server.ID)
635}
636
637// GetNetworkIDFromNetworks will return the network ID from a specified network
638// UUID using the os-networks API extension. An error will be returned if the
639// network could not be retrieved.
640func GetNetworkIDFromNetworks(t *testing.T, client *gophercloud.ServiceClient, networkName string) (string, error) {
641 allPages, err := networks.List(client).AllPages()
642 if err != nil {
643 t.Fatalf("Unable to list networks: %v", err)
644 }
645
646 networkList, err := networks.ExtractNetworks(allPages)
647 if err != nil {
648 t.Fatalf("Unable to list networks: %v", err)
649 }
650
651 networkID := ""
652 for _, network := range networkList {
653 t.Logf("Network: %v", network)
654 if network.Label == networkName {
655 networkID = network.ID
656 }
657 }
658
659 t.Logf("Found network ID for %s: %s", networkName, networkID)
660
661 return networkID, nil
662}
663
664// GetNetworkIDFromTenantNetworks will return the network UUID for a given
665// network name using the os-tenant-networks API extension. An error will be
666// returned if the network could not be retrieved.
667func GetNetworkIDFromTenantNetworks(t *testing.T, client *gophercloud.ServiceClient, networkName string) (string, error) {
668 allPages, err := tenantnetworks.List(client).AllPages()
669 if err != nil {
670 return "", err
671 }
672
673 allTenantNetworks, err := tenantnetworks.ExtractNetworks(allPages)
674 if err != nil {
675 return "", err
676 }
677
678 for _, network := range allTenantNetworks {
679 if network.Name == networkName {
680 return network.ID, nil
681 }
682 }
683
684 return "", fmt.Errorf("Failed to obtain network ID for network %s", networkName)
685}
686
687// ImportPublicKey will create a KeyPair with a random name and a specified
688// public key. An error will be returned if the keypair failed to be created.
689func ImportPublicKey(t *testing.T, client *gophercloud.ServiceClient, publicKey string) (*keypairs.KeyPair, error) {
690 keyPairName := tools.RandomString("keypair_", 5)
691
692 t.Logf("Attempting to create keypair: %s", keyPairName)
693 createOpts := keypairs.CreateOpts{
694 Name: keyPairName,
695 PublicKey: publicKey,
696 }
697 keyPair, err := keypairs.Create(client, createOpts).Extract()
698 if err != nil {
699 return keyPair, err
700 }
701
702 t.Logf("Created keypair: %s", keyPairName)
703 return keyPair, nil
704}
705
706// ResizeServer performs a resize action on an instance. An error will be
707// returned if the instance failed to resize.
708// The new flavor that the instance will be resized to is specified in OS_FLAVOR_ID_RESIZE.
Joe Topjian66a046c2017-01-19 22:07:26 -0700709func ResizeServer(t *testing.T, client *gophercloud.ServiceClient, server *servers.Server) error {
710 choices, err := clients.AcceptanceTestChoicesFromEnv()
711 if err != nil {
712 t.Fatal(err)
713 }
714
Joe Topjian1c15e3f2016-08-08 10:48:38 -0600715 opts := &servers.ResizeOpts{
716 FlavorRef: choices.FlavorIDResize,
717 }
718 if res := servers.Resize(client, server.ID, opts); res.Err != nil {
719 return res.Err
720 }
721
722 if err := WaitForComputeStatus(client, server, "VERIFY_RESIZE"); err != nil {
723 return err
724 }
725
726 return nil
727}
728
729// WaitForComputeStatus will poll an instance's status until it either matches
730// the specified status or the status becomes ERROR.
731func WaitForComputeStatus(client *gophercloud.ServiceClient, server *servers.Server, status string) error {
732 return tools.WaitFor(func() (bool, error) {
733 latest, err := servers.Get(client, server.ID).Extract()
734 if err != nil {
735 return false, err
736 }
737
738 if latest.Status == status {
739 // Success!
740 return true, nil
741 }
742
743 if latest.Status == "ERROR" {
744 return false, fmt.Errorf("Instance in ERROR state")
745 }
746
747 return false, nil
748 })
749}
750
dbaumgartenc2bb4912017-01-19 17:14:08 +0100751//Convenience method to fill an QuotaSet-UpdateOpts-struct from a QuotaSet-struct
Joe Topjian66a046c2017-01-19 22:07:26 -0700752func FillUpdateOptsFromQuotaSet(src quotasets.QuotaSet, dest *quotasets.UpdateOpts) {
dbaumgartenc2bb4912017-01-19 17:14:08 +0100753 dest.FixedIps = &src.FixedIps
754 dest.FloatingIps = &src.FloatingIps
755 dest.InjectedFileContentBytes = &src.InjectedFileContentBytes
756 dest.InjectedFilePathBytes = &src.InjectedFilePathBytes
757 dest.InjectedFiles = &src.InjectedFiles
758 dest.KeyPairs = &src.KeyPairs
759 dest.Ram = &src.Ram
760 dest.SecurityGroupRules = &src.SecurityGroupRules
761 dest.SecurityGroups = &src.SecurityGroups
762 dest.Cores = &src.Cores
763 dest.Instances = &src.Instances
764 dest.ServerGroups = &src.ServerGroups
765 dest.ServerGroupMembers = &src.ServerGroupMembers
766 dest.MetadataItems = &src.MetadataItems
767}