Joe Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 1 | // Package v2 contains common functions for creating compute-based resources |
| 2 | // for use in acceptance tests. See the `*_test.go` files for example usages. |
| 3 | package v2 |
| 4 | |
| 5 | import ( |
| 6 | "crypto/rand" |
| 7 | "crypto/rsa" |
| 8 | "fmt" |
| 9 | "testing" |
| 10 | |
Krzysztof Szukiełojć | 3f41d08 | 2017-05-07 14:43:06 +0200 | [diff] [blame^] | 11 | "gerrit.mcp.mirantis.net/debian/gophercloud.git" |
Krzysztof Szukiełojć | 24a29ce | 2017-05-07 14:24:02 +0200 | [diff] [blame] | 12 | "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 Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 27 | |
| 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. |
| 33 | func 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. |
| 50 | func 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 Topjian | 66a046c | 2017-01-19 22:07:26 -0700 | [diff] [blame] | 68 | func CreateBootableVolumeServer(t *testing.T, client *gophercloud.ServiceClient, blockDevices []bootfromvolume.BlockDevice) (*servers.Server, error) { |
Joe Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 69 | if testing.Short() { |
| 70 | t.Skip("Skipping test that requires server creation in short mode.") |
| 71 | } |
| 72 | |
| 73 | var server *servers.Server |
| 74 | |
Joe Topjian | 66a046c | 2017-01-19 22:07:26 -0700 | [diff] [blame] | 75 | choices, err := clients.AcceptanceTestChoicesFromEnv() |
| 76 | if err != nil { |
| 77 | t.Fatal(err) |
| 78 | } |
| 79 | |
Joe Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 80 | 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 Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 91 | Networks: []servers.Network{ |
| 92 | servers.Network{UUID: networkID}, |
| 93 | }, |
| 94 | } |
| 95 | |
Joe Topjian | f1f4041 | 2016-10-13 17:42:25 -0600 | [diff] [blame] | 96 | if blockDevices[0].SourceType == bootfromvolume.SourceImage && blockDevices[0].DestinationType == bootfromvolume.DestinationLocal { |
| 97 | serverCreateOpts.ImageRef = blockDevices[0].UUID |
| 98 | } |
| 99 | |
Joe Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 100 | 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 Topjian | 50cdddf | 2016-09-16 10:56:09 -0600 | [diff] [blame] | 109 | 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 Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 116 | } |
| 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. |
| 121 | func 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 Topjian | 66a046c | 2017-01-19 22:07:26 -0700 | [diff] [blame] | 141 | func 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 Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 147 | 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 | |
| 159 | func 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. |
| 179 | func 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 Topjian | 50cdddf | 2016-09-16 10:56:09 -0600 | [diff] [blame] | 195 | // 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 Topjian | 66a046c | 2017-01-19 22:07:26 -0700 | [diff] [blame] | 200 | func CreateMultiEphemeralServer(t *testing.T, client *gophercloud.ServiceClient, blockDevices []bootfromvolume.BlockDevice) (*servers.Server, error) { |
Joe Topjian | 50cdddf | 2016-09-16 10:56:09 -0600 | [diff] [blame] | 201 | if testing.Short() { |
| 202 | t.Skip("Skipping test that requires server creation in short mode.") |
| 203 | } |
| 204 | |
| 205 | var server *servers.Server |
| 206 | |
Joe Topjian | 66a046c | 2017-01-19 22:07:26 -0700 | [diff] [blame] | 207 | choices, err := clients.AcceptanceTestChoicesFromEnv() |
| 208 | if err != nil { |
| 209 | t.Fatal(err) |
| 210 | } |
| 211 | |
Joe Topjian | 50cdddf | 2016-09-16 10:56:09 -0600 | [diff] [blame] | 212 | 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 Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 247 | // CreateSecurityGroup will create a security group with a random name. |
| 248 | // An error will be returned if one was failed to be created. |
| 249 | func 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. |
| 267 | func 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 Topjian | 66a046c | 2017-01-19 22:07:26 -0700 | [diff] [blame] | 290 | func CreateServer(t *testing.T, client *gophercloud.ServiceClient) (*servers.Server, error) { |
Joe Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 291 | if testing.Short() { |
| 292 | t.Skip("Skipping test that requires server creation in short mode.") |
| 293 | } |
| 294 | |
| 295 | var server *servers.Server |
| 296 | |
Joe Topjian | 66a046c | 2017-01-19 22:07:26 -0700 | [diff] [blame] | 297 | choices, err := clients.AcceptanceTestChoicesFromEnv() |
| 298 | if err != nil { |
| 299 | t.Fatal(err) |
| 300 | } |
| 301 | |
Joe Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 302 | 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 Topjian | f464c96 | 2016-09-12 08:02:43 -0600 | [diff] [blame] | 320 | Metadata: map[string]string{ |
| 321 | "abc": "def", |
| 322 | }, |
Joe Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 323 | 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 Topjian | 50cdddf | 2016-09-16 10:56:09 -0600 | [diff] [blame] | 334 | 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 Topjian | 66a046c | 2017-01-19 22:07:26 -0700 | [diff] [blame] | 346 | func CreateServerWithoutImageRef(t *testing.T, client *gophercloud.ServiceClient) (*servers.Server, error) { |
Joe Topjian | 50cdddf | 2016-09-16 10:56:09 -0600 | [diff] [blame] | 347 | if testing.Short() { |
| 348 | t.Skip("Skipping test that requires server creation in short mode.") |
| 349 | } |
| 350 | |
| 351 | var server *servers.Server |
| 352 | |
Joe Topjian | 66a046c | 2017-01-19 22:07:26 -0700 | [diff] [blame] | 353 | choices, err := clients.AcceptanceTestChoicesFromEnv() |
| 354 | if err != nil { |
| 355 | t.Fatal(err) |
| 356 | } |
| 357 | |
Joe Topjian | 50cdddf | 2016-09-16 10:56:09 -0600 | [diff] [blame] | 358 | 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 Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 387 | 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. |
| 395 | func 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 Topjian | 66a046c | 2017-01-19 22:07:26 -0700 | [diff] [blame] | 410 | func CreateServerInServerGroup(t *testing.T, client *gophercloud.ServiceClient, serverGroup *servergroups.ServerGroup) (*servers.Server, error) { |
Joe Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 411 | if testing.Short() { |
| 412 | t.Skip("Skipping test that requires server creation in short mode.") |
| 413 | } |
| 414 | |
| 415 | var server *servers.Server |
| 416 | |
Joe Topjian | 66a046c | 2017-01-19 22:07:26 -0700 | [diff] [blame] | 417 | choices, err := clients.AcceptanceTestChoicesFromEnv() |
| 418 | if err != nil { |
| 419 | t.Fatal(err) |
| 420 | } |
| 421 | |
Joe Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 422 | 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 Topjian | 66a046c | 2017-01-19 22:07:26 -0700 | [diff] [blame] | 458 | func CreateServerWithPublicKey(t *testing.T, client *gophercloud.ServiceClient, keyPairName string) (*servers.Server, error) { |
Joe Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 459 | if testing.Short() { |
| 460 | t.Skip("Skipping test that requires server creation in short mode.") |
| 461 | } |
| 462 | |
| 463 | var server *servers.Server |
| 464 | |
Joe Topjian | 66a046c | 2017-01-19 22:07:26 -0700 | [diff] [blame] | 465 | choices, err := clients.AcceptanceTestChoicesFromEnv() |
| 466 | if err != nil { |
| 467 | t.Fatal(err) |
| 468 | } |
| 469 | |
Joe Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 470 | 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 Topjian | 50cdddf | 2016-09-16 10:56:09 -0600 | [diff] [blame] | 495 | if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil { |
Joe Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 496 | return server, err |
| 497 | } |
| 498 | |
| 499 | return server, nil |
| 500 | } |
| 501 | |
Gleb | 37b56e8 | 2016-09-06 19:07:58 +0300 | [diff] [blame] | 502 | // CreateVolumeAttachment will attach a volume to a server. An error will be |
Joe Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 503 | // returned if the volume failed to attach. |
| 504 | func 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 Topjian | 50cdddf | 2016-09-16 10:56:09 -0600 | [diff] [blame] | 515 | if err := volumes.WaitForStatus(blockClient, volume.ID, "in-use", 60); err != nil { |
Joe Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 516 | 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. |
| 525 | func 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. |
| 537 | func 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. |
| 549 | func 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. |
| 560 | func 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. |
| 572 | func 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. |
| 584 | func 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. |
| 596 | func 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 |
Gleb | 37b56e8 | 2016-09-06 19:07:58 +0300 | [diff] [blame] | 607 | // as a deferred function. |
Joe Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 608 | func 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 Topjian | 50cdddf | 2016-09-16 10:56:09 -0600 | [diff] [blame] | 615 | if err := volumes.WaitForStatus(blockClient, volumeAttachment.ID, "available", 60); err != nil { |
Joe Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 616 | 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. |
| 624 | func 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. |
| 640 | func 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. |
| 667 | func 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. |
| 689 | func 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 Topjian | 66a046c | 2017-01-19 22:07:26 -0700 | [diff] [blame] | 709 | func 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 Topjian | 1c15e3f | 2016-08-08 10:48:38 -0600 | [diff] [blame] | 715 | 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. |
| 731 | func 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 | |
dbaumgarten | c2bb491 | 2017-01-19 17:14:08 +0100 | [diff] [blame] | 751 | //Convenience method to fill an QuotaSet-UpdateOpts-struct from a QuotaSet-struct |
Joe Topjian | 66a046c | 2017-01-19 22:07:26 -0700 | [diff] [blame] | 752 | func FillUpdateOptsFromQuotaSet(src quotasets.QuotaSet, dest *quotasets.UpdateOpts) { |
dbaumgarten | c2bb491 | 2017-01-19 17:14:08 +0100 | [diff] [blame] | 753 | 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 | } |