Merge pull request #17 from jrperritt/5
Use EndpointOpts to query ServiceCatalog for endpoint for Identity service clients
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 0d85ca8..e9d5560 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -14,6 +14,10 @@
[README](/README.md#how-to-install) but add `-tags "fixtures acceptance"` to
get dependencies for unit and acceptance tests.
+ ```bash
+ go get -tags "fixtures acceptance" github.com/gophercloud/gophercloud
+ ```
+
2. Move into the directory that houses your local repository:
```bash
@@ -26,7 +30,7 @@
```bash
git remote rename origin upstream
- git remote add origin git@github.com/<my_username>/gophercloud
+ git remote add origin git@github.com:<my_username>/gophercloud.git
```
4. Checkout the latest development branch:
diff --git a/MIGRATING.md b/MIGRATING.md
index 8b13789..62bdf8c 100644
--- a/MIGRATING.md
+++ b/MIGRATING.md
@@ -1 +1,23 @@
+# Compute
+## Floating IPs
+
+* `github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingip` is now `github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips`
+* `floatingips.Associate` and `floatingips.Disassociate` have been removed.
+* `floatingips.DisassociateOpts` is now required to disassociate a Floating IP.
+
+## Security Groups
+
+* `secgroups.AddServerToGroup` is now `secgroups.AddServer`.
+* `secgroups.RemoveServerFromGroup` is now `secgroups.RemoveServer`.
+
+## Servers
+
+* `servers.Reboot` now requires a `servers.RebootOpts` struct:
+
+ ```golang
+ rebootOpts := &servers.RebootOpts{
+ Type: servers.SoftReboot,
+ }
+ res := servers.Reboot(client, server.ID, rebootOpts)
+ ```
diff --git a/acceptance/README.md b/acceptance/README.md
index 3199837..d969e03 100644
--- a/acceptance/README.md
+++ b/acceptance/README.md
@@ -28,15 +28,12 @@
|`OS_AUTH_URL`|The identity URL you need to authenticate|
|`OS_TENANT_NAME`|Your API tenant name|
|`OS_TENANT_ID`|Your API tenant ID|
-|`RS_USERNAME`|Your Rackspace username|
-|`RS_API_KEY`|Your Rackspace API key|
#### General
|Name|Description|
|---|---|
|`OS_REGION_NAME`|The region you want your resources to reside in|
-|`RS_REGION`|Rackspace region you want your resource to reside in|
#### Compute
@@ -45,8 +42,8 @@
|`OS_IMAGE_ID`|The ID of the image your want your server to be based on|
|`OS_FLAVOR_ID`|The ID of the flavor you want your server to be based on|
|`OS_FLAVOR_ID_RESIZE`|The ID of the flavor you want your server to be resized to|
-|`RS_IMAGE_ID`|The ID of the image you want servers to be created with|
-|`RS_FLAVOR_ID`|The ID of the flavor you want your server to be created with|
+|`OS_POOL_NAME`|The Pool from where to obtain Floating IPs|
+|`OS_NETWORK_NAME`|The network to launch instances on|
### 2. Run the test suite
@@ -55,3 +52,30 @@
```
./script/acceptancetest
```
+
+Alternatively, add the following to your `.bashrc`:
+
+```bash
+gophercloudtest() {
+ if [[ -n $1 ]] && [[ -n $2 ]]; then
+ pushd ~/go/src/github.com/gophercloud/gophercloud
+ go test -v -tags "fixtures acceptance" -run "$1" github.com/gophercloud/gophercloud/acceptance/openstack/$2 | tee ~/gophercloud.log
+ popd
+fi
+}
+```
+
+Then run either groups or individual tests by doing:
+
+```shell
+$ gophercloudtest TestFlavorsList compute/v2
+$ gophercloudtest TestFlavors compute/v2
+$ gophercloudtest Test compute/v2
+```
+
+### 3. Notes
+
+#### Compute Tests
+
+* In order to run the `TestBootFromVolumeMultiEphemeral` test, a flavor with ephemeral disk space must be used.
+* The `TestDefSecRules` tests require a compatible network driver and admin privileges.
diff --git a/acceptance/openstack/compute/v2/bootfromvolume_test.go b/acceptance/openstack/compute/v2/bootfromvolume_test.go
index d1607be..6b73347 100644
--- a/acceptance/openstack/compute/v2/bootfromvolume_test.go
+++ b/acceptance/openstack/compute/v2/bootfromvolume_test.go
@@ -1,33 +1,32 @@
-// +build acceptance
+// +build acceptance compute bootfromvolume
package v2
import (
"testing"
+ "github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/bootfromvolume"
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
- th "github.com/gophercloud/gophercloud/testhelper"
)
-func TestBootFromVolume(t *testing.T) {
- client, err := newClient()
- th.AssertNoErr(t, err)
-
+func TestBootFromVolumeSingleVolume(t *testing.T) {
if testing.Short() {
t.Skip("Skipping test that requires server creation in short mode.")
}
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+
choices, err := ComputeChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
- name := tools.RandomString("Gophercloud-", 8)
- t.Logf("Creating server [%s].", name)
-
- bd := []bootfromvolume.BlockDevice{
+ blockDevices := []bootfromvolume.BlockDevice{
bootfromvolume.BlockDevice{
UUID: choices.ImageID,
SourceType: bootfromvolume.Image,
@@ -35,48 +34,41 @@
},
}
- serverCreateOpts := servers.CreateOpts{
- Name: name,
- FlavorRef: choices.FlavorID,
- ImageRef: choices.ImageID,
+ server, err := createBootableVolumeServer(t, client, blockDevices, choices)
+ if err != nil {
+ t.Fatal("Unable to create server: %v", err)
}
- server, err := bootfromvolume.Create(client, bootfromvolume.CreateOptsExt{
- serverCreateOpts,
- bd,
- }).Extract()
- th.AssertNoErr(t, err)
if err = waitForStatus(client, server, "ACTIVE"); err != nil {
- t.Fatal(err)
+ t.Fatal("Unable to wait for server: %v", err)
}
+ defer deleteServer(t, client, server)
- t.Logf("Created server: %+v\n", server)
- defer servers.Delete(client, server.ID)
- t.Logf("Deleting server [%s]...", name)
+ printServer(t, server)
}
-func TestMultiEphemeral(t *testing.T) {
- client, err := newClient()
- th.AssertNoErr(t, err)
-
+func TestBootFromVolumeMultiEphemeral(t *testing.T) {
if testing.Short() {
t.Skip("Skipping test that requires server creation in short mode.")
}
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+
choices, err := ComputeChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
- name := tools.RandomString("Gophercloud-", 8)
- t.Logf("Creating server [%s].", name)
-
- bd := []bootfromvolume.BlockDevice{
+ blockDevices := []bootfromvolume.BlockDevice{
bootfromvolume.BlockDevice{
BootIndex: 0,
UUID: choices.ImageID,
SourceType: bootfromvolume.Image,
DestinationType: "local",
DeleteOnTermination: true,
+ VolumeSize: 5,
},
bootfromvolume.BlockDevice{
BootIndex: -1,
@@ -96,21 +88,48 @@
},
}
+ server, err := createBootableVolumeServer(t, client, blockDevices, choices)
+ if err != nil {
+ t.Fatalf("Unable to create server: %v", err)
+ }
+ if err = waitForStatus(client, server, "ACTIVE"); err != nil {
+ t.Fatalf("Unable to wait for server: %v", err)
+ }
+ defer deleteServer(t, client, server)
+
+ printServer(t, server)
+}
+
+func createBootableVolumeServer(t *testing.T, client *gophercloud.ServiceClient, blockDevices []bootfromvolume.BlockDevice, choices *ComputeChoices) (*servers.Server, error) {
+ if testing.Short() {
+ t.Skip("Skipping test that requires server creation in short mode.")
+ }
+
+ networkID, err := getNetworkIDFromTenantNetworks(t, client, choices.NetworkName)
+ if err != nil {
+ t.Fatalf("Failed to obtain network ID: %v", err)
+ }
+
+ name := tools.RandomString("ACPTTEST", 16)
+ t.Logf("Attempting to create bootable volume server: %s", name)
+
serverCreateOpts := servers.CreateOpts{
Name: name,
FlavorRef: choices.FlavorID,
ImageRef: choices.ImageID,
- }
- server, err := bootfromvolume.Create(client, bootfromvolume.CreateOptsExt{
- serverCreateOpts,
- bd,
- }).Extract()
- th.AssertNoErr(t, err)
- if err = waitForStatus(client, server, "ACTIVE"); err != nil {
- t.Fatal(err)
+ Networks: []servers.Network{
+ servers.Network{UUID: networkID},
+ },
}
- t.Logf("Created server: %+v\n", server)
- defer servers.Delete(client, server.ID)
- t.Logf("Deleting server [%s]...", name)
+ server, err := bootfromvolume.Create(client, bootfromvolume.CreateOptsExt{
+ serverCreateOpts,
+ blockDevices,
+ }).Extract()
+
+ if err != nil {
+ return server, err
+ }
+
+ return server, nil
}
diff --git a/acceptance/openstack/compute/v2/compute_test.go b/acceptance/openstack/compute/v2/compute_test.go
index 83b0e35..b76cfd1 100644
--- a/acceptance/openstack/compute/v2/compute_test.go
+++ b/acceptance/openstack/compute/v2/compute_test.go
@@ -29,6 +29,38 @@
})
}
+func newIdentityClient() (*gophercloud.ServiceClient, error) {
+ ao, err := openstack.AuthOptionsFromEnv()
+ if err != nil {
+ return nil, err
+ }
+
+ client, err := openstack.AuthenticatedClient(ao)
+ if err != nil {
+ return nil, err
+ }
+
+ return openstack.NewIdentityV2(client, gophercloud.EndpointOpts{
+ Region: os.Getenv("OS_REGION_NAME"),
+ })
+}
+
+func newBlockClient() (*gophercloud.ServiceClient, error) {
+ ao, err := openstack.AuthOptionsFromEnv()
+ if err != nil {
+ return nil, err
+ }
+
+ client, err := openstack.AuthenticatedClient(ao)
+ if err != nil {
+ return nil, err
+ }
+
+ return openstack.NewBlockStorageV1(client, gophercloud.EndpointOpts{
+ Region: os.Getenv("OS_REGION_NAME"),
+ })
+}
+
func waitForStatus(client *gophercloud.ServiceClient, server *servers.Server, status string) error {
return tools.WaitFor(func() (bool, error) {
latest, err := servers.Get(client, server.ID).Extract()
@@ -41,6 +73,10 @@
return true, nil
}
+ if latest.Status == "ERROR" {
+ return false, fmt.Errorf("Instance in ERROR state")
+ }
+
return false, nil
})
}
@@ -57,6 +93,9 @@
// from FlavorID.
FlavorIDResize string
+ // FloatingIPPool contains the name of the pool from where to obtain floating IPs.
+ FloatingIPPoolName string
+
// NetworkName is the name of a network to launch the instance on.
NetworkName string
}
@@ -68,6 +107,7 @@
flavorID := os.Getenv("OS_FLAVOR_ID")
flavorIDResize := os.Getenv("OS_FLAVOR_ID_RESIZE")
networkName := os.Getenv("OS_NETWORK_NAME")
+ floatingIPPoolName := os.Getenv("OS_POOL_NAME")
missing := make([]string, 0, 3)
if imageID == "" {
@@ -79,8 +119,11 @@
if flavorIDResize == "" {
missing = append(missing, "OS_FLAVOR_ID_RESIZE")
}
+ if floatingIPPoolName == "" {
+ missing = append(missing, "OS_POOL_NAME")
+ }
if networkName == "" {
- networkName = "public"
+ networkName = "private"
}
notDistinct := ""
@@ -100,5 +143,5 @@
return nil, fmt.Errorf(text)
}
- return &ComputeChoices{ImageID: imageID, FlavorID: flavorID, FlavorIDResize: flavorIDResize, NetworkName: networkName}, nil
+ return &ComputeChoices{ImageID: imageID, FlavorID: flavorID, FlavorIDResize: flavorIDResize, FloatingIPPoolName: floatingIPPoolName, NetworkName: networkName}, nil
}
diff --git a/acceptance/openstack/compute/v2/defsecrules_test.go b/acceptance/openstack/compute/v2/defsecrules_test.go
new file mode 100644
index 0000000..dbee8e3
--- /dev/null
+++ b/acceptance/openstack/compute/v2/defsecrules_test.go
@@ -0,0 +1,105 @@
+// +build acceptance compute defsecrules
+
+package v2
+
+import (
+ "testing"
+
+ "github.com/gophercloud/gophercloud"
+ "github.com/gophercloud/gophercloud/acceptance/tools"
+ dsr "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/defsecrules"
+)
+
+func TestDefSecRulesList(t *testing.T) {
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+
+ allPages, err := dsr.List(client).AllPages()
+ if err != nil {
+ t.Fatalf("Unable to list default rules: %v", err)
+ }
+
+ allDefaultRules, err := dsr.ExtractDefaultRules(allPages)
+ if err != nil {
+ t.Fatalf("Unable to extract default rules: %v", err)
+ }
+
+ for _, defaultRule := range allDefaultRules {
+ printDefaultRule(t, &defaultRule)
+ }
+}
+
+func TestDefSecRulesCreate(t *testing.T) {
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+
+ defaultRule, err := createDefaultRule(t, client)
+ if err != nil {
+ t.Fatalf("Unable to create default rule: %v", err)
+ }
+ defer deleteDefaultRule(t, client, defaultRule)
+
+ printDefaultRule(t, &defaultRule)
+}
+
+func TestDefSecRulesGet(t *testing.T) {
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+
+ defaultRule, err := createDefaultRule(t, client)
+ if err != nil {
+ t.Fatalf("Unable to create default rule: %v", err)
+ }
+ defer deleteDefaultRule(t, client, defaultRule)
+
+ newDefaultRule, err := dsr.Get(client, defaultRule.ID).Extract()
+ if err != nil {
+ t.Fatalf("Unable to get default rule %s: %v", defaultRule.ID, err)
+ }
+
+ printDefaultRule(t, newDefaultRule)
+}
+
+func createDefaultRule(t *testing.T, client *gophercloud.ServiceClient) (dsr.DefaultRule, error) {
+ createOpts := dsr.CreateOpts{
+ FromPort: tools.RandomInt(80, 89),
+ ToPort: tools.RandomInt(90, 99),
+ IPProtocol: "TCP",
+ CIDR: "0.0.0.0/0",
+ }
+
+ defaultRule, err := dsr.Create(client, createOpts).Extract()
+ if err != nil {
+ return *defaultRule, err
+ }
+
+ t.Logf("Created default rule: %s", defaultRule.ID)
+
+ return *defaultRule, nil
+}
+
+func deleteDefaultRule(t *testing.T, client *gophercloud.ServiceClient, defaultRule dsr.DefaultRule) {
+ err := dsr.Delete(client, defaultRule.ID).ExtractErr()
+ if err != nil {
+ t.Fatalf("Unable to delete default rule %s: %v", defaultRule.ID, err)
+ }
+
+ t.Logf("Deleted default rule: %s", defaultRule.ID)
+}
+
+func printDefaultRule(t *testing.T, defaultRule *dsr.DefaultRule) {
+ t.Logf("\tID: %s", defaultRule.ID)
+ t.Logf("\tFrom Port: %d", defaultRule.FromPort)
+ t.Logf("\tTo Port: %d", defaultRule.ToPort)
+ t.Logf("\tIP Protocol: %s", defaultRule.IPProtocol)
+ t.Logf("\tIP Range: %s", defaultRule.IPRange.CIDR)
+ t.Logf("\tParent Group ID: %s", defaultRule.ParentGroupID)
+ t.Logf("\tGroup Tenant ID: %s", defaultRule.Group.TenantID)
+ t.Logf("\tGroup Name: %s", defaultRule.Group.Name)
+}
diff --git a/acceptance/openstack/compute/v2/extension_test.go b/acceptance/openstack/compute/v2/extension_test.go
index 0fd6fec..626797a 100644
--- a/acceptance/openstack/compute/v2/extension_test.go
+++ b/acceptance/openstack/compute/v2/extension_test.go
@@ -1,47 +1,53 @@
-// +build acceptance compute extensionss
+// +build acceptance compute extensions
package v2
import (
"testing"
- "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions"
- "github.com/gophercloud/gophercloud/pagination"
- th "github.com/gophercloud/gophercloud/testhelper"
+ "github.com/gophercloud/gophercloud/openstack/common/extensions"
)
-func TestListExtensions(t *testing.T) {
+func TestExtensionsList(t *testing.T) {
client, err := newClient()
- th.AssertNoErr(t, err)
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
- err = extensions.List(client).EachPage(func(page pagination.Page) (bool, error) {
- t.Logf("--- Page ---")
+ allPages, err := extensions.List(client).AllPages()
+ if err != nil {
+ t.Fatalf("Unable to list extensions: %v", err)
+ }
- exts, err := extensions.ExtractExtensions(page)
- th.AssertNoErr(t, err)
+ allExtensions, err := extensions.ExtractExtensions(allPages)
+ if err != nil {
+ t.Fatalf("Unable to extract extensions: %v", err)
+ }
- for i, ext := range exts {
- t.Logf("[%02d] name=[%s]\n", i, ext.Name)
- t.Logf(" alias=[%s]\n", ext.Alias)
- t.Logf(" description=[%s]\n", ext.Description)
- }
-
- return true, nil
- })
- th.AssertNoErr(t, err)
+ for _, extension := range allExtensions {
+ printExtension(t, &extension)
+ }
}
-func TestGetExtension(t *testing.T) {
+func TestExtensionGet(t *testing.T) {
client, err := newClient()
- th.AssertNoErr(t, err)
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
- ext, err := extensions.Get(client, "os-admin-actions").Extract()
- th.AssertNoErr(t, err)
+ extension, err := extensions.Get(client, "os-admin-actions").Extract()
+ if err != nil {
+ t.Fatalf("Unable to get extension os-admin-actions: %v", err)
+ }
- t.Logf("Extension details:")
- t.Logf(" name=[%s]\n", ext.Name)
- t.Logf(" namespace=[%s]\n", ext.Namespace)
- t.Logf(" alias=[%s]\n", ext.Alias)
- t.Logf(" description=[%s]\n", ext.Description)
- t.Logf(" updated=[%s]\n", ext.Updated)
+ printExtension(t, extension)
+}
+
+func printExtension(t *testing.T, extension *extensions.Extension) {
+ t.Logf("Name: %s", extension.Name)
+ t.Logf("Namespace: %s", extension.Namespace)
+ t.Logf("Alias: %s", extension.Alias)
+ t.Logf("Description: %s", extension.Description)
+ t.Logf("Updated: %s", extension.Updated)
+ t.Logf("Links: %v", extension.Links)
}
diff --git a/acceptance/openstack/compute/v2/flavors_test.go b/acceptance/openstack/compute/v2/flavors_test.go
index 365281d..bc6a2c7 100644
--- a/acceptance/openstack/compute/v2/flavors_test.go
+++ b/acceptance/openstack/compute/v2/flavors_test.go
@@ -6,38 +6,30 @@
"testing"
"github.com/gophercloud/gophercloud/openstack/compute/v2/flavors"
- "github.com/gophercloud/gophercloud/pagination"
)
-func TestListFlavors(t *testing.T) {
+func TestFlavorsList(t *testing.T) {
client, err := newClient()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
- t.Logf("ID\tRegion\tName\tStatus\tCreated")
+ allPages, err := flavors.ListDetail(client, nil).AllPages()
+ if err != nil {
+ t.Fatalf("Unable to retrieve flavors: %v", err)
+ }
- pager := flavors.ListDetail(client, nil)
- count, pages := 0, 0
- pager.EachPage(func(page pagination.Page) (bool, error) {
- t.Logf("---")
- pages++
- flavors, err := flavors.ExtractFlavors(page)
- if err != nil {
- return false, err
- }
+ allFlavors, err := flavors.ExtractFlavors(allPages)
+ if err != nil {
+ t.Fatalf("Unable to extract flavor results: %v", err)
+ }
- for _, f := range flavors {
- t.Logf("%s\t%s\t%d\t%d\t%d", f.ID, f.Name, f.RAM, f.Disk, f.VCPUs)
- }
-
- return true, nil
- })
-
- t.Logf("--------\n%d flavors listed on %d pages.", count, pages)
+ for _, flavor := range allFlavors {
+ printFlavor(t, &flavor)
+ }
}
-func TestGetFlavor(t *testing.T) {
+func TestFlavorsGet(t *testing.T) {
client, err := newClient()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
@@ -53,5 +45,14 @@
t.Fatalf("Unable to get flavor information: %v", err)
}
- t.Logf("Flavor: %#v", flavor)
+ printFlavor(t, flavor)
+}
+
+func printFlavor(t *testing.T, flavor *flavors.Flavor) {
+ t.Logf("ID: %s", flavor.ID)
+ t.Logf("Name: %s", flavor.Name)
+ t.Logf("RAM: %d", flavor.RAM)
+ t.Logf("Disk: %d", flavor.Disk)
+ t.Logf("Swap: %d", flavor.Swap)
+ t.Logf("RxTxFactor: %f", flavor.RxTxFactor)
}
diff --git a/acceptance/openstack/compute/v2/floatingip_test.go b/acceptance/openstack/compute/v2/floatingip_test.go
index 8231bd6..92249c7 100644
--- a/acceptance/openstack/compute/v2/floatingip_test.go
+++ b/acceptance/openstack/compute/v2/floatingip_test.go
@@ -3,97 +3,147 @@
package v2
import (
- "os"
"testing"
"github.com/gophercloud/gophercloud"
- "github.com/gophercloud/gophercloud/acceptance/tools"
- "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingip"
+ "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips"
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
- th "github.com/gophercloud/gophercloud/testhelper"
)
-func createFIPServer(t *testing.T, client *gophercloud.ServiceClient, choices *ComputeChoices) (*servers.Server, error) {
+func TestFloatingIPsList(t *testing.T) {
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+
+ allPages, err := floatingips.List(client).AllPages()
+ if err != nil {
+ t.Fatalf("Unable to retrieve floating IPs: %v", err)
+ }
+
+ allFloatingIPs, err := floatingips.ExtractFloatingIPs(allPages)
+ if err != nil {
+ t.Fatalf("Unable to extract floating IPs: %v", err)
+ }
+
+ for _, floatingIP := range allFloatingIPs {
+ printFloatingIP(t, &floatingIP)
+ }
+}
+
+func TestFloatingIPsCreate(t *testing.T) {
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+
+ choices, err := ComputeChoicesFromEnv()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ floatingIP, err := createFloatingIP(t, client, choices)
+ if err != nil {
+ t.Fatalf("Unable to create floating IP: %v", err)
+ }
+ defer deleteFloatingIP(t, client, floatingIP)
+
+ printFloatingIP(t, floatingIP)
+}
+
+func TestFloatingIPsAssociate(t *testing.T) {
if testing.Short() {
t.Skip("Skipping test that requires server creation in short mode.")
}
- name := tools.RandomString("ACPTTEST", 16)
- t.Logf("Attempting to create server: %s\n", name)
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
- pwd := tools.MakeNewPassword("")
+ choices, err := ComputeChoicesFromEnv()
+ if err != nil {
+ t.Fatal(err)
+ }
- server, err := servers.Create(client, servers.CreateOpts{
- Name: name,
- FlavorRef: choices.FlavorID,
- ImageRef: choices.ImageID,
- AdminPass: pwd,
- }).Extract()
+ server, err := createServer(t, client, choices)
if err != nil {
t.Fatalf("Unable to create server: %v", err)
}
- th.AssertEquals(t, pwd, server.AdminPass)
-
- return server, err
-}
-
-func createFloatingIP(t *testing.T, client *gophercloud.ServiceClient) (*floatingip.FloatingIP, error) {
- pool := os.Getenv("OS_POOL_NAME")
- fip, err := floatingip.Create(client, &floatingip.CreateOpts{
- Pool: pool,
- }).Extract()
- th.AssertNoErr(t, err)
- t.Logf("Obtained Floating IP: %v", fip.IP)
-
- return fip, err
-}
-
-func associateFloatingIPDeprecated(t *testing.T, client *gophercloud.ServiceClient, serverId string, fip *floatingip.FloatingIP) {
- // This form works, but is considered deprecated.
- // See associateFloatingIP or associateFloatingIPFixed
- err := floatingip.Associate(client, serverId, fip.IP).ExtractErr()
- th.AssertNoErr(t, err)
- t.Logf("Associated floating IP %v from instance %v", fip.IP, serverId)
- defer func() {
- err = floatingip.Disassociate(client, serverId, fip.IP).ExtractErr()
- th.AssertNoErr(t, err)
- t.Logf("Disassociated floating IP %v from instance %v", fip.IP, serverId)
- }()
- floatingIp, err := floatingip.Get(client, fip.ID).Extract()
- th.AssertNoErr(t, err)
- t.Logf("Floating IP %v is associated with Fixed IP %v", fip.IP, floatingIp.FixedIP)
-}
-
-func associateFloatingIP(t *testing.T, client *gophercloud.ServiceClient, serverId string, fip *floatingip.FloatingIP) {
- associateOpts := floatingip.AssociateOpts{
- ServerID: serverId,
- FloatingIP: fip.IP,
+ if err = waitForStatus(client, server, "ACTIVE"); err != nil {
+ t.Fatalf("Unable to wait for server: %v", err)
}
+ defer deleteServer(t, client, server)
- err := floatingip.AssociateInstance(client, associateOpts).ExtractErr()
- th.AssertNoErr(t, err)
- t.Logf("Associated floating IP %v from instance %v", fip.IP, serverId)
- defer func() {
- err = floatingip.DisassociateInstance(client, associateOpts).ExtractErr()
- th.AssertNoErr(t, err)
- t.Logf("Disassociated floating IP %v from instance %v", fip.IP, serverId)
- }()
- floatingIp, err := floatingip.Get(client, fip.ID).Extract()
- th.AssertNoErr(t, err)
- t.Logf("Floating IP %v is associated with Fixed IP %v", fip.IP, floatingIp.FixedIP)
-}
-
-func associateFloatingIPFixed(t *testing.T, client *gophercloud.ServiceClient, serverId string, fip *floatingip.FloatingIP) {
-
- network := os.Getenv("OS_NETWORK_NAME")
- server, err := servers.Get(client, serverId).Extract()
+ floatingIP, err := createFloatingIP(t, client, choices)
if err != nil {
- t.Fatalf("%s", err)
+ t.Fatalf("Unable to create floating IP: %v", err)
}
+ defer deleteFloatingIP(t, client, floatingIP)
+
+ printFloatingIP(t, floatingIP)
+
+ associateOpts := floatingips.AssociateOpts{
+ FloatingIP: floatingIP.IP,
+ }
+
+ t.Logf("Attempting to associate floating IP %s to instance %s", floatingIP.IP, server.ID)
+ err = floatingips.AssociateInstance(client, server.ID, associateOpts).ExtractErr()
+ if err != nil {
+ t.Fatalf("Unable to associate floating IP %s with server %s: %v", floatingIP.IP, server.ID, err)
+ }
+ defer disassociateFloatingIP(t, client, floatingIP, server)
+ t.Logf("Floating IP %s is associated with Fixed IP %s", floatingIP.IP, floatingIP.FixedIP)
+
+ newFloatingIP, err := floatingips.Get(client, floatingIP.ID).Extract()
+ if err != nil {
+ t.Fatalf("Unable to get floating IP %s: %v", floatingIP.ID, err)
+ }
+
+ printFloatingIP(t, newFloatingIP)
+}
+
+func TestFloatingIPsFixedIPAssociate(t *testing.T) {
+ if testing.Short() {
+ t.Skip("Skipping test that requires server creation in short mode.")
+ }
+
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+
+ choices, err := ComputeChoicesFromEnv()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ server, err := createServer(t, client, choices)
+ if err != nil {
+ t.Fatalf("Unable to create server: %v", err)
+ }
+
+ if err = waitForStatus(client, server, "ACTIVE"); err != nil {
+ t.Fatalf("Unable to wait for server: %v", err)
+ }
+ defer deleteServer(t, client, server)
+
+ newServer, err := servers.Get(client, server.ID).Extract()
+ if err != nil {
+ t.Fatalf("Unable to get server %s: %v", server.ID, err)
+ }
+
+ floatingIP, err := createFloatingIP(t, client, choices)
+ if err != nil {
+ t.Fatalf("Unable to create floating IP: %v", err)
+ }
+ defer deleteFloatingIP(t, client, floatingIP)
+
+ printFloatingIP(t, floatingIP)
var fixedIP string
- for _, networkAddresses := range server.Addresses[network].([]interface{}) {
+ for _, networkAddresses := range newServer.Addresses[choices.NetworkName].([]interface{}) {
address := networkAddresses.(map[string]interface{})
if address["OS-EXT-IPS:type"] == "fixed" {
if address["version"].(float64) == 4 {
@@ -102,67 +152,66 @@
}
}
- associateOpts := floatingip.AssociateOpts{
- ServerID: serverId,
- FloatingIP: fip.IP,
+ associateOpts := floatingips.AssociateOpts{
+ FloatingIP: floatingIP.IP,
FixedIP: fixedIP,
}
- err = floatingip.AssociateInstance(client, associateOpts).ExtractErr()
- th.AssertNoErr(t, err)
- t.Logf("Associated floating IP %v from instance %v with Fixed IP %v", fip.IP, serverId, fixedIP)
- defer func() {
- err = floatingip.DisassociateInstance(client, associateOpts).ExtractErr()
- th.AssertNoErr(t, err)
- t.Logf("Disassociated floating IP %v from instance %v with Fixed IP %v", fip.IP, serverId, fixedIP)
- }()
- floatingIp, err := floatingip.Get(client, fip.ID).Extract()
- th.AssertNoErr(t, err)
- th.AssertEquals(t, floatingIp.FixedIP, fixedIP)
- t.Logf("Floating IP %v is associated with Fixed IP %v", fip.IP, floatingIp.FixedIP)
+ t.Logf("Attempting to associate floating IP %s to instance %s", floatingIP.IP, newServer.ID)
+ err = floatingips.AssociateInstance(client, newServer.ID, associateOpts).ExtractErr()
+ if err != nil {
+ t.Fatalf("Unable to associate floating IP %s with server %s: %v", floatingIP.IP, newServer.ID, err)
+ }
+ defer disassociateFloatingIP(t, client, floatingIP, newServer)
+ t.Logf("Floating IP %s is associated with Fixed IP %s", floatingIP.IP, floatingIP.FixedIP)
+
+ newFloatingIP, err := floatingips.Get(client, floatingIP.ID).Extract()
+ if err != nil {
+ t.Fatalf("Unable to get floating IP %s: %v", floatingIP.ID, err)
+ }
+
+ printFloatingIP(t, newFloatingIP)
}
-func TestFloatingIP(t *testing.T) {
- pool := os.Getenv("OS_POOL_NAME")
- if pool == "" {
- t.Fatalf("OS_POOL_NAME must be set")
+func createFloatingIP(t *testing.T, client *gophercloud.ServiceClient, choices *ComputeChoices) (*floatingips.FloatingIP, error) {
+ createOpts := floatingips.CreateOpts{
+ Pool: choices.FloatingIPPoolName,
}
-
- choices, err := ComputeChoicesFromEnv()
+ floatingIP, err := floatingips.Create(client, createOpts).Extract()
if err != nil {
- t.Fatal(err)
+ return floatingIP, err
}
- client, err := newClient()
+ t.Logf("Created floating IP: %s", floatingIP.ID)
+ return floatingIP, nil
+}
+
+func deleteFloatingIP(t *testing.T, client *gophercloud.ServiceClient, floatingIP *floatingips.FloatingIP) {
+ err := floatingips.Delete(client, floatingIP.ID).ExtractErr()
if err != nil {
- t.Fatalf("Unable to create a compute client: %v", err)
+ t.Fatalf("Unable to delete floating IP %s: %v", floatingIP.ID, err)
}
- server, err := createFIPServer(t, client, choices)
+ t.Logf("Deleted floating IP: %s", floatingIP.ID)
+}
+
+func disassociateFloatingIP(t *testing.T, client *gophercloud.ServiceClient, floatingIP *floatingips.FloatingIP, server *servers.Server) {
+ disassociateOpts := floatingips.DisassociateOpts{
+ FloatingIP: floatingIP.IP,
+ }
+
+ err := floatingips.DisassociateInstance(client, server.ID, disassociateOpts).ExtractErr()
if err != nil {
- t.Fatalf("Unable to create server: %v", err)
- }
- defer func() {
- servers.Delete(client, server.ID)
- t.Logf("Server deleted.")
- }()
-
- if err = waitForStatus(client, server, "ACTIVE"); err != nil {
- t.Fatalf("Unable to wait for server: %v", err)
+ t.Fatalf("Unable to disassociate floating IP %s from server %s: %v", floatingIP.IP, server.ID, err)
}
- fip, err := createFloatingIP(t, client)
- if err != nil {
- t.Fatalf("Unable to create floating IP: %v", err)
- }
- defer func() {
- err = floatingip.Delete(client, fip.ID).ExtractErr()
- th.AssertNoErr(t, err)
- t.Logf("Floating IP deleted.")
- }()
+ t.Logf("Disassociated floating IP %s from server %s", floatingIP.IP, server.ID)
+}
- associateFloatingIPDeprecated(t, client, server.ID, fip)
- associateFloatingIP(t, client, server.ID, fip)
- associateFloatingIPFixed(t, client, server.ID, fip)
-
+func printFloatingIP(t *testing.T, floatingIP *floatingips.FloatingIP) {
+ t.Logf("ID: %s", floatingIP.ID)
+ t.Logf("Fixed IP: %s", floatingIP.FixedIP)
+ t.Logf("Instance ID: %s", floatingIP.InstanceID)
+ t.Logf("IP: %s", floatingIP.IP)
+ t.Logf("Pool: %s", floatingIP.Pool)
}
diff --git a/acceptance/openstack/compute/v2/images_test.go b/acceptance/openstack/compute/v2/images_test.go
index 9b740ad..15af9af 100644
--- a/acceptance/openstack/compute/v2/images_test.go
+++ b/acceptance/openstack/compute/v2/images_test.go
@@ -6,32 +6,56 @@
"testing"
"github.com/gophercloud/gophercloud/openstack/compute/v2/images"
- "github.com/gophercloud/gophercloud/pagination"
)
-func TestListImages(t *testing.T) {
+func TestImagesList(t *testing.T) {
client, err := newClient()
if err != nil {
t.Fatalf("Unable to create a compute: client: %v", err)
}
- t.Logf("ID\tRegion\tName\tStatus\tCreated")
+ allPages, err := images.ListDetail(client, nil).AllPages()
+ if err != nil {
+ t.Fatalf("Unable to retrieve images: %v", err)
+ }
- pager := images.ListDetail(client, nil)
- count, pages := 0, 0
- pager.EachPage(func(page pagination.Page) (bool, error) {
- pages++
- images, err := images.ExtractImages(page)
- if err != nil {
- return false, err
- }
+ allImages, err := images.ExtractImages(allPages)
+ if err != nil {
+ t.Fatalf("Unable to extract image results: %v", err)
+ }
- for _, i := range images {
- t.Logf("%s\t%s\t%s\t%s", i.ID, i.Name, i.Status, i.Created)
- }
+ for _, image := range allImages {
+ printImage(t, image)
+ }
+}
- return true, nil
- })
+func TestImagesGet(t *testing.T) {
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute: client: %v", err)
+ }
- t.Logf("--------\n%d images listed on %d pages.", count, pages)
+ choices, err := ComputeChoicesFromEnv()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ image, err := images.Get(client, choices.ImageID).Extract()
+ if err != nil {
+ t.Fatalf("Unable to get image information: %v", err)
+ }
+
+ printImage(t, *image)
+}
+
+func printImage(t *testing.T, image images.Image) {
+ t.Logf("ID: %s", image.ID)
+ t.Logf("Name: %s", image.Name)
+ t.Logf("MinDisk: %d", image.MinDisk)
+ t.Logf("MinRAM: %d", image.MinRAM)
+ t.Logf("Status: %s", image.Status)
+ t.Logf("Progress: %d", image.Progress)
+ t.Logf("Metadata: %#v", image.Metadata)
+ t.Logf("Created: %s", image.Created)
+ t.Logf("Updated: %s", image.Updated)
}
diff --git a/acceptance/openstack/compute/v2/keypairs_test.go b/acceptance/openstack/compute/v2/keypairs_test.go
index 326c6f9..b9fb57a 100644
--- a/acceptance/openstack/compute/v2/keypairs_test.go
+++ b/acceptance/openstack/compute/v2/keypairs_test.go
@@ -1,4 +1,4 @@
-// +build acceptance
+// +build acceptance compute keypairs
package v2
@@ -7,68 +7,180 @@
"crypto/rsa"
"testing"
+ "github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs"
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
- th "github.com/gophercloud/gophercloud/testhelper"
"golang.org/x/crypto/ssh"
)
const keyName = "gophercloud_test_key_pair"
-func TestCreateServerWithKeyPair(t *testing.T) {
+func TestKeypairsList(t *testing.T) {
client, err := newClient()
- th.AssertNoErr(t, err)
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+ allPages, err := keypairs.List(client).AllPages()
+ if err != nil {
+ t.Fatalf("Unable to retrieve keypairs: %s", err)
+ }
+
+ allKeys, err := keypairs.ExtractKeyPairs(allPages)
+ if err != nil {
+ t.Fatalf("Unable to extract keypairs results: %s", err)
+ }
+
+ for _, keypair := range allKeys {
+ printKeyPair(t, &keypair)
+ }
+}
+
+func TestKeypairsCreate(t *testing.T) {
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+
+ createOpts := keypairs.CreateOpts{
+ Name: keyName,
+ }
+ keyPair, err := keypairs.Create(client, createOpts).Extract()
+ if err != nil {
+ t.Fatalf("Unable to create keypair: %s", err)
+ }
+ defer deleteKeyPair(t, client, keyPair)
+
+ printKeyPair(t, keyPair)
+}
+
+func TestKeypairsImportPublicKey(t *testing.T) {
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+
+ publicKey, err := createKey()
+ if err != nil {
+ t.Fatalf("Unable to create public key: %s", err)
+ }
+
+ createOpts := keypairs.CreateOpts{
+ Name: keyName,
+ PublicKey: publicKey,
+ }
+ keyPair, err := keypairs.Create(client, createOpts).Extract()
+ if err != nil {
+ t.Fatalf("Unable to create keypair: %s", err)
+ }
+ defer deleteKeyPair(t, client, keyPair)
+
+ printKeyPair(t, keyPair)
+}
+
+func TestKeypairsServerCreateWithKey(t *testing.T) {
if testing.Short() {
t.Skip("Skipping test that requires server creation in short mode.")
}
- privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
- publicKey := privateKey.PublicKey
- pub, err := ssh.NewPublicKey(&publicKey)
- th.AssertNoErr(t, err)
- pubBytes := ssh.MarshalAuthorizedKey(pub)
- pk := string(pubBytes)
-
- kp, err := keypairs.Create(client, keypairs.CreateOpts{
- Name: keyName,
- PublicKey: pk,
- }).Extract()
- th.AssertNoErr(t, err)
- t.Logf("Created key pair: %s\n", kp)
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
choices, err := ComputeChoicesFromEnv()
- th.AssertNoErr(t, err)
+ if err != nil {
+ t.Fatal(err)
+ }
- name := tools.RandomString("Gophercloud-", 8)
- t.Logf("Creating server [%s] with key pair.", name)
+ publicKey, err := createKey()
+ if err != nil {
+ t.Fatalf("Unable to create public key: %s", err)
+ }
+
+ createOpts := keypairs.CreateOpts{
+ Name: keyName,
+ PublicKey: publicKey,
+ }
+
+ keyPair, err := keypairs.Create(client, createOpts).Extract()
+ if err != nil {
+ t.Fatalf("Unable to create keypair: %s", err)
+ }
+ defer deleteKeyPair(t, client, keyPair)
+
+ networkID, err := getNetworkIDFromTenantNetworks(t, client, choices.NetworkName)
+ if err != nil {
+ t.Fatalf("Failed to obtain network ID: %v", err)
+ }
+
+ name := tools.RandomString("ACPTTEST", 16)
+ t.Logf("Attempting to create server: %s", name)
serverCreateOpts := servers.CreateOpts{
Name: name,
FlavorRef: choices.FlavorID,
ImageRef: choices.ImageID,
+ Networks: []servers.Network{
+ servers.Network{UUID: networkID},
+ },
}
server, err := servers.Create(client, keypairs.CreateOptsExt{
serverCreateOpts,
keyName,
}).Extract()
- th.AssertNoErr(t, err)
- defer servers.Delete(client, server.ID)
+ if err != nil {
+ t.Fatalf("Unable to create server: %s", err)
+ }
+
if err = waitForStatus(client, server, "ACTIVE"); err != nil {
t.Fatalf("Unable to wait for server: %v", err)
}
+ defer deleteServer(t, client, server)
server, err = servers.Get(client, server.ID).Extract()
- t.Logf("Created server: %+v\n", server)
- th.AssertNoErr(t, err)
- th.AssertEquals(t, server.KeyName, keyName)
+ if err != nil {
+ t.Fatalf("Unable to retrieve server: %s", err)
+ }
- t.Logf("Deleting key pair [%s]...", kp.Name)
- err = keypairs.Delete(client, keyName).ExtractErr()
- th.AssertNoErr(t, err)
+ if server.KeyName != keyName {
+ t.Fatalf("key name of server %s is %s, not %s", server.ID, server.KeyName, keyName)
+ }
+}
- t.Logf("Deleting server [%s]...", name)
+func createKey() (string, error) {
+ privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
+ if err != nil {
+ return "", err
+ }
+
+ publicKey := privateKey.PublicKey
+ pub, err := ssh.NewPublicKey(&publicKey)
+ if err != nil {
+ return "", err
+ }
+
+ pubBytes := ssh.MarshalAuthorizedKey(pub)
+ pk := string(pubBytes)
+ return pk, nil
+}
+
+func deleteKeyPair(t *testing.T, client *gophercloud.ServiceClient, keyPair *keypairs.KeyPair) {
+ err := keypairs.Delete(client, keyPair.Name).ExtractErr()
+ if err != nil {
+ t.Fatalf("Unable to delete keypair %s: %v", keyPair.Name, err)
+ }
+
+ t.Logf("Deleted keypair: %s", keyPair.Name)
+}
+
+func printKeyPair(t *testing.T, keypair *keypairs.KeyPair) {
+ t.Logf("Name: %s", keypair.Name)
+ t.Logf("Fingerprint: %s", keypair.Fingerprint)
+ t.Logf("Public Key: %s", keypair.PublicKey)
+ t.Logf("Private Key: %s", keypair.PrivateKey)
+ t.Logf("UserID: %s", keypair.UserID)
}
diff --git a/acceptance/openstack/compute/v2/network_test.go b/acceptance/openstack/compute/v2/network_test.go
index 615e391..c7ff1a3 100644
--- a/acceptance/openstack/compute/v2/network_test.go
+++ b/acceptance/openstack/compute/v2/network_test.go
@@ -3,15 +3,58 @@
package v2
import (
- "os"
+ "fmt"
"testing"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/networks"
- "github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
- th "github.com/gophercloud/gophercloud/testhelper"
)
+func TestNetworksList(t *testing.T) {
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+
+ allPages, err := networks.List(client).AllPages()
+ if err != nil {
+ t.Fatalf("Unable to list networks: %v", err)
+ }
+
+ allNetworks, err := networks.ExtractNetworks(allPages)
+ if err != nil {
+ t.Fatalf("Unable to list networks: %v", err)
+ }
+
+ for _, network := range allNetworks {
+ printNetwork(t, &network)
+ }
+}
+
+func TestNetworksGet(t *testing.T) {
+ choices, err := ComputeChoicesFromEnv()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+
+ networkID, err := getNetworkIDFromNetworks(t, client, choices.NetworkName)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ network, err := networks.Get(client, networkID).Extract()
+ if err != nil {
+ t.Fatalf("Unable to get network %s: %v", networkID, err)
+ }
+
+ printNetwork(t, network)
+}
+
func getNetworkIDFromNetworkExtension(t *testing.T, client *gophercloud.ServiceClient, networkName string) (string, error) {
allPages, err := networks.List(client).AllPages()
if err != nil {
@@ -36,43 +79,53 @@
return networkID, nil
}
-func TestNetworks(t *testing.T) {
- networkName := os.Getenv("OS_NETWORK_NAME")
- if networkName == "" {
- t.Fatalf("OS_NETWORK_NAME must be set")
- }
-
- choices, err := ComputeChoicesFromEnv()
- if err != nil {
- t.Fatal(err)
- }
-
- client, err := newClient()
- if err != nil {
- t.Fatalf("Unable to create a compute client: %v", err)
- }
-
- networkID, err := getNetworkIDFromNetworkExtension(t, client, networkName)
- if err != nil {
- t.Fatalf("Unable to get network ID: %v", err)
- }
-
- // createNetworkServer is defined in tenantnetworks_test.go
- server, err := createNetworkServer(t, client, choices, networkID)
- if err != nil {
- t.Fatalf("Unable to create server: %v", err)
- }
- defer func() {
- servers.Delete(client, server.ID)
- t.Logf("Server deleted.")
- }()
-
- if err = waitForStatus(client, server, "ACTIVE"); err != nil {
- t.Fatalf("Unable to wait for server: %v", err)
- }
-
+func getNetworkIDFromNetworks(t *testing.T, client *gophercloud.ServiceClient, networkName string) (string, error) {
allPages, err := networks.List(client).AllPages()
+ if err != nil {
+ t.Fatalf("Unable to list networks: %v", err)
+ }
+
allNetworks, err := networks.ExtractNetworks(allPages)
- th.AssertNoErr(t, err)
- t.Logf("Retrieved all %d networks: %+v", len(allNetworks), allNetworks)
+ if err != nil {
+ t.Fatalf("Unable to list networks: %v", err)
+ }
+
+ for _, network := range allNetworks {
+ if network.Label == networkName {
+ return network.ID, nil
+ }
+ }
+
+ return "", fmt.Errorf("Failed to obtain network ID for network %s", networkName)
+}
+
+func printNetwork(t *testing.T, network *networks.Network) {
+ t.Logf("Bridge: %s", network.Bridge)
+ t.Logf("BridgeInterface: %s", network.BridgeInterface)
+ t.Logf("Broadcast: %s", network.Broadcast)
+ t.Logf("CIDR: %s", network.CIDR)
+ t.Logf("CIDRv6: %s", network.CIDRv6)
+ t.Logf("CreatedAt: %v", network.CreatedAt)
+ t.Logf("Deleted: %t", network.Deleted)
+ t.Logf("DeletedAt: %v", network.DeletedAt)
+ t.Logf("DHCPStart: %s", network.DHCPStart)
+ t.Logf("DNS1: %s", network.DNS1)
+ t.Logf("DNS2: %s", network.DNS2)
+ t.Logf("Gateway: %s", network.Gateway)
+ t.Logf("Gatewayv6: %s", network.Gatewayv6)
+ t.Logf("Host: %s", network.Host)
+ t.Logf("ID: %s", network.ID)
+ t.Logf("Injected: %t", network.Injected)
+ t.Logf("Label: %s", network.Label)
+ t.Logf("MultiHost: %t", network.MultiHost)
+ t.Logf("Netmask: %s", network.Netmask)
+ t.Logf("Netmaskv6: %s", network.Netmaskv6)
+ t.Logf("Priority: %d", network.Priority)
+ t.Logf("ProjectID: %s", network.ProjectID)
+ t.Logf("RXTXBase: %d", network.RXTXBase)
+ t.Logf("UpdatedAt: %v", network.UpdatedAt)
+ t.Logf("VLAN: %d", network.VLAN)
+ t.Logf("VPNPrivateAddress: %s", network.VPNPrivateAddress)
+ t.Logf("VPNPublicAddress: %s", network.VPNPublicAddress)
+ t.Logf("VPNPublicPort: %d", network.VPNPublicPort)
}
diff --git a/acceptance/openstack/compute/v2/quotaset_test.go b/acceptance/openstack/compute/v2/quotaset_test.go
index d7883df..60b8563 100644
--- a/acceptance/openstack/compute/v2/quotaset_test.go
+++ b/acceptance/openstack/compute/v2/quotaset_test.go
@@ -1,60 +1,69 @@
-// +build acceptance compute
+// +build acceptance compute quotasets
package v2
import (
+ "fmt"
"testing"
"github.com/gophercloud/gophercloud"
- "github.com/gophercloud/gophercloud/openstack"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/quotasets"
"github.com/gophercloud/gophercloud/openstack/identity/v2/tenants"
- "github.com/gophercloud/gophercloud/pagination"
- th "github.com/gophercloud/gophercloud/testhelper"
)
-func TestGetQuotaset(t *testing.T) {
+func TestQuotasetGet(t *testing.T) {
client, err := newClient()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
- idclient := openstack.NewIdentityV2(client.ProviderClient)
- quotaset, err := quotasets.Get(client, findTenant(t, idclient)).Extract()
+ identityClient, err := newIdentityClient()
+ if err != nil {
+ t.Fatalf("Unable to get a new identity client: %v", err)
+ }
+
+ tenantID, err := getTenantID(t, identityClient)
if err != nil {
t.Fatal(err)
}
- t.Logf("QuotaSet details:\n")
- t.Logf(" instances=[%d]\n", quotaset.Instances)
- t.Logf(" cores=[%d]\n", quotaset.Cores)
- t.Logf(" ram=[%d]\n", quotaset.Ram)
- t.Logf(" key_pairs=[%d]\n", quotaset.KeyPairs)
- t.Logf(" metadata_items=[%d]\n", quotaset.MetadataItems)
- t.Logf(" security_groups=[%d]\n", quotaset.SecurityGroups)
- t.Logf(" security_group_rules=[%d]\n", quotaset.SecurityGroupRules)
- t.Logf(" fixed_ips=[%d]\n", quotaset.FixedIps)
- t.Logf(" floating_ips=[%d]\n", quotaset.FloatingIps)
- t.Logf(" injected_file_content_bytes=[%d]\n", quotaset.InjectedFileContentBytes)
- t.Logf(" injected_file_path_bytes=[%d]\n", quotaset.InjectedFilePathBytes)
- t.Logf(" injected_files=[%d]\n", quotaset.InjectedFiles)
+ quotaSet, err := quotasets.Get(client, tenantID).Extract()
+ if err != nil {
+ t.Fatal(err)
+ }
+ printQuotaSet(t, quotaSet)
}
-func findTenant(t *testing.T, client *gophercloud.ServiceClient) string {
- var tenantID string
- err := tenants.List(client, nil).EachPage(func(page pagination.Page) (bool, error) {
- tenantList, err := tenants.ExtractTenants(page)
- th.AssertNoErr(t, err)
+func getTenantID(t *testing.T, client *gophercloud.ServiceClient) (string, error) {
+ allPages, err := tenants.List(client, nil).AllPages()
+ if err != nil {
+ t.Fatalf("Unable to get list of tenants: %v", err)
+ }
- for _, t := range tenantList {
- tenantID = t.ID
- break
- }
+ allTenants, err := tenants.ExtractTenants(allPages)
+ if err != nil {
+ t.Fatalf("Unable to extract tenants: %v", err)
+ }
- return true, nil
- })
- th.AssertNoErr(t, err)
+ for _, tenant := range allTenants {
+ return tenant.ID, nil
+ }
- return tenantID
+ return "", fmt.Errorf("Unable to get tenant ID")
+}
+
+func printQuotaSet(t *testing.T, quotaSet *quotasets.QuotaSet) {
+ t.Logf("instances: %d\n", quotaSet.Instances)
+ t.Logf("cores: %d\n", quotaSet.Cores)
+ t.Logf("ram: %d\n", quotaSet.Ram)
+ t.Logf("key_pairs: %d\n", quotaSet.KeyPairs)
+ t.Logf("metadata_items: %d\n", quotaSet.MetadataItems)
+ t.Logf("security_groups: %d\n", quotaSet.SecurityGroups)
+ t.Logf("security_group_rules: %d\n", quotaSet.SecurityGroupRules)
+ t.Logf("fixed_ips: %d\n", quotaSet.FixedIps)
+ t.Logf("floating_ips: %d\n", quotaSet.FloatingIps)
+ t.Logf("injected_file_content_bytes: %d\n", quotaSet.InjectedFileContentBytes)
+ t.Logf("injected_file_path_bytes: %d\n", quotaSet.InjectedFilePathBytes)
+ t.Logf("injected_files: %d\n", quotaSet.InjectedFiles)
}
diff --git a/acceptance/openstack/compute/v2/secdefrules_test.go b/acceptance/openstack/compute/v2/secdefrules_test.go
deleted file mode 100644
index 15809e2..0000000
--- a/acceptance/openstack/compute/v2/secdefrules_test.go
+++ /dev/null
@@ -1,72 +0,0 @@
-// +build acceptance compute defsecrules
-
-package v2
-
-import (
- "testing"
-
- "github.com/gophercloud/gophercloud"
- "github.com/gophercloud/gophercloud/acceptance/tools"
- dsr "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/defsecrules"
- "github.com/gophercloud/gophercloud/pagination"
- th "github.com/gophercloud/gophercloud/testhelper"
-)
-
-func TestSecDefRules(t *testing.T) {
- client, err := newClient()
- th.AssertNoErr(t, err)
-
- id := createDefRule(t, client)
-
- listDefRules(t, client)
-
- getDefRule(t, client, id)
-
- deleteDefRule(t, client, id)
-}
-
-func createDefRule(t *testing.T, client *gophercloud.ServiceClient) string {
- opts := dsr.CreateOpts{
- FromPort: tools.RandomInt(80, 89),
- ToPort: tools.RandomInt(90, 99),
- IPProtocol: "TCP",
- CIDR: "0.0.0.0/0",
- }
-
- rule, err := dsr.Create(client, opts).Extract()
- th.AssertNoErr(t, err)
-
- t.Logf("Created default rule %s", rule.ID)
-
- return rule.ID
-}
-
-func listDefRules(t *testing.T, client *gophercloud.ServiceClient) {
- err := dsr.List(client).EachPage(func(page pagination.Page) (bool, error) {
- drList, err := dsr.ExtractDefaultRules(page)
- th.AssertNoErr(t, err)
-
- for _, dr := range drList {
- t.Logf("Listing default rule %s: Name [%s] From Port [%s] To Port [%s] Protocol [%s]",
- dr.ID, dr.FromPort, dr.ToPort, dr.IPProtocol)
- }
-
- return true, nil
- })
-
- th.AssertNoErr(t, err)
-}
-
-func getDefRule(t *testing.T, client *gophercloud.ServiceClient, id string) {
- rule, err := dsr.Get(client, id).Extract()
- th.AssertNoErr(t, err)
-
- t.Logf("Getting rule %s: %#v", id, rule)
-}
-
-func deleteDefRule(t *testing.T, client *gophercloud.ServiceClient, id string) {
- err := dsr.Delete(client, id).ExtractErr()
- th.AssertNoErr(t, err)
-
- t.Logf("Deleted rule %s", id)
-}
diff --git a/acceptance/openstack/compute/v2/secgroup_test.go b/acceptance/openstack/compute/v2/secgroup_test.go
index da06c35..8a2e7f7 100644
--- a/acceptance/openstack/compute/v2/secgroup_test.go
+++ b/acceptance/openstack/compute/v2/secgroup_test.go
@@ -8,188 +8,201 @@
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups"
- "github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
- "github.com/gophercloud/gophercloud/pagination"
- th "github.com/gophercloud/gophercloud/testhelper"
)
-func TestSecGroups(t *testing.T) {
+func TestSecGroupsList(t *testing.T) {
client, err := newClient()
- th.AssertNoErr(t, err)
-
- serverID, needsDeletion := findServer(t, client)
-
- groupID := createSecGroup(t, client)
-
- listSecGroups(t, client)
-
- newName := tools.RandomString("secgroup_", 5)
- updateSecGroup(t, client, groupID, newName)
-
- getSecGroup(t, client, groupID)
-
- addRemoveRules(t, client, groupID)
-
- addServerToSecGroup(t, client, serverID, newName)
-
- removeServerFromSecGroup(t, client, serverID, newName)
-
- if needsDeletion {
- servers.Delete(client, serverID)
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
}
- deleteSecGroup(t, client, groupID)
+ allPages, err := secgroups.List(client).AllPages()
+ if err != nil {
+ t.Fatalf("Unable to retrieve security groups: %v", err)
+ }
+
+ allSecGroups, err := secgroups.ExtractSecurityGroups(allPages)
+ if err != nil {
+ t.Fatalf("Unable to extract security groups: %v", err)
+ }
+
+ for _, secgroup := range allSecGroups {
+ printSecurityGroup(t, &secgroup)
+ }
}
-func createSecGroup(t *testing.T, client *gophercloud.ServiceClient) string {
- opts := secgroups.CreateOpts{
+func TestSecGroupsCreate(t *testing.T) {
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+
+ securityGroup, err := createSecurityGroup(t, client)
+ if err != nil {
+ t.Fatalf("Unable to create security group: %v", err)
+ }
+ defer deleteSecurityGroup(t, client, securityGroup)
+}
+
+func TestSecGroupsUpdate(t *testing.T) {
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+
+ securityGroup, err := createSecurityGroup(t, client)
+ if err != nil {
+ t.Fatalf("Unable to create security group: %v", err)
+ }
+ defer deleteSecurityGroup(t, client, securityGroup)
+
+ updateOpts := secgroups.UpdateOpts{
+ Name: tools.RandomString("secgroup_", 4),
+ Description: tools.RandomString("dec_", 10),
+ }
+ updatedSecurityGroup, err := secgroups.Update(client, securityGroup.ID, updateOpts).Extract()
+ if err != nil {
+ t.Fatalf("Unable to update security group: %v", err)
+ }
+
+ t.Logf("Updated %s's name to %s", updatedSecurityGroup.ID, updatedSecurityGroup.Name)
+}
+
+func TestSecGroupsRuleCreate(t *testing.T) {
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+
+ securityGroup, err := createSecurityGroup(t, client)
+ if err != nil {
+ t.Fatalf("Unable to create security group: %v", err)
+ }
+ defer deleteSecurityGroup(t, client, securityGroup)
+
+ rule, err := createSecurityGroupRule(t, client, securityGroup.ID)
+ if err != nil {
+ t.Fatalf("Unable to create rule: %v", err)
+ }
+ defer deleteSecurityGroupRule(t, client, rule)
+}
+
+func TestSecGroupsAddGroupToServer(t *testing.T) {
+ if testing.Short() {
+ t.Skip("Skipping test that requires server creation in short mode.")
+ }
+
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+
+ choices, err := ComputeChoicesFromEnv()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ server, err := createServer(t, client, choices)
+ if err != nil {
+ t.Fatalf("Unable to create server: %v", err)
+ }
+
+ if err = waitForStatus(client, server, "ACTIVE"); err != nil {
+ t.Fatalf("Unable to wait for server: %v", err)
+ }
+ defer deleteServer(t, client, server)
+
+ securityGroup, err := createSecurityGroup(t, client)
+ if err != nil {
+ t.Fatalf("Unable to create security group: %v", err)
+ }
+ defer deleteSecurityGroup(t, client, securityGroup)
+
+ rule, err := createSecurityGroupRule(t, client, securityGroup.ID)
+ if err != nil {
+ t.Fatalf("Unable to create rule: %v", err)
+ }
+ defer deleteSecurityGroupRule(t, client, rule)
+
+ t.Logf("Adding group %s to server %s", securityGroup.ID, server.ID)
+ err = secgroups.AddServer(client, server.ID, securityGroup.Name).ExtractErr()
+ if err != nil && err.Error() != "EOF" {
+ t.Fatalf("Unable to add group %s to server %s: %s", securityGroup.ID, server.ID, err)
+ }
+
+ t.Logf("Removing group %s from server %s", securityGroup.ID, server.ID)
+ err = secgroups.RemoveServer(client, server.ID, securityGroup.Name).ExtractErr()
+ if err != nil && err.Error() != "EOF" {
+ t.Fatalf("Unable to remove group %s from server %s: %s", securityGroup.ID, server.ID, err)
+ }
+}
+
+func createSecurityGroup(t *testing.T, client *gophercloud.ServiceClient) (secgroups.SecurityGroup, error) {
+ createOpts := secgroups.CreateOpts{
Name: tools.RandomString("secgroup_", 5),
Description: "something",
}
- group, err := secgroups.Create(client, opts).Extract()
- th.AssertNoErr(t, err)
-
- t.Logf("Created secgroup %s %s", group.ID, group.Name)
-
- return group.ID
-}
-
-func listSecGroups(t *testing.T, client *gophercloud.ServiceClient) {
- err := secgroups.List(client).EachPage(func(page pagination.Page) (bool, error) {
- secGrpList, err := secgroups.ExtractSecurityGroups(page)
- th.AssertNoErr(t, err)
-
- for _, sg := range secGrpList {
- t.Logf("Listing secgroup %s: Name [%s] Desc [%s] TenantID [%s]", sg.ID,
- sg.Name, sg.Description, sg.TenantID)
- }
-
- return true, nil
- })
-
- th.AssertNoErr(t, err)
-}
-
-func updateSecGroup(t *testing.T, client *gophercloud.ServiceClient, id, newName string) {
- opts := secgroups.UpdateOpts{
- Name: newName,
- Description: tools.RandomString("dec_", 10),
+ securityGroup, err := secgroups.Create(client, createOpts).Extract()
+ if err != nil {
+ return *securityGroup, err
}
- group, err := secgroups.Update(client, id, opts).Extract()
- th.AssertNoErr(t, err)
- t.Logf("Updated %s's name to %s", group.ID, group.Name)
+ t.Logf("Created security group: %s", securityGroup.ID)
+ return *securityGroup, nil
}
-func getSecGroup(t *testing.T, client *gophercloud.ServiceClient, id string) {
- group, err := secgroups.Get(client, id).Extract()
- th.AssertNoErr(t, err)
+func deleteSecurityGroup(t *testing.T, client *gophercloud.ServiceClient, securityGroup secgroups.SecurityGroup) {
+ err := secgroups.Delete(client, securityGroup.ID).ExtractErr()
+ if err != nil {
+ t.Fatalf("Unable to delete security group %s: %s", securityGroup.ID, err)
+ }
- t.Logf("Getting %s: %#v", id, group)
+ t.Logf("Deleted security group: %s", securityGroup.ID)
}
-func addRemoveRules(t *testing.T, client *gophercloud.ServiceClient, id string) {
- opts := secgroups.CreateRuleOpts{
- ParentGroupID: id,
- FromPort: 22,
- ToPort: 22,
+func createSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, securityGroupID string) (secgroups.Rule, error) {
+ createOpts := secgroups.CreateRuleOpts{
+ ParentGroupID: securityGroupID,
+ FromPort: tools.RandomInt(80, 89),
+ ToPort: tools.RandomInt(90, 99),
IPProtocol: "TCP",
CIDR: "0.0.0.0/0",
}
- rule, err := secgroups.CreateRule(client, opts).Extract()
- th.AssertNoErr(t, err)
-
- t.Logf("Adding rule %s to group %s", rule.ID, id)
-
- err = secgroups.DeleteRule(client, rule.ID).ExtractErr()
- th.AssertNoErr(t, err)
-
- t.Logf("Deleted rule %s from group %s", rule.ID, id)
-
- icmpOpts := secgroups.CreateRuleOpts{
- ParentGroupID: id,
- FromPort: 0,
- ToPort: 0,
- IPProtocol: "ICMP",
- CIDR: "0.0.0.0/0",
+ rule, err := secgroups.CreateRule(client, createOpts).Extract()
+ if err != nil {
+ return *rule, err
}
- icmpRule, err := secgroups.CreateRule(client, icmpOpts).Extract()
- th.AssertNoErr(t, err)
-
- t.Logf("Adding ICMP rule %s to group %s", icmpRule.ID, id)
-
- err = secgroups.DeleteRule(client, icmpRule.ID).ExtractErr()
- th.AssertNoErr(t, err)
-
- t.Logf("Deleted ICMP rule %s from group %s", icmpRule.ID, id)
+ t.Logf("Created security group rule: %s", rule.ID)
+ return *rule, nil
}
-func findServer(t *testing.T, client *gophercloud.ServiceClient) (string, bool) {
- var serverID string
- var needsDeletion bool
-
- err := servers.List(client, nil).EachPage(func(page pagination.Page) (bool, error) {
- sList, err := servers.ExtractServers(page)
- th.AssertNoErr(t, err)
-
- for _, s := range sList {
- serverID = s.ID
- needsDeletion = false
-
- t.Logf("Found an existing server: ID [%s]", serverID)
- break
- }
-
- return true, nil
- })
- th.AssertNoErr(t, err)
-
- if serverID == "" {
- t.Log("No server found, creating one")
-
- choices, err := ComputeChoicesFromEnv()
- th.AssertNoErr(t, err)
-
- opts := &servers.CreateOpts{
- Name: tools.RandomString("secgroup_test_", 5),
- ImageRef: choices.ImageID,
- FlavorRef: choices.FlavorID,
- }
-
- s, err := servers.Create(client, opts).Extract()
- th.AssertNoErr(t, err)
- serverID = s.ID
-
- t.Logf("Created server %s, waiting for it to build", s.ID)
- err = servers.WaitForStatus(client, serverID, "ACTIVE", 300)
- th.AssertNoErr(t, err)
-
- needsDeletion = true
+func deleteSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, rule secgroups.Rule) {
+ err := secgroups.DeleteRule(client, rule.ID).ExtractErr()
+ if err != nil {
+ t.Fatalf("Unable to delete rule: %v", err)
}
- return serverID, needsDeletion
+ t.Logf("Deleted security group rule: %s", rule.ID)
}
-func addServerToSecGroup(t *testing.T, client *gophercloud.ServiceClient, serverID, groupName string) {
- err := secgroups.AddServerToGroup(client, serverID, groupName).ExtractErr()
- th.AssertNoErr(t, err)
+func printSecurityGroup(t *testing.T, securityGroup *secgroups.SecurityGroup) {
+ t.Logf("ID: %s", securityGroup.ID)
+ t.Logf("Name: %s", securityGroup.Name)
+ t.Logf("Description: %s", securityGroup.Description)
+ t.Logf("Tenant ID: %s", securityGroup.TenantID)
+ t.Logf("Rules:")
- t.Logf("Adding group %s to server %s", groupName, serverID)
-}
-
-func removeServerFromSecGroup(t *testing.T, client *gophercloud.ServiceClient, serverID, groupName string) {
- err := secgroups.RemoveServerFromGroup(client, serverID, groupName).ExtractErr()
- th.AssertNoErr(t, err)
-
- t.Logf("Removing group %s from server %s", groupName, serverID)
-}
-
-func deleteSecGroup(t *testing.T, client *gophercloud.ServiceClient, id string) {
- err := secgroups.Delete(client, id).ExtractErr()
- th.AssertNoErr(t, err)
-
- t.Logf("Deleted group %s", id)
+ for _, rule := range securityGroup.Rules {
+ t.Logf("\tID: %s", rule.ID)
+ t.Logf("\tFrom Port: %d", rule.FromPort)
+ t.Logf("\tTo Port: %d", rule.ToPort)
+ t.Logf("\tIP Protocol: %s", rule.IPProtocol)
+ t.Logf("\tIP Range: %s", rule.IPRange.CIDR)
+ t.Logf("\tParent Group ID: %s", rule.ParentGroupID)
+ t.Logf("\tGroup Tenant ID: %s", rule.Group.TenantID)
+ t.Logf("\tGroup Name: %s", rule.Group.Name)
+ }
}
diff --git a/acceptance/openstack/compute/v2/servergroup_test.go b/acceptance/openstack/compute/v2/servergroup_test.go
index 79f7c92..0ee47ea 100644
--- a/acceptance/openstack/compute/v2/servergroup_test.go
+++ b/acceptance/openstack/compute/v2/servergroup_test.go
@@ -1,9 +1,8 @@
-// +build acceptance compute servers
+// +build acceptance compute servergroups
package v2
import (
- "fmt"
"testing"
"github.com/gophercloud/gophercloud"
@@ -11,43 +10,120 @@
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/schedulerhints"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups"
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
- th "github.com/gophercloud/gophercloud/testhelper"
)
-func createServerGroup(t *testing.T, computeClient *gophercloud.ServiceClient) (*servergroups.ServerGroup, error) {
- sg, err := servergroups.Create(computeClient, &servergroups.CreateOpts{
+func TestServergroupsList(t *testing.T) {
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+
+ allPages, err := servergroups.List(client).AllPages()
+ if err != nil {
+ t.Fatalf("Unable to list server groups: %v", err)
+ }
+
+ allServerGroups, err := servergroups.ExtractServerGroups(allPages)
+ if err != nil {
+ t.Fatalf("Unable to extract server groups: %v", err)
+ }
+
+ for _, serverGroup := range allServerGroups {
+ printServerGroup(t, &serverGroup)
+ }
+}
+
+func TestServergroupsCreate(t *testing.T) {
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+
+ serverGroup, err := createServerGroup(t, client, "anti-affinity")
+ if err != nil {
+ t.Fatalf("Unable to create server group: %v", err)
+ }
+ defer deleteServerGroup(t, client, serverGroup)
+
+ serverGroup, err = servergroups.Get(client, serverGroup.ID).Extract()
+ if err != nil {
+ t.Fatalf("Unable to get server group: %v", err)
+ }
+
+ printServerGroup(t, serverGroup)
+}
+
+func TestServergroupsAffinityPolicy(t *testing.T) {
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+
+ choices, err := ComputeChoicesFromEnv()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ serverGroup, err := createServerGroup(t, client, "affinity")
+ if err != nil {
+ t.Fatalf("Unable to create server group: %v", err)
+ }
+ defer deleteServerGroup(t, client, serverGroup)
+
+ firstServer, err := createServerInServerGroup(t, client, choices, serverGroup)
+ if err != nil {
+ t.Fatalf("Unable to create server: %v", err)
+ }
+
+ if err = waitForStatus(client, firstServer, "ACTIVE"); err != nil {
+ t.Fatalf("Unable to wait for server: %v", err)
+ }
+ defer deleteServer(t, client, firstServer)
+
+ firstServer, err = servers.Get(client, firstServer.ID).Extract()
+
+ secondServer, err := createServerInServerGroup(t, client, choices, serverGroup)
+ if err != nil {
+ t.Fatalf("Unable to create server: %v", err)
+ }
+
+ if err = waitForStatus(client, secondServer, "ACTIVE"); err != nil {
+ t.Fatalf("Unable to wait for server: %v", err)
+ }
+ defer deleteServer(t, client, secondServer)
+
+ secondServer, err = servers.Get(client, secondServer.ID).Extract()
+
+ if firstServer.HostID != secondServer.HostID {
+ t.Fatalf("%s and %s were not scheduled on the same host.", firstServer.ID, secondServer.ID)
+ }
+}
+
+func createServerGroup(t *testing.T, client *gophercloud.ServiceClient, policy string) (*servergroups.ServerGroup, error) {
+ sg, err := servergroups.Create(client, &servergroups.CreateOpts{
Name: "test",
- Policies: []string{"affinity"},
+ Policies: []string{policy},
}).Extract()
if err != nil {
t.Fatalf("Unable to create server group: %v", err)
}
- t.Logf("Created server group: %v", sg.ID)
- t.Logf("It has policies: %v", sg.Policies)
-
return sg, nil
}
-func getServerGroup(t *testing.T, computeClient *gophercloud.ServiceClient, sgID string) error {
- sg, err := servergroups.Get(computeClient, sgID).Extract()
- if err != nil {
- t.Fatalf("Unable to get server group: %v", err)
- }
-
- t.Logf("Got server group: %v", sg.Name)
-
- return nil
-}
-
-func createServerInGroup(t *testing.T, computeClient *gophercloud.ServiceClient, choices *ComputeChoices, serverGroup *servergroups.ServerGroup) (*servers.Server, error) {
+func createServerInServerGroup(t *testing.T, client *gophercloud.ServiceClient, choices *ComputeChoices, serverGroup *servergroups.ServerGroup) (*servers.Server, error) {
if testing.Short() {
t.Skip("Skipping test that requires server creation in short mode.")
}
+ networkID, err := getNetworkIDFromTenantNetworks(t, client, choices.NetworkName)
+ if err != nil {
+ t.Fatalf("Failed to obtain network ID: %v", err)
+ }
+
name := tools.RandomString("ACPTTEST", 16)
- t.Logf("Attempting to create server: %s\n", name)
+ t.Logf("Attempting to create server: %s", name)
pwd := tools.MakeNewPassword("")
@@ -56,88 +132,38 @@
FlavorRef: choices.FlavorID,
ImageRef: choices.ImageID,
AdminPass: pwd,
+ Networks: []servers.Network{
+ servers.Network{UUID: networkID},
+ },
}
- server, err := servers.Create(computeClient, schedulerhints.CreateOptsExt{
+
+ schedulerHintsOpts := schedulerhints.CreateOptsExt{
serverCreateOpts,
schedulerhints.SchedulerHints{
Group: serverGroup.ID,
},
- }).Extract()
+ }
+ server, err := servers.Create(client, schedulerHintsOpts).Extract()
if err != nil {
t.Fatalf("Unable to create server: %v", err)
}
- th.AssertEquals(t, pwd, server.AdminPass)
-
return server, err
}
-func verifySchedulerWorked(t *testing.T, firstServer, secondServer *servers.Server) error {
- t.Logf("First server hostID: %v", firstServer.HostID)
- t.Logf("Second server hostID: %v", secondServer.HostID)
- if firstServer.HostID == secondServer.HostID {
- return nil
+func deleteServerGroup(t *testing.T, client *gophercloud.ServiceClient, serverGroup *servergroups.ServerGroup) {
+ err := servergroups.Delete(client, serverGroup.ID).ExtractErr()
+ if err != nil {
+ t.Fatalf("Unable to delete server group %s: %v", serverGroup.ID, err)
}
- return fmt.Errorf("%s and %s were not scheduled on the same host.", firstServer.ID, secondServer.ID)
+ t.Logf("Deleted server group %s", serverGroup.ID)
}
-func TestServerGroups(t *testing.T) {
- choices, err := ComputeChoicesFromEnv()
- if err != nil {
- t.Fatal(err)
- }
-
- computeClient, err := newClient()
- if err != nil {
- t.Fatalf("Unable to create a compute client: %v", err)
- }
-
- sg, err := createServerGroup(t, computeClient)
- if err != nil {
- t.Fatalf("Unable to create server group: %v", err)
- }
- defer func() {
- servergroups.Delete(computeClient, sg.ID)
- t.Logf("Server Group deleted.")
- }()
-
- err = getServerGroup(t, computeClient, sg.ID)
- if err != nil {
- t.Fatalf("Unable to get server group: %v", err)
- }
-
- firstServer, err := createServerInGroup(t, computeClient, choices, sg)
- if err != nil {
- t.Fatalf("Unable to create server: %v", err)
- }
- defer func() {
- servers.Delete(computeClient, firstServer.ID)
- t.Logf("Server deleted.")
- }()
-
- if err = waitForStatus(computeClient, firstServer, "ACTIVE"); err != nil {
- t.Fatalf("Unable to wait for server: %v", err)
- }
-
- firstServer, err = servers.Get(computeClient, firstServer.ID).Extract()
-
- secondServer, err := createServerInGroup(t, computeClient, choices, sg)
- if err != nil {
- t.Fatalf("Unable to create server: %v", err)
- }
- defer func() {
- servers.Delete(computeClient, secondServer.ID)
- t.Logf("Server deleted.")
- }()
-
- if err = waitForStatus(computeClient, secondServer, "ACTIVE"); err != nil {
- t.Fatalf("Unable to wait for server: %v", err)
- }
-
- secondServer, err = servers.Get(computeClient, secondServer.ID).Extract()
-
- if err = verifySchedulerWorked(t, firstServer, secondServer); err != nil {
- t.Fatalf("Scheduling did not work: %v", err)
- }
+func printServerGroup(t *testing.T, serverGroup *servergroups.ServerGroup) {
+ t.Logf("ID: %s", serverGroup.ID)
+ t.Logf("Name: %s", serverGroup.Name)
+ t.Logf("Policies: %#v", serverGroup.Policies)
+ t.Logf("Members: %#v", serverGroup.Members)
+ t.Logf("Metadata: %#v", serverGroup.Metadata)
}
diff --git a/acceptance/openstack/compute/v2/servers_test.go b/acceptance/openstack/compute/v2/servers_test.go
index 4c4fb42..34a3fd2 100644
--- a/acceptance/openstack/compute/v2/servers_test.go
+++ b/acceptance/openstack/compute/v2/servers_test.go
@@ -3,178 +3,93 @@
package v2
import (
- "os"
"testing"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/acceptance/tools"
- "github.com/gophercloud/gophercloud/openstack"
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
- "github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
- "github.com/gophercloud/gophercloud/pagination"
th "github.com/gophercloud/gophercloud/testhelper"
)
-func TestListServers(t *testing.T) {
+func TestServersList(t *testing.T) {
client, err := newClient()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
- t.Logf("ID\tRegion\tName\tStatus\tIPv4\tIPv6")
+ allPages, err := servers.List(client, servers.ListOpts{}).AllPages()
+ if err != nil {
+ t.Fatalf("Unable to retrieve servers: %v", err)
+ }
- pager := servers.List(client, servers.ListOpts{})
- count, pages := 0, 0
- pager.EachPage(func(page pagination.Page) (bool, error) {
- pages++
- t.Logf("---")
+ allServers, err := servers.ExtractServers(allPages)
+ if err != nil {
+ t.Fatalf("Unable to extract servers: %v", err)
+ }
- servers, err := servers.ExtractServers(page)
- if err != nil {
- return false, err
- }
-
- for _, s := range servers {
- t.Logf("%s\t%s\t%s\t%s\t%s\t\n", s.ID, s.Name, s.Status, s.AccessIPv4, s.AccessIPv6)
- count++
- }
-
- return true, nil
- })
-
- t.Logf("--------\n%d servers listed on %d pages.\n", count, pages)
+ for _, server := range allServers {
+ printServer(t, &server)
+ }
}
-func networkingClient() (*gophercloud.ServiceClient, error) {
- opts, err := openstack.AuthOptionsFromEnv()
+func TestServersCreateDestroy(t *testing.T) {
+ client, err := newClient()
if err != nil {
- return nil, err
+ t.Fatalf("Unable to create a compute client: %v", err)
}
- provider, err := openstack.AuthenticatedClient(opts)
- if err != nil {
- return nil, err
- }
-
- return openstack.NewNetworkV2(provider, gophercloud.EndpointOpts{
- Region: os.Getenv("OS_REGION_NAME"),
- })
-}
-
-func createServer(t *testing.T, client *gophercloud.ServiceClient, choices *ComputeChoices) (*servers.Server, error) {
- if testing.Short() {
- t.Skip("Skipping test that requires server creation in short mode.")
- }
-
- var network networks.Network
-
- networkingClient, err := networkingClient()
- if err != nil {
- t.Fatalf("Unable to create a networking client: %v", err)
- }
-
- pager := networks.List(networkingClient, networks.ListOpts{
- Name: choices.NetworkName,
- Limit: 1,
- })
- pager.EachPage(func(page pagination.Page) (bool, error) {
- networks, err := networks.ExtractNetworks(page)
- if err != nil {
- t.Errorf("Failed to extract networks: %v", err)
- return false, err
- }
-
- if len(networks) == 0 {
- t.Fatalf("No networks to attach to server")
- return false, err
- }
-
- network = networks[0]
-
- return false, nil
- })
-
- name := tools.RandomString("ACPTTEST", 16)
- t.Logf("Attempting to create server: %s\n", name)
-
- pwd := tools.MakeNewPassword("")
-
- server, err := servers.Create(client, servers.CreateOpts{
- Name: name,
- FlavorRef: choices.FlavorID,
- ImageRef: choices.ImageID,
- Networks: []servers.Network{
- servers.Network{UUID: network.ID},
- },
- AdminPass: pwd,
- Personality: servers.Personality{
- &servers.File{
- Path: "/etc/test",
- Contents: []byte("hello world"),
- },
- },
- }).Extract()
- if err != nil {
- t.Fatalf("Unable to create server: %v", err)
- }
-
- th.AssertEquals(t, pwd, server.AdminPass)
-
- return server, err
-}
-
-func TestCreateDestroyServer(t *testing.T) {
choices, err := ComputeChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
- client, err := newClient()
- if err != nil {
- t.Fatalf("Unable to create a compute client: %v", err)
- }
-
server, err := createServer(t, client, choices)
if err != nil {
t.Fatalf("Unable to create server: %v", err)
}
- defer func() {
- servers.Delete(client, server.ID)
- t.Logf("Server deleted.")
- }()
if err = waitForStatus(client, server, "ACTIVE"); err != nil {
t.Fatalf("Unable to wait for server: %v", err)
}
+ defer deleteServer(t, client, server)
- pager := servers.ListAddresses(client, server.ID)
- pager.EachPage(func(page pagination.Page) (bool, error) {
- networks, err := servers.ExtractAddresses(page)
- if err != nil {
- return false, err
- }
+ newServer, err := servers.Get(client, server.ID).Extract()
+ if err != nil {
+ t.Errorf("Unable to retrieve server: %v", err)
+ }
+ printServer(t, newServer)
- for n, a := range networks {
- t.Logf("%s: %+v\n", n, a)
- }
- return true, nil
- })
+ allAddressPages, err := servers.ListAddresses(client, server.ID).AllPages()
+ if err != nil {
+ t.Errorf("Unable to list server addresses: %v", err)
+ }
- pager = servers.ListAddressesByNetwork(client, server.ID, choices.NetworkName)
- pager.EachPage(func(page pagination.Page) (bool, error) {
- addresses, err := servers.ExtractNetworkAddresses(page)
- if err != nil {
- return false, err
- }
+ allAddresses, err := servers.ExtractAddresses(allAddressPages)
+ if err != nil {
+ t.Errorf("Unable to extract server addresses: %v", err)
+ }
- for _, a := range addresses {
- t.Logf("%+v\n", a)
- }
- return true, nil
- })
+ for network, address := range allAddresses {
+ t.Logf("Addresses on %s: %+v", network, address)
+ }
+
+ allNetworkAddressPages, err := servers.ListAddressesByNetwork(client, server.ID, choices.NetworkName).AllPages()
+ if err != nil {
+ t.Errorf("Unable to list server addresses: %v", err)
+ }
+
+ allNetworkAddresses, err := servers.ExtractNetworkAddresses(allNetworkAddressPages)
+ if err != nil {
+ t.Errorf("Unable to extract server addresses: %v", err)
+ }
+
+ t.Logf("Addresses on %s:", choices.NetworkName)
+ for _, address := range allNetworkAddresses {
+ t.Logf("%+v", address)
+ }
}
-func TestUpdateServer(t *testing.T) {
+func TestServersUpdate(t *testing.T) {
client, err := newClient()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
@@ -189,11 +104,11 @@
if err != nil {
t.Fatal(err)
}
- defer servers.Delete(client, server.ID)
if err = waitForStatus(client, server, "ACTIVE"); err != nil {
t.Fatal(err)
}
+ defer deleteServer(t, client, server)
alternateName := tools.RandomString("ACPTTEST", 16)
for alternateName == server.Name {
@@ -202,7 +117,11 @@
t.Logf("Attempting to rename the server to %s.", alternateName)
- updated, err := servers.Update(client, server.ID, servers.UpdateOpts{Name: alternateName}).Extract()
+ updateOpts := servers.UpdateOpts{
+ Name: alternateName,
+ }
+
+ updated, err := servers.Update(client, server.ID, updateOpts).Extract()
if err != nil {
t.Fatalf("Unable to rename server: %v", err)
}
@@ -221,7 +140,73 @@
})
}
-func TestActionChangeAdminPassword(t *testing.T) {
+func TestServersMetadata(t *testing.T) {
+ t.Parallel()
+
+ choices, err := ComputeChoicesFromEnv()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+
+ server, err := createServer(t, client, choices)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if err = waitForStatus(client, server, "ACTIVE"); err != nil {
+ t.Fatal(err)
+ }
+ defer deleteServer(t, client, server)
+
+ metadata, err := servers.UpdateMetadata(client, server.ID, servers.MetadataOpts{
+ "foo": "bar",
+ "this": "that",
+ }).Extract()
+ if err != nil {
+ t.Fatalf("Unable to update metadata: %v", err)
+ }
+ t.Logf("UpdateMetadata result: %+v\n", metadata)
+
+ err = servers.DeleteMetadatum(client, server.ID, "foo").ExtractErr()
+ if err != nil {
+ t.Fatalf("Unable to delete metadatum: %v", err)
+ }
+
+ metadata, err = servers.CreateMetadatum(client, server.ID, servers.MetadatumOpts{
+ "foo": "baz",
+ }).Extract()
+ if err != nil {
+ t.Fatalf("Unable to create metadatum: %v", err)
+ }
+ t.Logf("CreateMetadatum result: %+v\n", metadata)
+
+ metadata, err = servers.Metadatum(client, server.ID, "foo").Extract()
+ if err != nil {
+ t.Fatalf("Unable to get metadatum: %v", err)
+ }
+ t.Logf("Metadatum result: %+v\n", metadata)
+ th.AssertEquals(t, "baz", metadata["foo"])
+
+ metadata, err = servers.Metadata(client, server.ID).Extract()
+ if err != nil {
+ t.Fatalf("Unable to get metadata: %v", err)
+ }
+ t.Logf("Metadata result: %+v\n", metadata)
+
+ metadata, err = servers.ResetMetadata(client, server.ID, servers.MetadataOpts{}).Extract()
+ if err != nil {
+ t.Fatalf("Unable to reset metadata: %v", err)
+ }
+ t.Logf("ResetMetadata result: %+v\n", metadata)
+ th.AssertDeepEquals(t, map[string]string{}, metadata)
+}
+
+func TestServersActionChangeAdminPassword(t *testing.T) {
t.Parallel()
client, err := newClient()
@@ -238,16 +223,16 @@
if err != nil {
t.Fatal(err)
}
- defer servers.Delete(client, server.ID)
if err = waitForStatus(client, server, "ACTIVE"); err != nil {
t.Fatal(err)
}
+ defer deleteServer(t, client, server)
randomPassword := tools.MakeNewPassword(server.AdminPass)
res := servers.ChangeAdminPassword(client, server.ID, randomPassword)
if res.Err != nil {
- t.Fatal(err)
+ t.Fatal(res.Err)
}
if err = waitForStatus(client, server, "PASSWORD"); err != nil {
@@ -259,7 +244,7 @@
}
}
-func TestActionReboot(t *testing.T) {
+func TestServersActionReboot(t *testing.T) {
t.Parallel()
client, err := newClient()
@@ -276,21 +261,20 @@
if err != nil {
t.Fatal(err)
}
- defer servers.Delete(client, server.ID)
if err = waitForStatus(client, server, "ACTIVE"); err != nil {
t.Fatal(err)
}
+ defer deleteServer(t, client, server)
- res := servers.Reboot(client, server.ID, "aldhjflaskhjf")
- if res.Err == nil {
- t.Fatal("Expected the SDK to provide an ArgumentError here")
+ rebootOpts := &servers.RebootOpts{
+ Type: servers.SoftReboot,
}
t.Logf("Attempting reboot of server %s", server.ID)
- res = servers.Reboot(client, server.ID, servers.OSReboot)
+ res := servers.Reboot(client, server.ID, rebootOpts)
if res.Err != nil {
- t.Fatalf("Unable to reboot server: %v", err)
+ t.Fatalf("Unable to reboot server: %v", res.Err)
}
if err = waitForStatus(client, server, "REBOOT"); err != nil {
@@ -302,7 +286,7 @@
}
}
-func TestActionRebuild(t *testing.T) {
+func TestServersActionRebuild(t *testing.T) {
t.Parallel()
client, err := newClient()
@@ -319,11 +303,11 @@
if err != nil {
t.Fatal(err)
}
- defer servers.Delete(client, server.ID)
if err = waitForStatus(client, server, "ACTIVE"); err != nil {
t.Fatal(err)
}
+ defer deleteServer(t, client, server)
t.Logf("Attempting to rebuild server %s", server.ID)
@@ -351,13 +335,118 @@
}
}
-func resizeServer(t *testing.T, client *gophercloud.ServiceClient, server *servers.Server, choices *ComputeChoices) {
- if err := waitForStatus(client, server, "ACTIVE"); err != nil {
+func TestServersActionResizeConfirm(t *testing.T) {
+ t.Parallel()
+
+ choices, err := ComputeChoicesFromEnv()
+ if err != nil {
t.Fatal(err)
}
- t.Logf("Attempting to resize server [%s]", server.ID)
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+ server, err := createServer(t, client, choices)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if err = waitForStatus(client, server, "ACTIVE"); err != nil {
+ t.Fatal(err)
+ }
+ defer deleteServer(t, client, server)
+
+ t.Logf("Attempting to resize server %s", server.ID)
+ resizeServer(t, client, server, choices)
+
+ t.Logf("Attempting to confirm resize for server %s", server.ID)
+ if res := servers.ConfirmResize(client, server.ID); res.Err != nil {
+ t.Fatal(res.Err)
+ }
+
+ if err = waitForStatus(client, server, "ACTIVE"); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestServersActionResizeRevert(t *testing.T) {
+ t.Parallel()
+
+ choices, err := ComputeChoicesFromEnv()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+
+ server, err := createServer(t, client, choices)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if err = waitForStatus(client, server, "ACTIVE"); err != nil {
+ t.Fatal(err)
+ }
+ defer deleteServer(t, client, server)
+
+ t.Logf("Attempting to resize server %s", server.ID)
+ resizeServer(t, client, server, choices)
+
+ t.Logf("Attempting to revert resize for server %s", server.ID)
+ if res := servers.RevertResize(client, server.ID); res.Err != nil {
+ t.Fatal(res.Err)
+ }
+
+ if err = waitForStatus(client, server, "ACTIVE"); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func createServer(t *testing.T, client *gophercloud.ServiceClient, choices *ComputeChoices) (*servers.Server, error) {
+ if testing.Short() {
+ t.Skip("Skipping test that requires server creation in short mode.")
+ }
+
+ networkID, err := getNetworkIDFromTenantNetworks(t, client, choices.NetworkName)
+ if err != nil {
+ t.Fatalf("Failed to obtain network ID: %v", err)
+ }
+
+ name := tools.RandomString("ACPTTEST", 16)
+ t.Logf("Attempting to create server: %s", name)
+
+ pwd := tools.MakeNewPassword("")
+
+ server, err := servers.Create(client, servers.CreateOpts{
+ Name: name,
+ FlavorRef: choices.FlavorID,
+ ImageRef: choices.ImageID,
+ AdminPass: pwd,
+ Networks: []servers.Network{
+ servers.Network{UUID: networkID},
+ },
+ Personality: servers.Personality{
+ &servers.File{
+ Path: "/etc/test",
+ Contents: []byte("hello world"),
+ },
+ },
+ }).Extract()
+ if err != nil {
+ t.Fatalf("Unable to create server: %v", err)
+ }
+
+ th.AssertEquals(t, pwd, server.AdminPass)
+
+ return server, err
+}
+
+func resizeServer(t *testing.T, client *gophercloud.ServiceClient, server *servers.Server, choices *ComputeChoices) {
opts := &servers.ResizeOpts{
FlavorRef: choices.FlavorIDResize,
}
@@ -370,115 +459,33 @@
}
}
-func TestActionResizeConfirm(t *testing.T) {
- t.Parallel()
-
- choices, err := ComputeChoicesFromEnv()
+func deleteServer(t *testing.T, client *gophercloud.ServiceClient, server *servers.Server) {
+ err := servers.Delete(client, server.ID).ExtractErr()
if err != nil {
- t.Fatal(err)
+ t.Fatalf("Unable to delete server %s: %s", server.ID, err)
}
- client, err := newClient()
- if err != nil {
- t.Fatalf("Unable to create a compute client: %v", err)
- }
-
- server, err := createServer(t, client, choices)
- if err != nil {
- t.Fatal(err)
- }
- defer servers.Delete(client, server.ID)
- resizeServer(t, client, server, choices)
-
- t.Logf("Attempting to confirm resize for server %s", server.ID)
-
- if res := servers.ConfirmResize(client, server.ID); res.Err != nil {
- t.Fatal(err)
- }
-
- if err = waitForStatus(client, server, "ACTIVE"); err != nil {
- t.Fatal(err)
- }
+ t.Logf("Deleted server: %s", server.ID)
}
-func TestActionResizeRevert(t *testing.T) {
- t.Parallel()
-
- choices, err := ComputeChoicesFromEnv()
- if err != nil {
- t.Fatal(err)
- }
-
- client, err := newClient()
- if err != nil {
- t.Fatalf("Unable to create a compute client: %v", err)
- }
-
- server, err := createServer(t, client, choices)
- if err != nil {
- t.Fatal(err)
- }
- defer servers.Delete(client, server.ID)
- resizeServer(t, client, server, choices)
-
- t.Logf("Attempting to revert resize for server %s", server.ID)
-
- if res := servers.RevertResize(client, server.ID); res.Err != nil {
- t.Fatal(err)
- }
-
- if err = waitForStatus(client, server, "ACTIVE"); err != nil {
- t.Fatal(err)
- }
-}
-
-func TestServerMetadata(t *testing.T) {
- t.Parallel()
-
- choices, err := ComputeChoicesFromEnv()
- th.AssertNoErr(t, err)
-
- client, err := newClient()
- if err != nil {
- t.Fatalf("Unable to create a compute client: %v", err)
- }
-
- server, err := createServer(t, client, choices)
- if err != nil {
- t.Fatal(err)
- }
- defer servers.Delete(client, server.ID)
- if err = waitForStatus(client, server, "ACTIVE"); err != nil {
- t.Fatal(err)
- }
-
- metadata, err := servers.UpdateMetadata(client, server.ID, servers.MetadataOpts{
- "foo": "bar",
- "this": "that",
- }).Extract()
- th.AssertNoErr(t, err)
- t.Logf("UpdateMetadata result: %+v\n", metadata)
-
- err = servers.DeleteMetadatum(client, server.ID, "foo").ExtractErr()
- th.AssertNoErr(t, err)
-
- metadata, err = servers.CreateMetadatum(client, server.ID, servers.MetadatumOpts{
- "foo": "baz",
- }).Extract()
- th.AssertNoErr(t, err)
- t.Logf("CreateMetadatum result: %+v\n", metadata)
-
- metadata, err = servers.Metadatum(client, server.ID, "foo").Extract()
- th.AssertNoErr(t, err)
- t.Logf("Metadatum result: %+v\n", metadata)
- th.AssertEquals(t, "baz", metadata["foo"])
-
- metadata, err = servers.Metadata(client, server.ID).Extract()
- th.AssertNoErr(t, err)
- t.Logf("Metadata result: %+v\n", metadata)
-
- metadata, err = servers.ResetMetadata(client, server.ID, servers.MetadataOpts{}).Extract()
- th.AssertNoErr(t, err)
- t.Logf("ResetMetadata result: %+v\n", metadata)
- th.AssertDeepEquals(t, map[string]string{}, metadata)
+func printServer(t *testing.T, server *servers.Server) {
+ t.Logf("ID: %s", server.ID)
+ t.Logf("TenantID: %s", server.TenantID)
+ t.Logf("UserID: %s", server.UserID)
+ t.Logf("Name: %s", server.Name)
+ t.Logf("Updated: %s", server.Updated)
+ t.Logf("Created: %s", server.Created)
+ t.Logf("HostID: %s", server.HostID)
+ t.Logf("Status: %s", server.Status)
+ t.Logf("Progress: %d", server.Progress)
+ t.Logf("AccessIPv4: %s", server.AccessIPv4)
+ t.Logf("AccessIPv6: %s", server.AccessIPv6)
+ t.Logf("Image: %s", server.Image)
+ t.Logf("Flavor: %s", server.Flavor)
+ t.Logf("Addresses: %#v", server.Addresses)
+ t.Logf("Metadata: %#v", server.Metadata)
+ t.Logf("Links: %#v", server.Links)
+ t.Logf("KeyName: %s", server.KeyName)
+ t.Logf("AdminPass: %s", server.AdminPass)
+ t.Logf("SecurityGroups: %#v", server.SecurityGroups)
}
diff --git a/acceptance/openstack/compute/v2/tenantnetworks_test.go b/acceptance/openstack/compute/v2/tenantnetworks_test.go
index 58208c0..3092077 100644
--- a/acceptance/openstack/compute/v2/tenantnetworks_test.go
+++ b/acceptance/openstack/compute/v2/tenantnetworks_test.go
@@ -3,77 +3,35 @@
package v2
import (
- "os"
+ "fmt"
"testing"
"github.com/gophercloud/gophercloud"
- "github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/tenantnetworks"
- "github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
- th "github.com/gophercloud/gophercloud/testhelper"
)
-func getNetworkID(t *testing.T, client *gophercloud.ServiceClient, networkName string) (string, error) {
+func TestTenantNetworksList(t *testing.T) {
+ client, err := newClient()
+ if err != nil {
+ t.Fatalf("Unable to create a compute client: %v", err)
+ }
+
allPages, err := tenantnetworks.List(client).AllPages()
if err != nil {
t.Fatalf("Unable to list networks: %v", err)
}
- networkList, err := tenantnetworks.ExtractNetworks(allPages)
+ allTenantNetworks, err := tenantnetworks.ExtractNetworks(allPages)
if err != nil {
t.Fatalf("Unable to list networks: %v", err)
}
- networkID := ""
- for _, network := range networkList {
- t.Logf("Network: %v", network)
- if network.Name == networkName {
- networkID = network.ID
- }
+ for _, network := range allTenantNetworks {
+ printTenantNetwork(t, &network)
}
-
- t.Logf("Found network ID for %s: %s\n", networkName, networkID)
-
- return networkID, nil
}
-func createNetworkServer(t *testing.T, client *gophercloud.ServiceClient, choices *ComputeChoices, networkID string) (*servers.Server, error) {
- if testing.Short() {
- t.Skip("Skipping test that requires server creation in short mode.")
- }
-
- name := tools.RandomString("ACPTTEST", 16)
- t.Logf("Attempting to create server: %s\n", name)
-
- pwd := tools.MakeNewPassword("")
-
- networks := make([]servers.Network, 1)
- networks[0] = servers.Network{
- UUID: networkID,
- }
-
- server, err := servers.Create(client, servers.CreateOpts{
- Name: name,
- FlavorRef: choices.FlavorID,
- ImageRef: choices.ImageID,
- AdminPass: pwd,
- Networks: networks,
- }).Extract()
- if err != nil {
- t.Fatalf("Unable to create server: %v", err)
- }
-
- th.AssertEquals(t, pwd, server.AdminPass)
-
- return server, err
-}
-
-func TestTenantNetworks(t *testing.T) {
- networkName := os.Getenv("OS_NETWORK_NAME")
- if networkName == "" {
- t.Fatalf("OS_NETWORK_NAME must be set")
- }
-
+func TestTenantNetworksGet(t *testing.T) {
choices, err := ComputeChoicesFromEnv()
if err != nil {
t.Fatal(err)
@@ -84,26 +42,41 @@
t.Fatalf("Unable to create a compute client: %v", err)
}
- networkID, err := getNetworkID(t, client, networkName)
+ networkID, err := getNetworkIDFromTenantNetworks(t, client, choices.NetworkName)
if err != nil {
- t.Fatalf("Unable to get network ID: %v", err)
+ t.Fatal(err)
}
- server, err := createNetworkServer(t, client, choices, networkID)
+ network, err := tenantnetworks.Get(client, networkID).Extract()
if err != nil {
- t.Fatalf("Unable to create server: %v", err)
- }
- defer func() {
- servers.Delete(client, server.ID)
- t.Logf("Server deleted.")
- }()
-
- if err = waitForStatus(client, server, "ACTIVE"); err != nil {
- t.Fatalf("Unable to wait for server: %v", err)
+ t.Fatalf("Unable to get network %s: %v", networkID, err)
}
+ printTenantNetwork(t, network)
+}
+
+func getNetworkIDFromTenantNetworks(t *testing.T, client *gophercloud.ServiceClient, networkName string) (string, error) {
allPages, err := tenantnetworks.List(client).AllPages()
- allNetworks, err := tenantnetworks.ExtractNetworks(allPages)
- th.AssertNoErr(t, err)
- t.Logf("Retrieved all %d networks: %+v", len(allNetworks), allNetworks)
+ if err != nil {
+ t.Fatalf("Unable to list networks: %v", err)
+ }
+
+ allTenantNetworks, err := tenantnetworks.ExtractNetworks(allPages)
+ if err != nil {
+ t.Fatalf("Unable to list networks: %v", err)
+ }
+
+ for _, network := range allTenantNetworks {
+ if network.Name == networkName {
+ return network.ID, nil
+ }
+ }
+
+ return "", fmt.Errorf("Failed to obtain network ID for network %s", networkName)
+}
+
+func printTenantNetwork(t *testing.T, network *tenantnetworks.Network) {
+ t.Logf("ID: %s", network.ID)
+ t.Logf("Name: %s", network.Name)
+ t.Logf("CIDR: %s", network.CIDR)
}
diff --git a/acceptance/openstack/compute/v2/volumeattach_test.go b/acceptance/openstack/compute/v2/volumeattach_test.go
index 459d283..a1b10a9 100644
--- a/acceptance/openstack/compute/v2/volumeattach_test.go
+++ b/acceptance/openstack/compute/v2/volumeattach_test.go
@@ -1,125 +1,126 @@
-// +build acceptance compute servers
+// +build acceptance compute volumeattach
package v2
import (
- "os"
"testing"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/acceptance/tools"
- "github.com/gophercloud/gophercloud/openstack"
"github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach"
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
- th "github.com/gophercloud/gophercloud/testhelper"
)
-func newBlockClient(t *testing.T) (*gophercloud.ServiceClient, error) {
- ao, err := openstack.AuthOptionsFromEnv()
- th.AssertNoErr(t, err)
-
- client, err := openstack.AuthenticatedClient(ao)
- th.AssertNoErr(t, err)
-
- return openstack.NewBlockStorageV1(client, gophercloud.EndpointOpts{
- Region: os.Getenv("OS_REGION_NAME"),
- })
-}
-
-func createVAServer(t *testing.T, computeClient *gophercloud.ServiceClient, choices *ComputeChoices) (*servers.Server, error) {
+func TestVolumeAttachAttachment(t *testing.T) {
if testing.Short() {
t.Skip("Skipping test that requires server creation in short mode.")
}
- name := tools.RandomString("ACPTTEST", 16)
- t.Logf("Attempting to create server: %s\n", name)
-
- pwd := tools.MakeNewPassword("")
-
- server, err := servers.Create(computeClient, servers.CreateOpts{
- Name: name,
- FlavorRef: choices.FlavorID,
- ImageRef: choices.ImageID,
- AdminPass: pwd,
- }).Extract()
+ client, err := newClient()
if err != nil {
- t.Fatalf("Unable to create server: %v", err)
+ t.Fatalf("Unable to create a compute client: %v", err)
}
- th.AssertEquals(t, pwd, server.AdminPass)
-
- return server, err
-}
-
-func createVAVolume(t *testing.T, blockClient *gophercloud.ServiceClient) (*volumes.Volume, error) {
- volume, err := volumes.Create(blockClient, &volumes.CreateOpts{
- Size: 1,
- Name: "gophercloud-test-volume",
- }).Extract()
- th.AssertNoErr(t, err)
- defer func() {
- err = volumes.WaitForStatus(blockClient, volume.ID, "available", 60)
- th.AssertNoErr(t, err)
- }()
-
- return volume, err
-}
-
-func createVolumeAttachment(t *testing.T, computeClient *gophercloud.ServiceClient, blockClient *gophercloud.ServiceClient, serverId string, volumeId string) {
- va, err := volumeattach.Create(computeClient, serverId, &volumeattach.CreateOpts{
- VolumeID: volumeId,
- }).Extract()
- th.AssertNoErr(t, err)
- defer func() {
- err = volumes.WaitForStatus(blockClient, volumeId, "in-use", 60)
- th.AssertNoErr(t, err)
- err = volumeattach.Delete(computeClient, serverId, va.ID).ExtractErr()
- th.AssertNoErr(t, err)
- err = volumes.WaitForStatus(blockClient, volumeId, "available", 60)
- th.AssertNoErr(t, err)
- }()
-}
-
-func TestAttachVolume(t *testing.T) {
choices, err := ComputeChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
- computeClient, err := newClient()
- if err != nil {
- t.Fatalf("Unable to create a compute client: %v", err)
- }
-
- blockClient, err := newBlockClient(t)
+ blockClient, err := newBlockClient()
if err != nil {
t.Fatalf("Unable to create a blockstorage client: %v", err)
}
- server, err := createVAServer(t, computeClient, choices)
+ server, err := createServer(t, client, choices)
if err != nil {
t.Fatalf("Unable to create server: %v", err)
}
- defer func() {
- servers.Delete(computeClient, server.ID)
- t.Logf("Server deleted.")
- }()
- if err = waitForStatus(computeClient, server, "ACTIVE"); err != nil {
+ if err = waitForStatus(client, server, "ACTIVE"); err != nil {
t.Fatalf("Unable to wait for server: %v", err)
}
+ defer deleteServer(t, client, server)
- volume, err := createVAVolume(t, blockClient)
+ volume, err := createVolume(t, blockClient)
if err != nil {
t.Fatalf("Unable to create volume: %v", err)
}
- defer func() {
- err = volumes.Delete(blockClient, volume.ID).ExtractErr()
- th.AssertNoErr(t, err)
- t.Logf("Volume deleted.")
- }()
- createVolumeAttachment(t, computeClient, blockClient, server.ID, volume.ID)
+ if err = volumes.WaitForStatus(blockClient, volume.ID, "available", 60); err != nil {
+ t.Fatalf("Unable to wait for volume: %v", err)
+ }
+ defer deleteVolume(t, blockClient, volume)
+ volumeAttachment, err := createVolumeAttachment(t, client, blockClient, server, volume)
+ if err != nil {
+ t.Fatalf("Unable to attach volume: %v", err)
+ }
+ defer deleteVolumeAttachment(t, client, blockClient, server, volumeAttachment)
+
+ printVolumeAttachment(t, volumeAttachment)
+
+}
+
+func createVolume(t *testing.T, blockClient *gophercloud.ServiceClient) (*volumes.Volume, error) {
+ volumeName := tools.RandomString("ACPTTEST", 16)
+ createOpts := volumes.CreateOpts{
+ Size: 1,
+ Name: volumeName,
+ }
+
+ volume, err := volumes.Create(blockClient, createOpts).Extract()
+ if err != nil {
+ return volume, err
+ }
+
+ t.Logf("Created volume: %s", volume.ID)
+ return volume, nil
+}
+
+func deleteVolume(t *testing.T, blockClient *gophercloud.ServiceClient, volume *volumes.Volume) {
+ err := volumes.Delete(blockClient, volume.ID).ExtractErr()
+ if err != nil {
+ t.Fatalf("Unable to delete volume: %v", err)
+ }
+
+ t.Logf("Deleted volume: %s", volume.ID)
+}
+
+func createVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, blockClient *gophercloud.ServiceClient, server *servers.Server, volume *volumes.Volume) (*volumeattach.VolumeAttachment, error) {
+ volumeAttachOptions := volumeattach.CreateOpts{
+ VolumeID: volume.ID,
+ }
+
+ t.Logf("Attempting to attach volume %s to server %s", volume.ID, server.ID)
+ volumeAttachment, err := volumeattach.Create(client, server.ID, volumeAttachOptions).Extract()
+ if err != nil {
+ return volumeAttachment, err
+ }
+
+ if err = volumes.WaitForStatus(blockClient, volume.ID, "in-use", 60); err != nil {
+ return volumeAttachment, err
+ }
+
+ return volumeAttachment, nil
+}
+
+func deleteVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, blockClient *gophercloud.ServiceClient, server *servers.Server, volumeAttachment *volumeattach.VolumeAttachment) {
+
+ err := volumeattach.Delete(client, server.ID, volumeAttachment.VolumeID).ExtractErr()
+ if err != nil {
+ t.Fatalf("Unable to detach volume: %v", err)
+ }
+
+ if err = volumes.WaitForStatus(blockClient, volumeAttachment.ID, "available", 60); err != nil {
+ t.Fatalf("Unable to wait for volume: %v", err)
+ }
+ t.Logf("Deleted volume: %s", volumeAttachment.VolumeID)
+}
+
+func printVolumeAttachment(t *testing.T, volumeAttachment *volumeattach.VolumeAttachment) {
+ t.Logf("ID: %s", volumeAttachment.ID)
+ t.Logf("Device: %s", volumeAttachment.Device)
+ t.Logf("VolumeID: %s", volumeAttachment.VolumeID)
+ t.Logf("ServerID: %s", volumeAttachment.ServerID)
}
diff --git a/openstack/blockstorage/v1/volumes/results.go b/openstack/blockstorage/v1/volumes/results.go
index b056b8c..5c954bf 100644
--- a/openstack/blockstorage/v1/volumes/results.go
+++ b/openstack/blockstorage/v1/volumes/results.go
@@ -18,7 +18,7 @@
// Indicates whether this is a bootable volume.
Bootable string `json:"bootable"`
// The date when this volume was created.
- CreatedAt gophercloud.JSONRFC3339Milli `json:"created_at"`
+ CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"`
// Human-readable description for the volume.
Description string `json:"display_description"`
// The type of volume to create, either SATA or SSD.
diff --git a/openstack/blockstorage/v1/volumes/testing/fixtures.go b/openstack/blockstorage/v1/volumes/testing/fixtures.go
index 421cbf4..c841676 100644
--- a/openstack/blockstorage/v1/volumes/testing/fixtures.go
+++ b/openstack/blockstorage/v1/volumes/testing/fixtures.go
@@ -66,7 +66,7 @@
"device": "/"
}
],
- "created_at": "2012-02-14T20:53:07Z"
+ "created_at": "2012-02-14T20:53:07"
}
}
`)
diff --git a/openstack/blockstorage/v1/volumes/testing/requests_test.go b/openstack/blockstorage/v1/volumes/testing/requests_test.go
index 85a6cd8..898aca5 100644
--- a/openstack/blockstorage/v1/volumes/testing/requests_test.go
+++ b/openstack/blockstorage/v1/volumes/testing/requests_test.go
@@ -98,7 +98,7 @@
},
AvailabilityZone: "us-east1",
Bootable: "false",
- CreatedAt: gophercloud.JSONRFC3339Milli(time.Date(2012, 2, 14, 20, 53, 07, 0, time.UTC)),
+ CreatedAt: gophercloud.JSONRFC3339MilliNoZ(time.Date(2012, 2, 14, 20, 53, 07, 0, time.UTC)),
Description: "Another volume.",
VolumeType: "289da7f8-6440-407c-9fb4-7db01ec49164",
SnapshotID: "",
diff --git a/openstack/client.go b/openstack/client.go
index fdaee49..ebad6dc 100644
--- a/openstack/client.go
+++ b/openstack/client.go
@@ -175,16 +175,16 @@
}
v3Opts := tokens3.AuthOptions{
- IdentityEndpoint: options.IdentityEndpoint,
- Username: options.Username,
- UserID: options.UserID,
- Password: options.Password,
- DomainID: options.DomainID,
- DomainName: options.DomainName,
- TenantID: options.TenantID,
- TenantName: options.TenantName,
- AllowReauth: options.AllowReauth,
- TokenID: options.TokenID,
+ IdentityEndpoint: v3Options.IdentityEndpoint,
+ Username: v3Options.Username,
+ UserID: v3Options.UserID,
+ Password: v3Options.Password,
+ DomainID: v3Options.DomainID,
+ DomainName: v3Options.DomainName,
+ TenantID: v3Options.TenantID,
+ TenantName: v3Options.TenantName,
+ AllowReauth: v3Options.AllowReauth,
+ TokenID: v3Options.TokenID,
}
result := tokens3.Create(v3Client, v3Opts, scope)
diff --git a/openstack/compute/v2/servers/results.go b/openstack/compute/v2/servers/results.go
index 023d0dd..7659c75 100644
--- a/openstack/compute/v2/servers/results.go
+++ b/openstack/compute/v2/servers/results.go
@@ -3,13 +3,13 @@
import (
"crypto/rsa"
"encoding/base64"
+ "encoding/json"
"fmt"
"net/url"
"path"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/pagination"
- "github.com/mitchellh/mapstructure"
)
type serverResult struct {
@@ -75,20 +75,14 @@
// If privateKey == nil the encrypted password is returned and can be decrypted with:
// echo '<pwd>' | base64 -D | openssl rsautl -decrypt -inkey <private_key>
func (r GetPasswordResult) ExtractPassword(privateKey *rsa.PrivateKey) (string, error) {
-
- if r.Err != nil {
- return "", r.Err
+ var s struct {
+ Password string `json:"password"`
}
-
- var response struct {
- Password string `mapstructure:"password"`
+ err := r.ExtractInto(&s)
+ if err == nil && privateKey != nil && s.Password != "" {
+ return decryptPassword(s.Password, privateKey)
}
-
- err := mapstructure.Decode(r.Body, &response)
- if err == nil && privateKey != nil && response.Password != "" {
- return decryptPassword(response.Password, privateKey)
- }
- return response.Password, err
+ return s.Password, err
}
func decryptPassword(encryptedPassword string, privateKey *rsa.PrivateKey) (string, error) {
@@ -136,58 +130,68 @@
type Server struct {
// ID uniquely identifies this server amongst all other servers, including those not accessible to the current tenant.
ID string `json:"id"`
-
// TenantID identifies the tenant owning this server resource.
TenantID string `json:"tenant_id"`
-
// UserID uniquely identifies the user account owning the tenant.
UserID string `json:"user_id"`
-
// Name contains the human-readable name for the server.
Name string `json:"name"`
-
// Updated and Created contain ISO-8601 timestamps of when the state of the server last changed, and when it was created.
Updated string
Created string
-
- HostID string
-
+ HostID string
// Status contains the current operational status of the server, such as IN_PROGRESS or ACTIVE.
Status string
-
// Progress ranges from 0..100.
// A request made against the server completes only once Progress reaches 100.
Progress int
-
// AccessIPv4 and AccessIPv6 contain the IP addresses of the server, suitable for remote access for administration.
AccessIPv4, AccessIPv6 string
-
// Image refers to a JSON object, which itself indicates the OS image used to deploy the server.
Image map[string]interface{}
-
// Flavor refers to a JSON object, which itself indicates the hardware configuration of the deployed server.
Flavor map[string]interface{}
-
// Addresses includes a list of all IP addresses assigned to the server, keyed by pool.
Addresses map[string]interface{}
-
// Metadata includes a list of all user-specified key-value pairs attached to the server.
Metadata map[string]interface{}
-
// Links includes HTTP references to the itself, useful for passing along to other APIs that might want a server reference.
Links []interface{}
-
// KeyName indicates which public key was injected into the server on launch.
KeyName string `json:"key_name"`
-
// AdminPass will generally be empty (""). However, it will contain the administrative password chosen when provisioning a new server without a set AdminPass setting in the first place.
// Note that this is the ONLY time this field will be valid.
AdminPass string `json:"adminPass"`
-
// SecurityGroups includes the security groups that this instance has applied to it
SecurityGroups []map[string]interface{} `json:"security_groups"`
}
+func (s *Server) UnmarshalJSON(b []byte) error {
+ type tmp Server
+ var server *struct {
+ tmp
+ Image interface{}
+ }
+ err := json.Unmarshal(b, &server)
+ if err != nil {
+ return err
+ }
+
+ *s = Server(server.tmp)
+
+ switch t := server.Image.(type) {
+ case map[string]interface{}:
+ s.Image = t
+ case string:
+ switch t {
+ case "":
+ s.Image = nil
+ }
+ }
+
+ return nil
+}
+
// ServerPage abstracts the raw results of making a List() request against the API.
// As OpenStack extensions may freely alter the response bodies of structures returned to the client, you may only safely access the
// data provided through the ExtractServers call.
diff --git a/openstack/compute/v2/servers/testing/fixtures.go b/openstack/compute/v2/servers/testing/fixtures.go
index b4fb7ff..613dc1e 100644
--- a/openstack/compute/v2/servers/testing/fixtures.go
+++ b/openstack/compute/v2/servers/testing/fixtures.go
@@ -154,7 +154,69 @@
"OS-EXT-STS:power_state": 1,
"config_drive": "",
"metadata": {}
- }
+ },
+ {
+ "status": "ACTIVE",
+ "updated": "2014-09-25T13:04:49Z",
+ "hostId": "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362",
+ "OS-EXT-SRV-ATTR:host": "devstack",
+ "addresses": {
+ "private": [
+ {
+ "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:9e:89:be",
+ "version": 4,
+ "addr": "10.0.0.31",
+ "OS-EXT-IPS:type": "fixed"
+ }
+ ]
+ },
+ "links": [
+ {
+ "href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba",
+ "rel": "self"
+ },
+ {
+ "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba",
+ "rel": "bookmark"
+ }
+ ],
+ "key_name": null,
+ "image": "",
+ "OS-EXT-STS:task_state": null,
+ "OS-EXT-STS:vm_state": "active",
+ "OS-EXT-SRV-ATTR:instance_name": "instance-0000001d",
+ "OS-SRV-USG:launched_at": "2014-09-25T13:04:49.000000",
+ "OS-EXT-SRV-ATTR:hypervisor_hostname": "devstack",
+ "flavor": {
+ "id": "1",
+ "links": [
+ {
+ "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1",
+ "rel": "bookmark"
+ }
+ ]
+ },
+ "id": "9e5476bd-a4ec-4653-93d6-72c93aa682bb",
+ "security_groups": [
+ {
+ "name": "default"
+ }
+ ],
+ "OS-SRV-USG:terminated_at": null,
+ "OS-EXT-AZ:availability_zone": "nova",
+ "user_id": "9349aff8be7545ac9d2f1d00999a23cd",
+ "name": "merp",
+ "created": "2014-09-25T13:04:41Z",
+ "tenant_id": "fcad67a6189847c4aecfa3c81a05783b",
+ "OS-DCF:diskConfig": "MANUAL",
+ "os-extended-volumes:volumes_attached": [],
+ "accessIPv4": "",
+ "accessIPv6": "",
+ "progress": 0,
+ "OS-EXT-STS:power_state": 1,
+ "config_drive": "",
+ "metadata": {}
+ }
]
}
`
@@ -353,6 +415,54 @@
},
},
}
+
+ // ServerMerp is a Server struct that should correspond to the second server in ServerListBody.
+ ServerMerp = servers.Server{
+ Status: "ACTIVE",
+ Updated: "2014-09-25T13:04:49Z",
+ HostID: "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362",
+ Addresses: map[string]interface{}{
+ "private": []interface{}{
+ map[string]interface{}{
+ "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:9e:89:be",
+ "version": float64(4),
+ "addr": "10.0.0.31",
+ "OS-EXT-IPS:type": "fixed",
+ },
+ },
+ },
+ Links: []interface{}{
+ map[string]interface{}{
+ "href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba",
+ "rel": "self",
+ },
+ map[string]interface{}{
+ "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba",
+ "rel": "bookmark",
+ },
+ },
+ Image: nil,
+ Flavor: map[string]interface{}{
+ "id": "1",
+ "links": []interface{}{
+ map[string]interface{}{
+ "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1",
+ "rel": "bookmark",
+ },
+ },
+ },
+ ID: "9e5476bd-a4ec-4653-93d6-72c93aa682bb",
+ UserID: "9349aff8be7545ac9d2f1d00999a23cd",
+ Name: "merp",
+ Created: "2014-09-25T13:04:41Z",
+ TenantID: "fcad67a6189847c4aecfa3c81a05783b",
+ Metadata: map[string]interface{}{},
+ SecurityGroups: []map[string]interface{}{
+ map[string]interface{}{
+ "name": "default",
+ },
+ },
+ }
)
type CreateOptsWithCustomField struct {
diff --git a/openstack/compute/v2/servers/testing/requests_test.go b/openstack/compute/v2/servers/testing/requests_test.go
index 7db6b93..6d792d5 100644
--- a/openstack/compute/v2/servers/testing/requests_test.go
+++ b/openstack/compute/v2/servers/testing/requests_test.go
@@ -26,11 +26,12 @@
return false, err
}
- if len(actual) != 2 {
- t.Fatalf("Expected 2 servers, got %d", len(actual))
+ if len(actual) != 3 {
+ t.Fatalf("Expected 3 servers, got %d", len(actual))
}
th.CheckDeepEquals(t, ServerHerp, actual[0])
th.CheckDeepEquals(t, ServerDerp, actual[1])
+ th.CheckDeepEquals(t, ServerMerp, actual[2])
return true, nil
})
diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go
index ce493c9..bc4a3c6 100644
--- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go
+++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go
@@ -22,6 +22,7 @@
TenantID string `q:"tenant_id"`
ProvisioningStatus string `q:"provisioning_status"`
VipAddress string `q:"vip_address"`
+ VipPortID string `q:"vip_port_id"`
VipSubnetID string `q:"vip_subnet_id"`
ID string `q:"id"`
OperatingStatus string `q:"operating_status"`
diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go
index 168e531..4423c24 100644
--- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go
+++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go
@@ -20,6 +20,8 @@
ProvisioningStatus string `json:"provisioning_status"`
// The IP address of the Loadbalancer.
VipAddress string `json:"vip_address"`
+ // The UUID of the port associated with the IP address.
+ VipPortID string `json:"vip_port_id"`
// The UUID of the subnet on which to allocate the virtual IP for the Loadbalancer address.
VipSubnetID string `json:"vip_subnet_id"`
// The unique ID for the LoadBalancer.
diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures.go
index f882949..a452236 100644
--- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures.go
+++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures.go
@@ -25,6 +25,7 @@
"description": "lb config for the web tier",
"vip_subnet_id": "8a49c438-848f-467b-9655-ea1548708154",
"vip_address": "10.30.176.47",
+ "vip_port_id": "2a22e552-a347-44fd-b530-1f2b1b2a6735",
"flavor": "small",
"provider": "haproxy",
"admin_state_up": true,
@@ -38,6 +39,7 @@
"description": "lb config for the db tier",
"vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
"vip_address": "10.30.176.48",
+ "vip_port_id": "2bf413c8-41a9-4477-b505-333d5cbe8b55",
"flavor": "medium",
"provider": "haproxy",
"admin_state_up": true,
@@ -58,6 +60,7 @@
"description": "lb config for the db tier",
"vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
"vip_address": "10.30.176.48",
+ "vip_port_id": "2bf413c8-41a9-4477-b505-333d5cbe8b55",
"flavor": "medium",
"provider": "haproxy",
"admin_state_up": true,
@@ -77,6 +80,7 @@
"description": "lb config for the db tier",
"vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
"vip_address": "10.30.176.48",
+ "vip_port_id": "2bf413c8-41a9-4477-b505-333d5cbe8b55",
"flavor": "medium",
"provider": "haproxy",
"admin_state_up": true,
@@ -126,6 +130,7 @@
Description: "lb config for the web tier",
VipSubnetID: "8a49c438-848f-467b-9655-ea1548708154",
VipAddress: "10.30.176.47",
+ VipPortID: "2a22e552-a347-44fd-b530-1f2b1b2a6735",
Flavor: "small",
Provider: "haproxy",
AdminStateUp: true,
@@ -139,6 +144,7 @@
Description: "lb config for the db tier",
VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
VipAddress: "10.30.176.48",
+ VipPortID: "2bf413c8-41a9-4477-b505-333d5cbe8b55",
Flavor: "medium",
Provider: "haproxy",
AdminStateUp: true,
@@ -152,6 +158,7 @@
Description: "lb config for the db tier",
VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086",
VipAddress: "10.30.176.48",
+ VipPortID: "2bf413c8-41a9-4477-b505-333d5cbe8b55",
Flavor: "medium",
Provider: "haproxy",
AdminStateUp: true,
diff --git a/openstack/testing/client_test.go b/openstack/testing/client_test.go
index 93f4d7c..570b3a0 100644
--- a/openstack/testing/client_test.go
+++ b/openstack/testing/client_test.go
@@ -49,8 +49,10 @@
})
options := gophercloud.AuthOptions{
- UserID: "me",
+ Username: "me",
Password: "secret",
+ DomainName: "default",
+ TenantName: "project",
IdentityEndpoint: th.Endpoint(),
}
client, err := openstack.AuthenticatedClient(options)
diff --git a/script/acceptancetest_environments/keystonev2-lbaasv1.sh b/script/acceptancetest_environments/keystonev2-lbaasv1.sh
new file mode 100644
index 0000000..a68a346
--- /dev/null
+++ b/script/acceptancetest_environments/keystonev2-lbaasv1.sh
@@ -0,0 +1,175 @@
+#!/bin/bash
+#
+# This script is useful for creating a devstack environment to run gophercloud
+# acceptance tests on.
+#
+# This can be considered a "legacy" devstack environment since it uses
+# Keystone v2 and LBaaS v1.
+#
+# To run, simply execute this script within a virtual machine.
+#
+# The following OpenStack versions are installed:
+# * OpenStack Mitaka
+# * Keystone v2
+# * Glance v1 and v2
+# * Nova v2 and v2.1
+# * Cinder v1 and v2
+# * Trove v1
+# * Swift v1
+# * Neutron v2
+# * Neutron LBaaS v1.0
+# * Neutron FWaaS v2.0
+#
+# Go 1.6 is also installed.
+
+set -e
+
+cd
+sudo apt-get update
+sudo apt-get install -y git make mercurial
+
+sudo wget -O /usr/local/bin/gimme https://raw.githubusercontent.com/travis-ci/gimme/master/gimme
+sudo chmod +x /usr/local/bin/gimme
+gimme 1.6 >> .bashrc
+
+mkdir ~/go
+eval "$(/usr/local/bin/gimme 1.6)"
+echo 'export GOPATH=$HOME/go' >> .bashrc
+export GOPATH=$HOME/go
+source .bashrc
+
+git clone https://git.openstack.org/openstack-dev/devstack -b stable/mitaka
+cd devstack
+cat >local.conf <<EOF
+[[local|localrc]]
+# OpenStack version
+OPENSTACK_VERSION="mitaka"
+
+# devstack password
+DEVSTACK_PASSWORD="password"
+
+# Configure passwords and the Swift Hash
+MYSQL_PASSWORD=\$DEVSTACK_PASSWORD
+RABBIT_PASSWORD=\$DEVSTACK_PASSWORD
+SERVICE_TOKEN=\$DEVSTACK_PASSWORD
+ADMIN_PASSWORD=\$DEVSTACK_PASSWORD
+SERVICE_PASSWORD=\$DEVSTACK_PASSWORD
+SWIFT_HASH=\$DEVSTACK_PASSWORD
+
+# Configure the stable OpenStack branches used by DevStack
+# For stable branches see
+# http://git.openstack.org/cgit/openstack-dev/devstack/refs/
+CINDER_BRANCH=stable/\$OPENSTACK_VERSION
+CEILOMETER_BRANCH=stable/\$OPENSTACK_VERSION
+GLANCE_BRANCH=stable/\$OPENSTACK_VERSION
+HEAT_BRANCH=stable/\$OPENSTACK_VERSION
+HORIZON_BRANCH=stable/\$OPENSTACK_VERSION
+KEYSTONE_BRANCH=stable/\$OPENSTACK_VERSION
+NEUTRON_BRANCH=stable/\$OPENSTACK_VERSION
+NOVA_BRANCH=stable/\$OPENSTACK_VERSION
+SWIFT_BRANCH=stable/\$OPENSTACK_VERSION
+ZAQAR_BRANCH=stable/\$OPENSTACK_VERSION
+
+# Enable Swift
+enable_service s-proxy
+enable_service s-object
+enable_service s-container
+enable_service s-account
+
+# Disable Nova Network and enable Neutron
+disable_service n-net
+enable_service q-svc
+enable_service q-agt
+enable_service q-dhcp
+enable_service q-l3
+enable_service q-meta
+#enable_service q-flavors
+
+# Disable Neutron metering
+disable_service q-metering
+
+# Enable LBaaS V1
+enable_service q-lbaas
+
+# Enable FWaaS
+enable_service q-fwaas
+
+# Enable LBaaS v2
+#enable_plugin neutron-lbaas https://git.openstack.org/openstack/neutron-lbaas stable/\$OPENSTACK_VERSION
+#enable_plugin octavia https://git.openstack.org/openstack/octavia stable/\$OPENSTACK_VERSION
+#enable_service q-lbaasv2
+#enable_service octavia
+#enable_service o-cw
+#enable_service o-hk
+#enable_service o-hm
+#enable_service o-api
+
+# Enable Trove
+enable_plugin trove git://git.openstack.org/openstack/trove.git stable/\$OPENSTACK_VERSION
+enable_service trove,tr-api,tr-tmgr,tr-cond
+
+# Disable Temptest
+disable_service tempest
+
+# Disable Horizon
+disable_service horizon
+
+# Disable Keystone v2
+#ENABLE_IDENTITY_V2=False
+
+# Enable SSL/tls
+#enable_service tls-proxy
+#USE_SSL=True
+
+# Enable Ceilometer
+#enable_service ceilometer-acompute
+#enable_service ceilometer-acentral
+#enable_service ceilometer-anotification
+#enable_service ceilometer-collector
+#enable_service ceilometer-alarm-evaluator
+#enable_service ceilometer-alarm-notifier
+#enable_service ceilometer-api
+
+# Enable Zaqar
+#enable_plugin zaqar https://github.com/openstack/zaqar
+#enable_service zaqar-server
+
+# Automatically download and register a VM image that Heat can launch
+# For more information on Heat and DevStack see
+# http://docs.openstack.org/developer/heat/getting_started/on_devstack.html
+#IMAGE_URLS+=",http://cloud.fedoraproject.org/fedora-20.x86_64.qcow2"
+#IMAGE_URLS+=",https://cloud-images.ubuntu.com/trusty/current/trusty-server-cloudimg-amd64-disk1.img"
+
+# Logging
+LOGDAYS=1
+LOGFILE=/opt/stack/logs/stack.sh.log
+LOGDIR=/opt/stack/logs
+EOF
+./stack.sh
+
+# Patch openrc
+#cat >> openrc <<EOF
+#
+# Currently, in order to use openstackclient with Identity API v3,
+# we need to set the domain which the user and project belong to.
+#if [ "$OS_IDENTITY_API_VERSION" = "3" ]; then
+# export OS_USER_DOMAIN_ID=${OS_USER_DOMAIN_ID:-"default"}
+# export OS_PROJECT_DOMAIN_ID=${OS_PROJECT_DOMAIN_ID:-"default"}
+#fi
+#EOF
+
+# Prep the testing environment by creating the required testing resources and environment variables
+source openrc admin
+wget http://download.cirros-cloud.net/0.3.4/cirros-0.3.4-x86_64-disk.img
+glance image-create --name CirrOS --disk-format qcow2 --container-format bare < cirros-0.3.4-x86_64-disk.img
+nova flavor-create m1.tform 99 512 5 1 --ephemeral 10
+_NETWORK_ID=$(nova net-list | grep private | awk -F\| '{print $2}' | tr -d ' ')
+_EXTGW_ID=$(nova net-list | grep public | awk -F\| '{print $2}' | tr -d ' ')
+_IMAGE_ID=$(nova image-list | grep CirrOS | awk -F\| '{print $2}' | tr -d ' ' | head -1)
+echo export OS_IMAGE_NAME="cirros-0.3.4-x86_64-uec" >> openrc
+echo export OS_IMAGE_ID="$_IMAGE_ID" >> openrc
+echo export OS_NETWORK_ID=$_NETWORK_ID >> openrc
+echo export OS_EXTGW_ID=$_EXTGW_ID >> openrc
+echo export OS_POOL_NAME="public" >> openrc
+echo export OS_FLAVOR_ID=99 >> openrc
+source openrc demo