PageRenderTime 46ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/github.com/gophercloud/gophercloud/acceptance/openstack/compute/v2/compute.go

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