blob: 97d61f6531cb8a474c34248b3c630fad4f6300e7 [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
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.
35func 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.
52func 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.
70func 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 Topjian1c15e3f2016-08-08 10:48:38 -060088 Networks: []servers.Network{
89 servers.Network{UUID: networkID},
90 },
91 }
92
Joe Topjianf1f40412016-10-13 17:42:25 -060093 if blockDevices[0].SourceType == bootfromvolume.SourceImage && blockDevices[0].DestinationType == bootfromvolume.DestinationLocal {
94 serverCreateOpts.ImageRef = blockDevices[0].UUID
95 }
96
Joe Topjian1c15e3f2016-08-08 10:48:38 -060097 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 Topjian50cdddf2016-09-16 10:56:09 -0600106 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 Topjian1c15e3f2016-08-08 10:48:38 -0600113}
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.
118func 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.
138func 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
151func 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.
171func 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 Topjian50cdddf2016-09-16 10:56:09 -0600187// 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.
192func 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 Topjian1c15e3f2016-08-08 10:48:38 -0600234// CreateSecurityGroup will create a security group with a random name.
235// An error will be returned if one was failed to be created.
236func 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.
254func 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.
277func 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 Topjianf464c962016-09-12 08:02:43 -0600302 Metadata: map[string]string{
303 "abc": "def",
304 },
Joe Topjian1c15e3f2016-08-08 10:48:38 -0600305 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 Topjian50cdddf2016-09-16 10:56:09 -0600316 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.
328func 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 Topjian1c15e3f2016-08-08 10:48:38 -0600364 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.
372func 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.
387func 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.
430func 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 Topjian50cdddf2016-09-16 10:56:09 -0600462 if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil {
Joe Topjian1c15e3f2016-08-08 10:48:38 -0600463 return server, err
464 }
465
466 return server, nil
467}
468
Gleb37b56e82016-09-06 19:07:58 +0300469// CreateVolumeAttachment will attach a volume to a server. An error will be
Joe Topjian1c15e3f2016-08-08 10:48:38 -0600470// returned if the volume failed to attach.
471func 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 Topjian50cdddf2016-09-16 10:56:09 -0600482 if err := volumes.WaitForStatus(blockClient, volume.ID, "in-use", 60); err != nil {
Joe Topjian1c15e3f2016-08-08 10:48:38 -0600483 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.
492func 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.
504func 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.
516func 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.
527func 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.
539func 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.
551func 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.
563func 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
Gleb37b56e82016-09-06 19:07:58 +0300574// as a deferred function.
Joe Topjian1c15e3f2016-08-08 10:48:38 -0600575func 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 Topjian50cdddf2016-09-16 10:56:09 -0600582 if err := volumes.WaitForStatus(blockClient, volumeAttachment.ID, "available", 60); err != nil {
Joe Topjian1c15e3f2016-08-08 10:48:38 -0600583 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.
591func 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.
607func 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.
634func 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.
656func 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.
676func 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.
693func 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
dbaumgartenc2bb4912017-01-19 17:14:08 +0100713//Convenience method to fill an QuotaSet-UpdateOpts-struct from a QuotaSet-struct
714func FillUpdateOptsFromQuotaSet(src quotasets.QuotaSet,dest *quotasets.UpdateOpts) {
715 dest.FixedIps = &src.FixedIps
716 dest.FloatingIps = &src.FloatingIps
717 dest.InjectedFileContentBytes = &src.InjectedFileContentBytes
718 dest.InjectedFilePathBytes = &src.InjectedFilePathBytes
719 dest.InjectedFiles = &src.InjectedFiles
720 dest.KeyPairs = &src.KeyPairs
721 dest.Ram = &src.Ram
722 dest.SecurityGroupRules = &src.SecurityGroupRules
723 dest.SecurityGroups = &src.SecurityGroups
724 dest.Cores = &src.Cores
725 dest.Instances = &src.Instances
726 dest.ServerGroups = &src.ServerGroups
727 dest.ServerGroupMembers = &src.ServerGroupMembers
728 dest.MetadataItems = &src.MetadataItems
729}
730
Joe Topjian1c15e3f2016-08-08 10:48:38 -0600731// PrintServer will print an instance and all of its attributes.
732func PrintServer(t *testing.T, server *servers.Server) {
733 t.Logf("ID: %s", server.ID)
734 t.Logf("TenantID: %s", server.TenantID)
735 t.Logf("UserID: %s", server.UserID)
736 t.Logf("Name: %s", server.Name)
737 t.Logf("Updated: %s", server.Updated)
738 t.Logf("Created: %s", server.Created)
739 t.Logf("HostID: %s", server.HostID)
740 t.Logf("Status: %s", server.Status)
741 t.Logf("Progress: %d", server.Progress)
742 t.Logf("AccessIPv4: %s", server.AccessIPv4)
743 t.Logf("AccessIPv6: %s", server.AccessIPv6)
744 t.Logf("Image: %s", server.Image)
745 t.Logf("Flavor: %s", server.Flavor)
746 t.Logf("Addresses: %#v", server.Addresses)
747 t.Logf("Metadata: %#v", server.Metadata)
748 t.Logf("Links: %#v", server.Links)
749 t.Logf("KeyName: %s", server.KeyName)
750 t.Logf("AdminPass: %s", server.AdminPass)
751 t.Logf("SecurityGroups: %#v", server.SecurityGroups)
752}
753
754// PrintDefaultRule will print a default security group rule and all of its attributes.
755func PrintDefaultRule(t *testing.T, defaultRule *dsr.DefaultRule) {
756 t.Logf("\tID: %s", defaultRule.ID)
757 t.Logf("\tFrom Port: %d", defaultRule.FromPort)
758 t.Logf("\tTo Port: %d", defaultRule.ToPort)
759 t.Logf("\tIP Protocol: %s", defaultRule.IPProtocol)
760 t.Logf("\tIP Range: %s", defaultRule.IPRange.CIDR)
761 t.Logf("\tParent Group ID: %s", defaultRule.ParentGroupID)
762 t.Logf("\tGroup Tenant ID: %s", defaultRule.Group.TenantID)
763 t.Logf("\tGroup Name: %s", defaultRule.Group.Name)
764}
765
766// PrintFlavor will print a flavor and all of its attributes.
767func PrintFlavor(t *testing.T, flavor *flavors.Flavor) {
768 t.Logf("ID: %s", flavor.ID)
769 t.Logf("Name: %s", flavor.Name)
770 t.Logf("RAM: %d", flavor.RAM)
771 t.Logf("Disk: %d", flavor.Disk)
772 t.Logf("Swap: %d", flavor.Swap)
773 t.Logf("RxTxFactor: %f", flavor.RxTxFactor)
774}
775
776// PrintFloatingIP will print a floating IP and all of its attributes.
777func PrintFloatingIP(t *testing.T, floatingIP *floatingips.FloatingIP) {
778 t.Logf("ID: %s", floatingIP.ID)
779 t.Logf("Fixed IP: %s", floatingIP.FixedIP)
780 t.Logf("Instance ID: %s", floatingIP.InstanceID)
781 t.Logf("IP: %s", floatingIP.IP)
782 t.Logf("Pool: %s", floatingIP.Pool)
783}
784
785// PrintImage will print an image and all of its attributes.
786func PrintImage(t *testing.T, image images.Image) {
787 t.Logf("ID: %s", image.ID)
788 t.Logf("Name: %s", image.Name)
789 t.Logf("MinDisk: %d", image.MinDisk)
790 t.Logf("MinRAM: %d", image.MinRAM)
791 t.Logf("Status: %s", image.Status)
792 t.Logf("Progress: %d", image.Progress)
793 t.Logf("Metadata: %#v", image.Metadata)
794 t.Logf("Created: %s", image.Created)
795 t.Logf("Updated: %s", image.Updated)
796}
797
798// PrintKeyPair will print keypair and all of its attributes.
799func PrintKeyPair(t *testing.T, keypair *keypairs.KeyPair) {
800 t.Logf("Name: %s", keypair.Name)
801 t.Logf("Fingerprint: %s", keypair.Fingerprint)
802 t.Logf("Public Key: %s", keypair.PublicKey)
803 t.Logf("Private Key: %s", keypair.PrivateKey)
804 t.Logf("UserID: %s", keypair.UserID)
805}
806
807// PrintNetwork will print an os-networks based network and all of its attributes.
808func PrintNetwork(t *testing.T, network *networks.Network) {
809 t.Logf("Bridge: %s", network.Bridge)
810 t.Logf("BridgeInterface: %s", network.BridgeInterface)
811 t.Logf("Broadcast: %s", network.Broadcast)
812 t.Logf("CIDR: %s", network.CIDR)
813 t.Logf("CIDRv6: %s", network.CIDRv6)
814 t.Logf("CreatedAt: %v", network.CreatedAt)
815 t.Logf("Deleted: %t", network.Deleted)
816 t.Logf("DeletedAt: %v", network.DeletedAt)
817 t.Logf("DHCPStart: %s", network.DHCPStart)
818 t.Logf("DNS1: %s", network.DNS1)
819 t.Logf("DNS2: %s", network.DNS2)
820 t.Logf("Gateway: %s", network.Gateway)
821 t.Logf("Gatewayv6: %s", network.Gatewayv6)
822 t.Logf("Host: %s", network.Host)
823 t.Logf("ID: %s", network.ID)
824 t.Logf("Injected: %t", network.Injected)
825 t.Logf("Label: %s", network.Label)
826 t.Logf("MultiHost: %t", network.MultiHost)
827 t.Logf("Netmask: %s", network.Netmask)
828 t.Logf("Netmaskv6: %s", network.Netmaskv6)
829 t.Logf("Priority: %d", network.Priority)
830 t.Logf("ProjectID: %s", network.ProjectID)
831 t.Logf("RXTXBase: %d", network.RXTXBase)
832 t.Logf("UpdatedAt: %v", network.UpdatedAt)
833 t.Logf("VLAN: %d", network.VLAN)
834 t.Logf("VPNPrivateAddress: %s", network.VPNPrivateAddress)
835 t.Logf("VPNPublicAddress: %s", network.VPNPublicAddress)
836 t.Logf("VPNPublicPort: %d", network.VPNPublicPort)
837}
838
839// PrintQuotaSet will print a quota set and all of its attributes.
840func PrintQuotaSet(t *testing.T, quotaSet *quotasets.QuotaSet) {
841 t.Logf("instances: %d\n", quotaSet.Instances)
842 t.Logf("cores: %d\n", quotaSet.Cores)
843 t.Logf("ram: %d\n", quotaSet.Ram)
844 t.Logf("key_pairs: %d\n", quotaSet.KeyPairs)
845 t.Logf("metadata_items: %d\n", quotaSet.MetadataItems)
846 t.Logf("security_groups: %d\n", quotaSet.SecurityGroups)
847 t.Logf("security_group_rules: %d\n", quotaSet.SecurityGroupRules)
848 t.Logf("fixed_ips: %d\n", quotaSet.FixedIps)
849 t.Logf("floating_ips: %d\n", quotaSet.FloatingIps)
850 t.Logf("injected_file_content_bytes: %d\n", quotaSet.InjectedFileContentBytes)
851 t.Logf("injected_file_path_bytes: %d\n", quotaSet.InjectedFilePathBytes)
852 t.Logf("injected_files: %d\n", quotaSet.InjectedFiles)
853}
854
855// PrintSecurityGroup will print a security group and all of its attributes and rules.
856func PrintSecurityGroup(t *testing.T, securityGroup *secgroups.SecurityGroup) {
857 t.Logf("ID: %s", securityGroup.ID)
858 t.Logf("Name: %s", securityGroup.Name)
859 t.Logf("Description: %s", securityGroup.Description)
860 t.Logf("Tenant ID: %s", securityGroup.TenantID)
861 t.Logf("Rules:")
862
863 for _, rule := range securityGroup.Rules {
864 t.Logf("\tID: %s", rule.ID)
865 t.Logf("\tFrom Port: %d", rule.FromPort)
866 t.Logf("\tTo Port: %d", rule.ToPort)
867 t.Logf("\tIP Protocol: %s", rule.IPProtocol)
868 t.Logf("\tIP Range: %s", rule.IPRange.CIDR)
869 t.Logf("\tParent Group ID: %s", rule.ParentGroupID)
870 t.Logf("\tGroup Tenant ID: %s", rule.Group.TenantID)
871 t.Logf("\tGroup Name: %s", rule.Group.Name)
872 }
873}
874
875// PrintServerGroup will print a server group and all of its attributes.
876func PrintServerGroup(t *testing.T, serverGroup *servergroups.ServerGroup) {
877 t.Logf("ID: %s", serverGroup.ID)
878 t.Logf("Name: %s", serverGroup.Name)
879 t.Logf("Policies: %#v", serverGroup.Policies)
880 t.Logf("Members: %#v", serverGroup.Members)
881 t.Logf("Metadata: %#v", serverGroup.Metadata)
882}
883
884// PrintTenantNetwork will print an os-tenant-networks based network and all of its attributes.
885func PrintTenantNetwork(t *testing.T, network *tenantnetworks.Network) {
886 t.Logf("ID: %s", network.ID)
887 t.Logf("Name: %s", network.Name)
888 t.Logf("CIDR: %s", network.CIDR)
889}
890
891// PrintVolumeAttachment will print a volume attachment and all of its attributes.
892func PrintVolumeAttachment(t *testing.T, volumeAttachment *volumeattach.VolumeAttachment) {
893 t.Logf("ID: %s", volumeAttachment.ID)
894 t.Logf("Device: %s", volumeAttachment.Device)
895 t.Logf("VolumeID: %s", volumeAttachment.VolumeID)
896 t.Logf("ServerID: %s", volumeAttachment.ServerID)
897}