Networking Acceptance Test Cleanup (#79)

diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go
index 9efa901..86871aa 100644
--- a/acceptance/clients/clients.go
+++ b/acceptance/clients/clients.go
@@ -29,6 +29,9 @@
 
 	// NetworkName is the name of a network to launch the instance on.
 	NetworkName string
+
+	// ExternalNetworkID is the network ID of the external network.
+	ExternalNetworkID string
 }
 
 // AcceptanceTestChoicesFromEnv populates a ComputeChoices struct from environment variables.
@@ -39,6 +42,7 @@
 	flavorIDResize := os.Getenv("OS_FLAVOR_ID_RESIZE")
 	networkName := os.Getenv("OS_NETWORK_NAME")
 	floatingIPPoolName := os.Getenv("OS_POOL_NAME")
+	externalNetworkID := os.Getenv("OS_EXTGW_ID")
 
 	missing := make([]string, 0, 3)
 	if imageID == "" {
@@ -53,6 +57,9 @@
 	if floatingIPPoolName == "" {
 		missing = append(missing, "OS_POOL_NAME")
 	}
+	if externalNetworkID == "" {
+		missing = append(missing, "OS_EXTGW_ID")
+	}
 	if networkName == "" {
 		networkName = "private"
 	}
@@ -74,7 +81,7 @@
 		return nil, fmt.Errorf(text)
 	}
 
-	return &AcceptanceTestChoices{ImageID: imageID, FlavorID: flavorID, FlavorIDResize: flavorIDResize, FloatingIPPoolName: floatingIPPoolName, NetworkName: networkName}, nil
+	return &AcceptanceTestChoices{ImageID: imageID, FlavorID: flavorID, FlavorIDResize: flavorIDResize, FloatingIPPoolName: floatingIPPoolName, NetworkName: networkName, ExternalNetworkID: externalNetworkID}, nil
 }
 
 // NewBlockStorageV1Client returns a *ServiceClient for making calls
@@ -225,3 +232,22 @@
 
 	return openstack.NewIdentityV3(client, gophercloud.EndpointOpts{})
 }
+
+// NewNetworkV2Client returns a *ServiceClient for making calls to the
+// OpenStack Networking v2 API. An error will be returned if authentication
+// or client creation was not possible.
+func NewNetworkV2Client() (*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.NewNetworkV2(client, gophercloud.EndpointOpts{
+		Region: os.Getenv("OS_REGION_NAME"),
+	})
+}
diff --git a/acceptance/openstack/networking/v2/apiversion_test.go b/acceptance/openstack/networking/v2/apiversion_test.go
index 22827d6..409550c 100644
--- a/acceptance/openstack/networking/v2/apiversion_test.go
+++ b/acceptance/openstack/networking/v2/apiversion_test.go
@@ -5,47 +5,48 @@
 import (
 	"testing"
 
+	"github.com/gophercloud/gophercloud/acceptance/clients"
 	"github.com/gophercloud/gophercloud/openstack/networking/v2/apiversions"
-	"github.com/gophercloud/gophercloud/pagination"
-	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
-func TestListAPIVersions(t *testing.T) {
-	Setup(t)
-	defer Teardown()
+func TestAPIVersionsList(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
 
-	pager := apiversions.ListVersions(Client)
-	err := pager.EachPage(func(page pagination.Page) (bool, error) {
-		t.Logf("--- Page ---")
+	allPages, err := apiversions.ListVersions(client).AllPages()
+	if err != nil {
+		t.Fatalf("Unable to list api versions: %v", err)
+	}
 
-		versions, err := apiversions.ExtractAPIVersions(page)
-		th.AssertNoErr(t, err)
+	allAPIVersions, err := apiversions.ExtractAPIVersions(allPages)
+	if err != nil {
+		t.Fatalf("Unable to extract api versions: %v", err)
+	}
 
-		for _, v := range versions {
-			t.Logf("API Version: ID [%s] Status [%s]", v.ID, v.Status)
-		}
-
-		return true, nil
-	})
-	th.CheckNoErr(t, err)
+	for _, apiVersion := range allAPIVersions {
+		PrintAPIVersion(t, &apiVersion)
+	}
 }
 
-func TestListAPIResources(t *testing.T) {
-	Setup(t)
-	defer Teardown()
+func TestAPIResourcesList(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
 
-	pager := apiversions.ListVersionResources(Client, "v2.0")
-	err := pager.EachPage(func(page pagination.Page) (bool, error) {
-		t.Logf("--- Page ---")
+	allPages, err := apiversions.ListVersionResources(client, "v2.0").AllPages()
+	if err != nil {
+		t.Fatalf("Unable to list api version reosources: %v", err)
+	}
 
-		vrs, err := apiversions.ExtractVersionResources(page)
-		th.AssertNoErr(t, err)
+	allVersionResources, err := apiversions.ExtractVersionResources(allPages)
+	if err != nil {
+		t.Fatalf("Unable to extract version resources: %v", err)
+	}
 
-		for _, vr := range vrs {
-			t.Logf("Network: Name [%s] Collection [%s]", vr.Name, vr.Collection)
-		}
-
-		return true, nil
-	})
-	th.CheckNoErr(t, err)
+	for _, versionResource := range allVersionResources {
+		PrintVersionResource(t, &versionResource)
+	}
 }
diff --git a/acceptance/openstack/networking/v2/common.go b/acceptance/openstack/networking/v2/common.go
deleted file mode 100644
index b48855b..0000000
--- a/acceptance/openstack/networking/v2/common.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package v2
-
-import (
-	"os"
-	"testing"
-
-	"github.com/gophercloud/gophercloud"
-	"github.com/gophercloud/gophercloud/openstack"
-	th "github.com/gophercloud/gophercloud/testhelper"
-)
-
-var Client *gophercloud.ServiceClient
-
-func NewClient() (*gophercloud.ServiceClient, error) {
-	opts, err := openstack.AuthOptionsFromEnv()
-	if err != nil {
-		return nil, err
-	}
-
-	provider, err := openstack.AuthenticatedClient(opts)
-	if err != nil {
-		return nil, err
-	}
-
-	return openstack.NewNetworkV2(provider, gophercloud.EndpointOpts{
-		Name:   "neutron",
-		Region: os.Getenv("OS_REGION_NAME"),
-	})
-}
-
-func Setup(t *testing.T) {
-	client, err := NewClient()
-	th.AssertNoErr(t, err)
-	Client = client
-}
-
-func Teardown() {
-	Client = nil
-}
diff --git a/acceptance/openstack/networking/v2/extension_test.go b/acceptance/openstack/networking/v2/extension_test.go
index e125034..baade1e 100644
--- a/acceptance/openstack/networking/v2/extension_test.go
+++ b/acceptance/openstack/networking/v2/extension_test.go
@@ -1,45 +1,46 @@
-// +build acceptance networking
+// +build acceptance networking extensions
 
 package v2
 
 import (
 	"testing"
 
-	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions"
-	"github.com/gophercloud/gophercloud/pagination"
-	th "github.com/gophercloud/gophercloud/testhelper"
+	"github.com/gophercloud/gophercloud/acceptance/clients"
+	"github.com/gophercloud/gophercloud/acceptance/openstack"
+	"github.com/gophercloud/gophercloud/openstack/common/extensions"
 )
 
-func TestListExts(t *testing.T) {
-	Setup(t)
-	defer Teardown()
+func TestExtensionsList(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
 
-	pager := extensions.List(Client)
-	err := pager.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 _, ext := range exts {
-			t.Logf("Extension: Name [%s] Description [%s]", ext.Name, ext.Description)
-		}
-
-		return true, nil
-	})
-	th.CheckNoErr(t, err)
+	for _, extension := range allExtensions {
+		openstack.PrintExtension(t, &extension)
+	}
 }
 
-func TestGetExt(t *testing.T) {
-	Setup(t)
-	defer Teardown()
+func TestExtensionGet(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
 
-	ext, err := extensions.Get(Client, "service-type").Extract()
-	th.AssertNoErr(t, err)
+	extension, err := extensions.Get(client, "router").Extract()
+	if err != nil {
+		t.Fatalf("Unable to get extension port-security: %v", err)
+	}
 
-	th.AssertEquals(t, ext.Updated, "2013-01-20T00:00:00-00:00")
-	th.AssertEquals(t, ext.Name, "Neutron Service Type Management")
-	th.AssertEquals(t, ext.Namespace, "http://docs.openstack.org/ext/neutron/service-type/api/v1.0")
-	th.AssertEquals(t, ext.Alias, "service-type")
-	th.AssertEquals(t, ext.Description, "API for retrieving service providers for Neutron advanced services")
+	openstack.PrintExtension(t, extension)
 }
diff --git a/acceptance/openstack/networking/v2/extensions/extensions.go b/acceptance/openstack/networking/v2/extensions/extensions.go
new file mode 100644
index 0000000..f18e430
--- /dev/null
+++ b/acceptance/openstack/networking/v2/extensions/extensions.go
@@ -0,0 +1,180 @@
+package extensions
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/acceptance/tools"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/external"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/provider"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
+)
+
+// CreateExternalNetwork will create an external network. An error will be
+// returned if the creation failed.
+func CreateExternalNetwork(t *testing.T, client *gophercloud.ServiceClient) (*networks.Network, error) {
+	networkName := tools.RandomString("TESTACC-", 8)
+
+	t.Logf("Attempting to create external network: %s", networkName)
+
+	adminStateUp := true
+	isExternal := true
+	createOpts := external.CreateOpts{
+		External: &isExternal,
+	}
+
+	createOpts.Name = networkName
+	createOpts.AdminStateUp = &adminStateUp
+
+	network, err := networks.Create(client, createOpts).Extract()
+	if err != nil {
+		return network, err
+	}
+
+	t.Logf("Created external network: %s", networkName)
+
+	return network, nil
+}
+
+// CreatePortWithSecurityGroup will create a port with a security group
+// attached. An error will be returned if the port could not be created.
+func CreatePortWithSecurityGroup(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID, secGroupID string) (*ports.Port, error) {
+	portName := tools.RandomString("TESTACC-", 8)
+	iFalse := false
+
+	t.Logf("Attempting to create port: %s", portName)
+
+	createOpts := ports.CreateOpts{
+		NetworkID:      networkID,
+		Name:           portName,
+		AdminStateUp:   &iFalse,
+		FixedIPs:       []ports.IP{ports.IP{SubnetID: subnetID}},
+		SecurityGroups: []string{secGroupID},
+	}
+
+	port, err := ports.Create(client, createOpts).Extract()
+	if err != nil {
+		return port, err
+	}
+
+	t.Logf("Successfully created port: %s", portName)
+
+	return port, nil
+}
+
+// CreateSecurityGroup will create a security group with a random name.
+// An error will be returned if one was failed to be created.
+func CreateSecurityGroup(t *testing.T, client *gophercloud.ServiceClient) (*groups.SecGroup, error) {
+	secGroupName := tools.RandomString("TESTACC-", 8)
+
+	t.Logf("Attempting to create security group: %s", secGroupName)
+
+	createOpts := groups.CreateOpts{
+		Name: secGroupName,
+	}
+
+	secGroup, err := groups.Create(client, createOpts).Extract()
+	if err != nil {
+		return secGroup, err
+	}
+
+	t.Logf("Created security group: %s", secGroup.ID)
+
+	return secGroup, nil
+}
+
+// CreateSecurityGroupRule will create a security group rule with a random name
+// and random port between 80 and 99.
+// An error will be returned if one was failed to be created.
+func CreateSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, secGroupID string) (*rules.SecGroupRule, error) {
+	t.Logf("Attempting to create security group rule in group: %s", secGroupID)
+
+	fromPort := tools.RandomInt(80, 89)
+	toPort := tools.RandomInt(90, 99)
+
+	createOpts := rules.CreateOpts{
+		Direction:    "ingress",
+		EtherType:    "IPv4",
+		SecGroupID:   secGroupID,
+		PortRangeMin: fromPort,
+		PortRangeMax: toPort,
+		Protocol:     rules.ProtocolTCP,
+	}
+
+	rule, err := rules.Create(client, createOpts).Extract()
+	if err != nil {
+		return rule, err
+	}
+
+	t.Logf("Created security group rule: %s", rule.ID)
+
+	return rule, nil
+}
+
+// DeleteSecurityGroup will delete a security group of a specified ID.
+// A fatal error will occur if the deletion failed. This works best as a
+// deferred function
+func DeleteSecurityGroup(t *testing.T, client *gophercloud.ServiceClient, secGroupID string) {
+	t.Logf("Attempting to delete security group: %s", secGroupID)
+
+	err := groups.Delete(client, secGroupID).ExtractErr()
+	if err != nil {
+		t.Fatalf("Unable to delete security group: %v", err)
+	}
+}
+
+// DeleteSecurityGroupRule will delete a security group rule of a specified ID.
+// A fatal error will occur if the deletion failed. This works best as a
+// deferred function
+func DeleteSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, ruleID string) {
+	t.Logf("Attempting to delete security group rule: %s", ruleID)
+
+	err := rules.Delete(client, ruleID).ExtractErr()
+	if err != nil {
+		t.Fatalf("Unable to delete security group rule: %v", err)
+	}
+}
+
+// PrintNetworkExtAttrs prints a network and all of its extra attributes.
+func PrintNetworkExtAttrs(t *testing.T, network *provider.NetworkExtAttrs) {
+	t.Logf("ID: %s", network.ID)
+	t.Logf("Name: %s", network.Name)
+	t.Logf("AdminStateUp: %t", network.AdminStateUp)
+	t.Logf("Status: %s", network.Status)
+	t.Logf("Subnets: %s", network.Subnets)
+	t.Logf("TenantID: %s", network.TenantID)
+	t.Logf("Shared: %t", network.Shared)
+	t.Logf("NetworkType: %s", network.NetworkType)
+	t.Logf("PhysicalNetwork: %s", network.PhysicalNetwork)
+	t.Logf("SegmentationID: %d", network.SegmentationID)
+}
+
+// PrintSecurityGroup will print a security group and all of its attributes.
+func PrintSecurityGroup(t *testing.T, secGroup *groups.SecGroup) {
+	t.Logf("ID: %s", secGroup.ID)
+	t.Logf("Name: %s", secGroup.Name)
+	t.Logf("Description: %s", secGroup.Description)
+	t.Logf("TenantID: %s", secGroup.TenantID)
+	t.Logf("Rules:")
+
+	for _, rule := range secGroup.Rules {
+		PrintSecurityGroupRule(t, &rule)
+	}
+}
+
+// PrintSecurityGroupRule will print a security group rule and all of its attributes.
+func PrintSecurityGroupRule(t *testing.T, rule *rules.SecGroupRule) {
+	t.Logf("ID: %s", rule.ID)
+	t.Logf("Direction: %s", rule.Direction)
+	t.Logf("EtherType: %s", rule.EtherType)
+	t.Logf("SecGroupID: %s", rule.SecGroupID)
+	t.Logf("PortRangeMin: %d", rule.PortRangeMin)
+	t.Logf("PortRangeMax: %d", rule.PortRangeMax)
+	t.Logf("Protocol: %s", rule.Protocol)
+	t.Logf("RemoteGroupID: %s", rule.RemoteGroupID)
+	t.Logf("RemoteIPPrefix: %s", rule.RemoteIPPrefix)
+	t.Logf("TenantID: %s", rule.TenantID)
+}
diff --git a/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go b/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go
index ef1fb1a..976eb79 100644
--- a/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go
+++ b/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go
@@ -4,113 +4,76 @@
 
 import (
 	"testing"
-	"time"
 
-	"github.com/gophercloud/gophercloud"
-	base "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
+	"github.com/gophercloud/gophercloud/acceptance/clients"
 	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls"
-	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/policies"
-	"github.com/gophercloud/gophercloud/pagination"
-	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
-func firewallSetup(t *testing.T) string {
-	base.Setup(t)
-	return createPolicy(t, &policies.CreateOpts{})
-}
+func TestFirewallList(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
 
-func firewallTeardown(t *testing.T, policyID string) {
-	defer base.Teardown()
-	deletePolicy(t, policyID)
-}
+	allPages, err := firewalls.List(client, nil).AllPages()
+	if err != nil {
+		t.Fatalf("Unable to list firewalls: %v", err)
+	}
 
-func TestFirewall(t *testing.T) {
-	policyID := firewallSetup(t)
-	defer firewallTeardown(t, policyID)
+	allFirewalls, err := firewalls.ExtractFirewalls(allPages)
+	if err != nil {
+		t.Fatalf("Unable to extract firewalls: %v", err)
+	}
 
-	firewallID := createFirewall(t, &firewalls.CreateOpts{
-		Name:        "gophercloud test",
-		Description: "acceptance test",
-		PolicyID:    policyID,
-	})
-
-	waitForFirewallToBeActive(t, firewallID)
-
-	listFirewalls(t)
-
-	updateFirewall(t, firewallID, &firewalls.UpdateOpts{
-		Description: "acceptance test updated",
-	})
-
-	waitForFirewallToBeActive(t, firewallID)
-
-	deleteFirewall(t, firewallID)
-
-	waitForFirewallToBeDeleted(t, firewallID)
-}
-
-func createFirewall(t *testing.T, opts *firewalls.CreateOpts) string {
-	f, err := firewalls.Create(base.Client, *opts).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Created firewall: %#v", opts)
-	return f.ID
-}
-
-func listFirewalls(t *testing.T) {
-	err := firewalls.List(base.Client, firewalls.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		firewallList, err := firewalls.ExtractFirewalls(page)
-		if err != nil {
-			t.Errorf("Failed to extract firewalls: %v", err)
-			return false, err
-		}
-
-		for _, r := range firewallList {
-			t.Logf("Listing firewalls: ID [%s]", r.ID)
-		}
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-}
-
-func updateFirewall(t *testing.T, firewallID string, opts *firewalls.UpdateOpts) {
-	f, err := firewalls.Update(base.Client, firewallID, *opts).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Updated firewall ID [%s]", f.ID)
-}
-
-func getFirewall(t *testing.T, firewallID string) *firewalls.Firewall {
-	f, err := firewalls.Get(base.Client, firewallID).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Getting firewall ID [%s]", f.ID)
-	return f
-}
-
-func deleteFirewall(t *testing.T, firewallID string) {
-	res := firewalls.Delete(base.Client, firewallID)
-	th.AssertNoErr(t, res.Err)
-	t.Logf("Deleted firewall %s", firewallID)
-}
-
-func waitForFirewallToBeActive(t *testing.T, firewallID string) {
-	for i := 0; i < 10; i++ {
-		fw := getFirewall(t, firewallID)
-		if fw.Status == "ACTIVE" {
-			break
-		}
-		time.Sleep(time.Second)
+	for _, firewall := range allFirewalls {
+		PrintFirewall(t, &firewall)
 	}
 }
 
-func waitForFirewallToBeDeleted(t *testing.T, firewallID string) {
-	for i := 0; i < 10; i++ {
-		err := firewalls.Get(base.Client, firewallID).Err
-		if err != nil {
-			httpStatus := err.(*gophercloud.UnexpectedResponseCodeError)
-			if httpStatus.Actual == 404 {
-				return
-			}
-		}
-		time.Sleep(time.Second)
+func TestFirewallCRUD(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
 	}
+
+	rule, err := CreateRule(t, client)
+	if err != nil {
+		t.Fatalf("Unable to create rule: %v", err)
+	}
+	defer DeleteRule(t, client, rule.ID)
+
+	PrintRule(t, rule)
+
+	policy, err := CreatePolicy(t, client, rule.ID)
+	if err != nil {
+		t.Fatalf("Unable to create policy: %v", err)
+	}
+	defer DeletePolicy(t, client, policy.ID)
+
+	PrintPolicy(t, policy)
+
+	firewall, err := CreateFirewall(t, client, policy.ID)
+	if err != nil {
+		t.Fatalf("Unable to create firewall: %v", err)
+	}
+	defer DeleteFirewall(t, client, firewall.ID)
+
+	PrintFirewall(t, firewall)
+
+	updateOpts := firewalls.UpdateOpts{
+		PolicyID:    policy.ID,
+		Description: "Some firewall description",
+	}
+
+	_, err = firewalls.Update(client, firewall.ID, updateOpts).Extract()
+	if err != nil {
+		t.Fatalf("Unable to update firewall: %v", err)
+	}
+
+	newFirewall, err := firewalls.Get(client, firewall.ID).Extract()
+	if err != nil {
+		t.Fatalf("Unable to get firewall: %v", err)
+	}
+
+	PrintFirewall(t, newFirewall)
 }
diff --git a/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go b/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go
new file mode 100644
index 0000000..64ce402
--- /dev/null
+++ b/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go
@@ -0,0 +1,210 @@
+package fwaas
+
+import (
+	"fmt"
+	"strconv"
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/acceptance/tools"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/policies"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/rules"
+)
+
+// CreateFirewall will create a Firewaill with a random name and a specified
+// policy ID. An error will be returned if the firewall could not be created.
+func CreateFirewall(t *testing.T, client *gophercloud.ServiceClient, policyID string) (*firewalls.Firewall, error) {
+	firewallName := tools.RandomString("TESTACC-", 8)
+
+	t.Logf("Attempting to create firewall %s", firewallName)
+
+	createOpts := firewalls.CreateOpts{
+		Name:     firewallName,
+		PolicyID: policyID,
+	}
+
+	firewall, err := firewalls.Create(client, createOpts).Extract()
+	if err != nil {
+		return firewall, err
+	}
+
+	t.Logf("Waiting for firewall to become active.")
+	if err := WaitForFirewallState(client, firewall.ID, "ACTIVE", 60); err != nil {
+		return firewall, err
+	}
+
+	t.Logf("Successfully created firewall %s", firewallName)
+
+	return firewall, nil
+}
+
+// CreatePolicy will create a Firewall Policy with a random name and given
+// rule. An error will be returned if the rule could not be created.
+func CreatePolicy(t *testing.T, client *gophercloud.ServiceClient, ruleID string) (*policies.Policy, error) {
+	policyName := tools.RandomString("TESTACC-", 8)
+
+	t.Logf("Attempting to create policy %s", policyName)
+
+	createOpts := policies.CreateOpts{
+		Name: policyName,
+		Rules: []string{
+			ruleID,
+		},
+	}
+
+	policy, err := policies.Create(client, createOpts).Extract()
+	if err != nil {
+		return policy, err
+	}
+
+	t.Logf("Successfully created policy %s", policyName)
+
+	return policy, nil
+}
+
+// CreateRule will create a Firewall Rule with a random source address and
+//source port, destination address and port. An error will be returned if
+// the rule could not be created.
+func CreateRule(t *testing.T, client *gophercloud.ServiceClient) (*rules.Rule, error) {
+	ruleName := tools.RandomString("TESTACC-", 8)
+	sourceAddress := fmt.Sprintf("192.168.1.%d", tools.RandomInt(1, 100))
+	sourcePort := strconv.Itoa(tools.RandomInt(1, 100))
+	destinationAddress := fmt.Sprintf("192.168.2.%d", tools.RandomInt(1, 100))
+	destinationPort := strconv.Itoa(tools.RandomInt(1, 100))
+
+	t.Logf("Attempting to create rule %s with source %s:%s and destination %s:%s",
+		ruleName, sourceAddress, sourcePort, destinationAddress, destinationPort)
+
+	createOpts := rules.CreateOpts{
+		Name:                 ruleName,
+		Protocol:             "tcp",
+		Action:               "allow",
+		SourceIPAddress:      sourceAddress,
+		SourcePort:           sourcePort,
+		DestinationIPAddress: destinationAddress,
+		DestinationPort:      destinationPort,
+	}
+
+	rule, err := rules.Create(client, createOpts).Extract()
+	if err != nil {
+		return rule, err
+	}
+
+	t.Logf("Rule %s successfully created", ruleName)
+
+	return rule, nil
+}
+
+// DeleteFirewall will delete a firewall with a specified ID. A fatal error
+// will occur if the delete was not successful. This works best when used as
+// a deferred function.
+func DeleteFirewall(t *testing.T, client *gophercloud.ServiceClient, firewallID string) {
+	t.Logf("Attempting to delete firewall: %s", firewallID)
+
+	err := firewalls.Delete(client, firewallID).ExtractErr()
+	if err != nil {
+		t.Fatalf("Unable to delete firewall %s: %v", firewallID, err)
+	}
+
+	t.Logf("Waiting for firewall to delete.")
+	if err := WaitForFirewallState(client, firewallID, "DELETED", 60); err != nil {
+		t.Logf("Unable to delete firewall: %s", firewallID)
+	}
+
+	t.Logf("Firewall deleted: %s", firewallID)
+}
+
+// DeletePolicy will delete a policy with a specified ID. A fatal error will
+// occur if the delete was not successful. This works best when used as a
+// deferred function.
+func DeletePolicy(t *testing.T, client *gophercloud.ServiceClient, policyID string) {
+	t.Logf("Attempting to delete policy: %s", policyID)
+
+	err := policies.Delete(client, policyID).ExtractErr()
+	if err != nil {
+		t.Fatalf("Unable to delete policy %s: %v", policyID, err)
+	}
+
+	t.Logf("Deleted policy: %s", policyID)
+}
+
+// DeleteRule will delete a rule with a specified ID. A fatal error will occur
+// if the delete was not successful. This works best when used as a deferred
+// function.
+func DeleteRule(t *testing.T, client *gophercloud.ServiceClient, ruleID string) {
+	t.Logf("Attempting to delete rule: %s", ruleID)
+
+	err := rules.Delete(client, ruleID).ExtractErr()
+	if err != nil {
+		t.Fatalf("Unable to delete rule %s: %v", ruleID, err)
+	}
+
+	t.Logf("Deleted rule: %s", ruleID)
+}
+
+// PrintFirewall will print a firewall and all of its attributes.
+func PrintFirewall(t *testing.T, firewall *firewalls.Firewall) {
+	t.Logf("ID: %s", firewall.ID)
+	t.Logf("Name: %s", firewall.Name)
+	t.Logf("Description: %s", firewall.Description)
+	t.Logf("AdminStateUp: %t", firewall.AdminStateUp)
+	t.Logf("Status: %s", firewall.Status)
+	t.Logf("PolicyID: %s", firewall.PolicyID)
+	t.Logf("TenantID: %s", firewall.TenantID)
+}
+
+// PrintPolicy will print a policy and all of its attributes.
+func PrintPolicy(t *testing.T, policy *policies.Policy) {
+	t.Logf("ID: %s", policy.ID)
+	t.Logf("Name: %s", policy.Name)
+	t.Logf("Description: %s", policy.Description)
+	t.Logf("TenantID: %s", policy.TenantID)
+	t.Logf("Audited: %t", policy.Audited)
+	t.Logf("Shared: %t", policy.Shared)
+	t.Logf("Rules:")
+
+	for _, rule := range policy.Rules {
+		t.Logf("Rule ID: %s", rule)
+	}
+}
+
+// PrintRule will print a rule and all of its attributes.
+func PrintRule(t *testing.T, rule *rules.Rule) {
+	t.Logf("ID: %s", rule.ID)
+	t.Logf("Name: %s", rule.Name)
+	t.Logf("Description: %s", rule.Description)
+	t.Logf("Protocol: %s", rule.Protocol)
+	t.Logf("Action: %s", rule.Action)
+	t.Logf("IPVersion: %d", rule.IPVersion)
+	t.Logf("SourceIPAddress: %s", rule.SourceIPAddress)
+	t.Logf("DestinationIPAddress: %s", rule.DestinationIPAddress)
+	t.Logf("Shared: %t", rule.Shared)
+	t.Logf("Enabled: %t", rule.Enabled)
+	t.Logf("PolicyID: %s", rule.PolicyID)
+	t.Logf("Position: %d", rule.Position)
+	t.Logf("TenantID: %s", rule.TenantID)
+}
+
+// WaitForFirewallState will wait until a firewall reaches a given state.
+func WaitForFirewallState(client *gophercloud.ServiceClient, firewallID, status string, secs int) error {
+	return gophercloud.WaitFor(secs, func() (bool, error) {
+		current, err := firewalls.Get(client, firewallID).Extract()
+		if err != nil {
+			if httpStatus, ok := err.(gophercloud.ErrDefault404); ok {
+				if httpStatus.Actual == 404 {
+					if status == "DELETED" {
+						return true, nil
+					}
+				}
+			}
+			return false, err
+		}
+
+		if current.Status == status {
+			return true, nil
+		}
+
+		return false, nil
+	})
+}
diff --git a/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go b/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go
index 84bae52..9003e54 100644
--- a/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go
+++ b/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go
@@ -5,103 +5,66 @@
 import (
 	"testing"
 
-	base "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
+	"github.com/gophercloud/gophercloud/acceptance/clients"
 	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/policies"
-	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/rules"
-	"github.com/gophercloud/gophercloud/pagination"
-	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
-func firewallPolicySetup(t *testing.T) string {
-	base.Setup(t)
-	return createRule(t, &rules.CreateOpts{
-		Protocol: "tcp",
-		Action:   "allow",
-	})
+func TestPolicyList(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
+
+	allPages, err := policies.List(client, nil).AllPages()
+	if err != nil {
+		t.Fatalf("Unable to list policies: %v", err)
+	}
+
+	allPolicies, err := policies.ExtractPolicies(allPages)
+	if err != nil {
+		t.Fatalf("Unable to extract policies: %v", err)
+	}
+
+	for _, policy := range allPolicies {
+		PrintPolicy(t, &policy)
+	}
 }
 
-func firewallPolicyTeardown(t *testing.T, ruleID string) {
-	defer base.Teardown()
-	deleteRule(t, ruleID)
-}
+func TestPolicyCRUD(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
 
-func TestFirewallPolicy(t *testing.T) {
-	ruleID := firewallPolicySetup(t)
-	defer firewallPolicyTeardown(t, ruleID)
+	rule, err := CreateRule(t, client)
+	if err != nil {
+		t.Fatalf("Unable to create rule: %v", err)
+	}
+	defer DeleteRule(t, client, rule.ID)
 
-	policyID := createPolicy(t, &policies.CreateOpts{
-		Name:        "gophercloud test",
-		Description: "acceptance test",
-		Rules: []string{
-			ruleID,
-		},
-	})
+	PrintRule(t, rule)
 
-	listPolicies(t)
+	policy, err := CreatePolicy(t, client, rule.ID)
+	if err != nil {
+		t.Fatalf("Unable to create policy: %v", err)
+	}
+	defer DeletePolicy(t, client, policy.ID)
 
-	updatePolicy(t, policyID, &policies.UpdateOpts{
-		Description: "acceptance test updated",
-	})
+	PrintPolicy(t, policy)
 
-	getPolicy(t, policyID)
+	updateOpts := policies.UpdateOpts{
+		Description: "Some policy description",
+	}
 
-	removeRuleFromPolicy(t, policyID, ruleID)
+	_, err = policies.Update(client, policy.ID, updateOpts).Extract()
+	if err != nil {
+		t.Fatalf("Unable to update policy: %v", err)
+	}
 
-	addRuleToPolicy(t, policyID, ruleID)
+	newPolicy, err := policies.Get(client, policy.ID).Extract()
+	if err != nil {
+		t.Fatalf("Unable to get policy: %v", err)
+	}
 
-	deletePolicy(t, policyID)
-}
-
-func createPolicy(t *testing.T, opts *policies.CreateOpts) string {
-	p, err := policies.Create(base.Client, *opts).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Created policy: %#v", opts)
-	return p.ID
-}
-
-func listPolicies(t *testing.T) {
-	err := policies.List(base.Client, policies.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		policyList, err := policies.ExtractPolicies(page)
-		if err != nil {
-			t.Errorf("Failed to extract policies: %v", err)
-			return false, err
-		}
-
-		for _, p := range policyList {
-			t.Logf("Listing policies: ID [%s]", p.ID)
-		}
-
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-}
-
-func updatePolicy(t *testing.T, policyID string, opts *policies.UpdateOpts) {
-	p, err := policies.Update(base.Client, policyID, *opts).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Updated policy ID [%s]", p.ID)
-}
-
-func removeRuleFromPolicy(t *testing.T, policyID string, ruleID string) {
-	err := policies.RemoveRule(base.Client, policyID, ruleID)
-	th.AssertNoErr(t, err)
-	t.Logf("Removed rule [%s] from policy ID [%s]", ruleID, policyID)
-}
-
-func addRuleToPolicy(t *testing.T, policyID string, ruleID string) {
-	err := policies.InsertRule(base.Client, policyID, ruleID, "", "")
-	th.AssertNoErr(t, err)
-	t.Logf("Inserted rule [%s] into policy ID [%s]", ruleID, policyID)
-}
-
-func getPolicy(t *testing.T, policyID string) {
-	p, err := policies.Get(base.Client, policyID).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Getting policy ID [%s]", p.ID)
-}
-
-func deletePolicy(t *testing.T, policyID string) {
-	res := policies.Delete(base.Client, policyID)
-	th.AssertNoErr(t, res.Err)
-	t.Logf("Deleted policy %s", policyID)
+	PrintPolicy(t, newPolicy)
 }
diff --git a/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go b/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go
index aa11ec6..c8e67dc 100644
--- a/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go
+++ b/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go
@@ -5,80 +5,59 @@
 import (
 	"testing"
 
-	base "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
+	"github.com/gophercloud/gophercloud/acceptance/clients"
 	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/rules"
-	"github.com/gophercloud/gophercloud/pagination"
-	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
-func TestFirewallRules(t *testing.T) {
-	base.Setup(t)
-	defer base.Teardown()
+func TestRuleList(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
 
-	ruleID := createRule(t, &rules.CreateOpts{
-		Name:                 "gophercloud_test",
-		Description:          "acceptance test",
-		Protocol:             "tcp",
-		Action:               "allow",
-		DestinationIPAddress: "192.168.0.0/24",
-		DestinationPort:      "22",
-	})
+	allPages, err := rules.List(client, nil).AllPages()
+	if err != nil {
+		t.Fatalf("Unable to list rules: %v", err)
+	}
 
-	listRules(t)
+	allRules, err := rules.ExtractRules(allPages)
+	if err != nil {
+		t.Fatalf("Unable to extract rules: %v", err)
+	}
 
-	destinationIPAddress := "192.168.1.0/24"
-	destinationPort := ""
-	sourcePort := "1234"
-
-	updateRule(t, ruleID, &rules.UpdateOpts{
-		DestinationIPAddress: &destinationIPAddress,
-		DestinationPort:      &destinationPort,
-		SourcePort:           &sourcePort,
-	})
-
-	getRule(t, ruleID)
-
-	deleteRule(t, ruleID)
+	for _, rule := range allRules {
+		PrintRule(t, &rule)
+	}
 }
 
-func createRule(t *testing.T, opts *rules.CreateOpts) string {
-	r, err := rules.Create(base.Client, *opts).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Created rule: %#v", opts)
-	return r.ID
-}
+func TestRuleCRUD(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
 
-func listRules(t *testing.T) {
-	err := rules.List(base.Client, rules.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		ruleList, err := rules.ExtractRules(page)
-		if err != nil {
-			t.Errorf("Failed to extract rules: %v", err)
-			return false, err
-		}
+	rule, err := CreateRule(t, client)
+	if err != nil {
+		t.Fatalf("Unable to create rule: %v", err)
+	}
+	defer DeleteRule(t, client, rule.ID)
 
-		for _, r := range ruleList {
-			t.Logf("Listing rules: ID [%s]", r.ID)
-		}
+	PrintRule(t, rule)
 
-		return true, nil
-	})
-	th.AssertNoErr(t, err)
-}
+	ruleDescription := "Some rule description"
+	updateOpts := rules.UpdateOpts{
+		Description: &ruleDescription,
+	}
 
-func updateRule(t *testing.T, ruleID string, opts *rules.UpdateOpts) {
-	r, err := rules.Update(base.Client, ruleID, *opts).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Updated rule ID [%s]", r.ID)
-}
+	_, err = rules.Update(client, rule.ID, updateOpts).Extract()
+	if err != nil {
+		t.Fatalf("Unable to update rule: %v", err)
+	}
 
-func getRule(t *testing.T, ruleID string) {
-	r, err := rules.Get(base.Client, ruleID).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Getting rule ID [%s]", r.ID)
-}
+	newRule, err := rules.Get(client, rule.ID).Extract()
+	if err != nil {
+		t.Fatalf("Unable to get rule: %v", err)
+	}
 
-func deleteRule(t *testing.T, ruleID string) {
-	res := rules.Delete(base.Client, ruleID)
-	th.AssertNoErr(t, res.Err)
-	t.Logf("Deleted rule %s", ruleID)
+	PrintRule(t, newRule)
 }
diff --git a/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go b/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go
new file mode 100644
index 0000000..cac8983
--- /dev/null
+++ b/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go
@@ -0,0 +1,99 @@
+// +build acceptance networking layer3 floatingips
+
+package layer3
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/acceptance/clients"
+	networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers"
+)
+
+func TestLayer3FloatingIPsList(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a compute client: %v", err)
+	}
+
+	listOpts := floatingips.ListOpts{}
+	allPages, err := floatingips.List(client, listOpts).AllPages()
+	if err != nil {
+		t.Fatalf("Unable to list floating IPs: %v", err)
+	}
+
+	allFIPs, err := floatingips.ExtractFloatingIPs(allPages)
+	if err != nil {
+		t.Fatalf("Unable to extract floating IPs: %v", err)
+	}
+
+	for _, fip := range allFIPs {
+		PrintFloatingIP(t, &fip)
+	}
+}
+
+func TestLayer3FloatingIPsCreateDelete(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a compute client: %v", err)
+	}
+
+	choices, err := clients.AcceptanceTestChoicesFromEnv()
+	if err != nil {
+		t.Fatalf("Unable to get choices: %v", err)
+	}
+
+	subnet, err := networking.CreateSubnet(t, client, choices.ExternalNetworkID)
+	if err != nil {
+		t.Fatalf("Unable to create subnet: %v", err)
+	}
+	defer networking.DeleteSubnet(t, client, subnet.ID)
+
+	router, err := CreateExternalRouter(t, client)
+	if err != nil {
+		t.Fatalf("Unable to create router: %v", err)
+	}
+	defer DeleteRouter(t, client, router.ID)
+
+	aiOpts := routers.AddInterfaceOpts{
+		SubnetID: subnet.ID,
+	}
+
+	iface, err := routers.AddInterface(client, router.ID, aiOpts).Extract()
+	if err != nil {
+		t.Fatalf("Unable to add interface to router: %v", err)
+	}
+
+	PrintRouter(t, router)
+	PrintRouterInterface(t, iface)
+
+	port, err := networking.CreatePort(t, client, choices.ExternalNetworkID, subnet.ID)
+	if err != nil {
+		t.Fatalf("Unable to create port: %v", err)
+	}
+	defer networking.DeletePort(t, client, port.ID)
+
+	fip, err := CreateFloatingIP(t, client, choices.ExternalNetworkID, port.ID)
+	if err != nil {
+		t.Fatalf("Unable to create floating IP: %v", err)
+	}
+
+	newFip, err := floatingips.Get(client, fip.ID).Extract()
+	if err != nil {
+		t.Fatalf("Unable to get floating ip: %v", err)
+	}
+
+	PrintFloatingIP(t, newFip)
+
+	DeleteFloatingIP(t, client, fip.ID)
+
+	riOpts := routers.RemoveInterfaceOpts{
+		SubnetID: subnet.ID,
+	}
+
+	_, err = routers.RemoveInterface(client, router.ID, riOpts).Extract()
+	if err != nil {
+		t.Fatalf("Failed to remove interface from router: %v", err)
+	}
+}
diff --git a/acceptance/openstack/networking/v2/extensions/layer3/layer3.go b/acceptance/openstack/networking/v2/extensions/layer3/layer3.go
new file mode 100644
index 0000000..5c6031e
--- /dev/null
+++ b/acceptance/openstack/networking/v2/extensions/layer3/layer3.go
@@ -0,0 +1,157 @@
+package layer3
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/acceptance/clients"
+	"github.com/gophercloud/gophercloud/acceptance/tools"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers"
+)
+
+// CreateFloatingIP creates a floating IP on a given network and port. An error
+// will be returned if the creation failed.
+func CreateFloatingIP(t *testing.T, client *gophercloud.ServiceClient, networkID, portID string) (*floatingips.FloatingIP, error) {
+	t.Logf("Attempting to create floating IP on port: %s", portID)
+
+	createOpts := &floatingips.CreateOpts{
+		FloatingNetworkID: networkID,
+		PortID:            portID,
+	}
+
+	floatingIP, err := floatingips.Create(client, createOpts).Extract()
+	if err != nil {
+		return floatingIP, err
+	}
+
+	t.Logf("Created floating IP.")
+
+	return floatingIP, err
+}
+
+// CreateExternalRouter creates a router on the external network. This requires
+// the OS_EXTGW_ID environment variable to be set. An error is returned if the
+// creation failed.
+func CreateExternalRouter(t *testing.T, client *gophercloud.ServiceClient) (*routers.Router, error) {
+	var router *routers.Router
+	choices, err := clients.AcceptanceTestChoicesFromEnv()
+	if err != nil {
+		return router, err
+	}
+
+	routerName := tools.RandomString("TESTACC-", 8)
+
+	t.Logf("Attempting to create router: %s", routerName)
+
+	adminStateUp := true
+	gatewayInfo := routers.GatewayInfo{
+		NetworkID: choices.ExternalNetworkID,
+	}
+
+	createOpts := routers.CreateOpts{
+		Name:         routerName,
+		AdminStateUp: &adminStateUp,
+		GatewayInfo:  &gatewayInfo,
+	}
+
+	router, err = routers.Create(client, createOpts).Extract()
+	if err != nil {
+		return router, err
+	}
+
+	t.Logf("Created router: %s", routerName)
+
+	return router, nil
+}
+
+// CreateRouter creates a router on a specified Network ID. An error will be
+// returned if the creation failed.
+func CreateRouter(t *testing.T, client *gophercloud.ServiceClient, networkID string) (*routers.Router, error) {
+	routerName := tools.RandomString("TESTACC-", 8)
+
+	t.Logf("Attempting to create router: %s", routerName)
+
+	adminStateUp := true
+	gatewayInfo := routers.GatewayInfo{
+		NetworkID: networkID,
+	}
+
+	createOpts := routers.CreateOpts{
+		Name:         routerName,
+		AdminStateUp: &adminStateUp,
+		GatewayInfo:  &gatewayInfo,
+	}
+
+	router, err := routers.Create(client, createOpts).Extract()
+	if err != nil {
+		return router, err
+	}
+
+	t.Logf("Created router: %s", routerName)
+
+	return router, nil
+}
+
+// DeleteRouter deletes a router of a specified ID. A fatal error will occur
+// if the deletion failed. This works best when used as a deferred function.
+func DeleteRouter(t *testing.T, client *gophercloud.ServiceClient, routerID string) {
+	t.Logf("Attempting to delete router: %s", routerID)
+
+	err := routers.Delete(client, routerID).ExtractErr()
+	if err != nil {
+		t.Fatalf("Error deleting router: %v", err)
+	}
+
+	t.Logf("Deleted router: %s", routerID)
+}
+
+// DeleteFloatingIP deletes a floatingIP of a specified ID. A fatal error will
+// occur if the deletion failed. This works best when used as a deferred
+// function.
+func DeleteFloatingIP(t *testing.T, client *gophercloud.ServiceClient, floatingIPID string) {
+	t.Logf("Attempting to delete floating IP: %s", floatingIPID)
+
+	err := floatingips.Delete(client, floatingIPID).ExtractErr()
+	if err != nil {
+		t.Fatalf("Failed to delete floating IP: %v", err)
+	}
+
+	t.Logf("Deleted floating IP: %s", floatingIPID)
+}
+
+// PrintFloatingIP prints a floating IP and all of its attributes.
+func PrintFloatingIP(t *testing.T, fip *floatingips.FloatingIP) {
+	t.Logf("ID: %s", fip.ID)
+	t.Logf("FloatingNetworkID: %s", fip.FloatingNetworkID)
+	t.Logf("FloatingIP: %s", fip.FloatingIP)
+	t.Logf("PortID: %s", fip.PortID)
+	t.Logf("FixedIP: %s", fip.FixedIP)
+	t.Logf("TenantID: %s", fip.TenantID)
+	t.Logf("Status: %s", fip.Status)
+}
+
+// PrintRouterInterface prints a router interface and all of its attributes.
+func PrintRouterInterface(t *testing.T, routerInterface *routers.InterfaceInfo) {
+	t.Logf("ID: %s", routerInterface.ID)
+	t.Logf("SubnetID: %s", routerInterface.SubnetID)
+	t.Logf("PortID: %s", routerInterface.PortID)
+	t.Logf("TenantID: %s", routerInterface.TenantID)
+}
+
+// PrintRouter prints a router and all of its attributes.
+func PrintRouter(t *testing.T, router *routers.Router) {
+	t.Logf("ID: %s", router.ID)
+	t.Logf("Status: %s", router.Status)
+	t.Logf("GatewayInfo: %s", router.GatewayInfo)
+	t.Logf("AdminStateUp: %t", router.AdminStateUp)
+	t.Logf("Distributed: %t", router.Distributed)
+	t.Logf("Name: %s", router.Name)
+	t.Logf("TenantID: %s", router.TenantID)
+	t.Logf("Routes:")
+
+	for _, route := range router.Routes {
+		t.Logf("\tNextHop: %s", route.NextHop)
+		t.Logf("\tDestinationCIDR: %s", route.DestinationCIDR)
+	}
+}
diff --git a/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go b/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go
new file mode 100644
index 0000000..67688ec
--- /dev/null
+++ b/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go
@@ -0,0 +1,113 @@
+// +build acceptance networking layer3 router
+
+package layer3
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/acceptance/clients"
+	networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
+	"github.com/gophercloud/gophercloud/acceptance/tools"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers"
+)
+
+func TestLayer3RouterList(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
+
+	listOpts := routers.ListOpts{}
+	allPages, err := routers.List(client, listOpts).AllPages()
+	if err != nil {
+		t.Fatalf("Unable to list routers: %v", err)
+	}
+
+	allRouters, err := routers.ExtractRouters(allPages)
+	if err != nil {
+		t.Fatalf("Unable to extract routers: %v", err)
+	}
+
+	for _, router := range allRouters {
+		PrintRouter(t, &router)
+	}
+}
+
+func TestLayer3RouterCreateDelete(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
+
+	router, err := CreateExternalRouter(t, client)
+	if err != nil {
+		t.Fatalf("Unable to create router: %v", err)
+	}
+	defer DeleteRouter(t, client, router.ID)
+
+	PrintRouter(t, router)
+
+	newName := tools.RandomString("TESTACC-", 8)
+	updateOpts := routers.UpdateOpts{
+		Name: newName,
+	}
+
+	_, err = routers.Update(client, router.ID, updateOpts).Extract()
+	if err != nil {
+		t.Fatalf("Unable to update router: %v", err)
+	}
+
+	newRouter, err := routers.Get(client, router.ID).Extract()
+	if err != nil {
+		t.Fatalf("Unable to get router: %v", err)
+	}
+
+	PrintRouter(t, newRouter)
+}
+
+func TestLayer3RouterInterface(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a compute client: %v", err)
+	}
+
+	choices, err := clients.AcceptanceTestChoicesFromEnv()
+	if err != nil {
+		t.Fatalf("Unable to get choices: %v", err)
+	}
+
+	subnet, err := networking.CreateSubnet(t, client, choices.ExternalNetworkID)
+	if err != nil {
+		t.Fatalf("Unable to create subnet: %v", err)
+	}
+	defer networking.DeleteSubnet(t, client, subnet.ID)
+
+	networking.PrintSubnet(t, subnet)
+
+	router, err := CreateExternalRouter(t, client)
+	if err != nil {
+		t.Fatalf("Unable to create router: %v", err)
+	}
+	defer DeleteRouter(t, client, router.ID)
+
+	aiOpts := routers.AddInterfaceOpts{
+		SubnetID: subnet.ID,
+	}
+
+	iface, err := routers.AddInterface(client, router.ID, aiOpts).Extract()
+	if err != nil {
+		t.Fatalf("Failed to add interface to router: %v", err)
+	}
+
+	PrintRouter(t, router)
+	PrintRouterInterface(t, iface)
+
+	riOpts := routers.RemoveInterfaceOpts{
+		SubnetID: subnet.ID,
+	}
+
+	_, err = routers.RemoveInterface(client, router.ID, riOpts).Extract()
+	if err != nil {
+		t.Fatalf("Failed to remove interface from router: %v", err)
+	}
+}
diff --git a/acceptance/openstack/networking/v2/extensions/layer3_test.go b/acceptance/openstack/networking/v2/extensions/layer3_test.go
deleted file mode 100644
index 7d9dba3..0000000
--- a/acceptance/openstack/networking/v2/extensions/layer3_test.go
+++ /dev/null
@@ -1,300 +0,0 @@
-// +build acceptance networking layer3ext
-
-package extensions
-
-import (
-	"testing"
-
-	base "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
-	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/external"
-	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips"
-	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers"
-	"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
-	"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
-	"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
-	"github.com/gophercloud/gophercloud/pagination"
-	th "github.com/gophercloud/gophercloud/testhelper"
-)
-
-const (
-	cidr1 = "10.0.0.1/24"
-	cidr2 = "20.0.0.1/24"
-)
-
-func TestAll(t *testing.T) {
-	base.Setup(t)
-	defer base.Teardown()
-
-	testRouter(t)
-	testFloatingIP(t)
-}
-
-func testRouter(t *testing.T) {
-	// Setup: Create network
-	networkID := createNetwork(t)
-
-	// Create router
-	routerID := createRouter(t, networkID)
-
-	// Lists routers
-	listRouters(t)
-
-	// Update router
-	updateRouter(t, routerID)
-
-	// Get router
-	getRouter(t, routerID)
-
-	// Create new subnet. Note: this subnet will be deleted when networkID is deleted
-	subnetID := createSubnet(t, networkID, cidr2)
-
-	// Add interface
-	addInterface(t, routerID, subnetID)
-
-	// Remove interface
-	removeInterface(t, routerID, subnetID)
-
-	// Delete router
-	deleteRouter(t, routerID)
-
-	// Cleanup
-	deleteNetwork(t, networkID)
-}
-
-func testFloatingIP(t *testing.T) {
-	// Setup external network
-	extNetworkID := createNetwork(t)
-
-	// Setup internal network, subnet and port
-	intNetworkID, subnetID, portID := createInternalTopology(t)
-
-	// Now the important part: we need to allow the external network to talk to
-	// the internal subnet. For this we need a router that has an interface to
-	// the internal subnet.
-	routerID := bridgeIntSubnetWithExtNetwork(t, extNetworkID, subnetID)
-
-	// Create floating IP
-	ipID := createFloatingIP(t, extNetworkID, portID)
-
-	// Get floating IP
-	getFloatingIP(t, ipID)
-
-	// Update floating IP
-	updateFloatingIP(t, ipID, portID)
-
-	// Delete floating IP
-	deleteFloatingIP(t, ipID)
-
-	// Remove the internal subnet interface
-	removeInterface(t, routerID, subnetID)
-
-	// Delete router and external network
-	deleteRouter(t, routerID)
-	deleteNetwork(t, extNetworkID)
-
-	// Delete internal port and network
-	deletePort(t, portID)
-	deleteNetwork(t, intNetworkID)
-}
-
-func createNetwork(t *testing.T) string {
-	t.Logf("Creating a network")
-
-	asu := true
-	opts := external.CreateOpts{
-		Parent:   networks.CreateOpts{Name: "sample_network", AdminStateUp: &asu},
-		External: true,
-	}
-	n, err := networks.Create(base.Client, opts).Extract()
-
-	th.AssertNoErr(t, err)
-
-	if n.ID == "" {
-		t.Fatalf("No ID returned when creating a network")
-	}
-
-	createSubnet(t, n.ID, cidr1)
-
-	t.Logf("Network created: ID [%s]", n.ID)
-
-	return n.ID
-}
-
-func deleteNetwork(t *testing.T, networkID string) {
-	t.Logf("Deleting network %s", networkID)
-	networks.Delete(base.Client, networkID)
-}
-
-func deletePort(t *testing.T, portID string) {
-	t.Logf("Deleting port %s", portID)
-	ports.Delete(base.Client, portID)
-}
-
-func createInternalTopology(t *testing.T) (string, string, string) {
-	t.Logf("Creating an internal network (for port)")
-	opts := networks.CreateOpts{Name: "internal_network"}
-	n, err := networks.Create(base.Client, opts).Extract()
-	th.AssertNoErr(t, err)
-
-	// A subnet is also needed
-	subnetID := createSubnet(t, n.ID, cidr2)
-
-	t.Logf("Creating an internal port on network %s", n.ID)
-	p, err := ports.Create(base.Client, ports.CreateOpts{
-		NetworkID: n.ID,
-		Name:      "fixed_internal_port",
-	}).Extract()
-	th.AssertNoErr(t, err)
-
-	return n.ID, subnetID, p.ID
-}
-
-func bridgeIntSubnetWithExtNetwork(t *testing.T, networkID, subnetID string) string {
-	// Create router with external gateway info
-	routerID := createRouter(t, networkID)
-
-	// Add interface for internal subnet
-	addInterface(t, routerID, subnetID)
-
-	return routerID
-}
-
-func createSubnet(t *testing.T, networkID, cidr string) string {
-	t.Logf("Creating a subnet for network %s", networkID)
-
-	iFalse := false
-	s, err := subnets.Create(base.Client, subnets.CreateOpts{
-		NetworkID:  networkID,
-		CIDR:       cidr,
-		IPVersion:  subnets.IPv4,
-		Name:       "my_subnet",
-		EnableDHCP: &iFalse,
-	}).Extract()
-
-	th.AssertNoErr(t, err)
-
-	t.Logf("Subnet created: ID [%s]", s.ID)
-
-	return s.ID
-}
-
-func createRouter(t *testing.T, networkID string) string {
-	t.Logf("Creating a router for network %s", networkID)
-
-	asu := false
-	gwi := routers.GatewayInfo{NetworkID: networkID}
-	r, err := routers.Create(base.Client, routers.CreateOpts{
-		Name:         "foo_router",
-		AdminStateUp: &asu,
-		GatewayInfo:  &gwi,
-	}).Extract()
-
-	th.AssertNoErr(t, err)
-
-	if r.ID == "" {
-		t.Fatalf("No ID returned when creating a router")
-	}
-
-	t.Logf("Router created: ID [%s]", r.ID)
-
-	return r.ID
-}
-
-func listRouters(t *testing.T) {
-	pager := routers.List(base.Client, routers.ListOpts{})
-
-	err := pager.EachPage(func(page pagination.Page) (bool, error) {
-		routerList, err := routers.ExtractRouters(page)
-		th.AssertNoErr(t, err)
-
-		for _, r := range routerList {
-			t.Logf("Listing router: ID [%s] Name [%s] Status [%s] GatewayInfo [%#v]",
-				r.ID, r.Name, r.Status, r.GatewayInfo)
-		}
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-}
-
-func updateRouter(t *testing.T, routerID string) {
-	_, err := routers.Update(base.Client, routerID, routers.UpdateOpts{
-		Name: "another_name",
-	}).Extract()
-
-	th.AssertNoErr(t, err)
-}
-
-func getRouter(t *testing.T, routerID string) {
-	r, err := routers.Get(base.Client, routerID).Extract()
-
-	th.AssertNoErr(t, err)
-
-	t.Logf("Getting router: ID [%s] Name [%s] Status [%s]", r.ID, r.Name, r.Status)
-}
-
-func addInterface(t *testing.T, routerID, subnetID string) {
-	ir, err := routers.AddInterface(base.Client, routerID, routers.InterfaceOpts{SubnetID: subnetID}).Extract()
-
-	th.AssertNoErr(t, err)
-
-	t.Logf("Interface added to router %s: SubnetID [%s] PortID [%s]", routerID, ir.SubnetID, ir.PortID)
-}
-
-func removeInterface(t *testing.T, routerID, subnetID string) {
-	ir, err := routers.RemoveInterface(base.Client, routerID, routers.InterfaceOpts{SubnetID: subnetID}).Extract()
-
-	th.AssertNoErr(t, err)
-
-	t.Logf("Interface %s removed from %s", ir.ID, routerID)
-}
-
-func deleteRouter(t *testing.T, routerID string) {
-	t.Logf("Deleting router %s", routerID)
-
-	res := routers.Delete(base.Client, routerID)
-
-	th.AssertNoErr(t, res.Err)
-}
-
-func createFloatingIP(t *testing.T, networkID, portID string) string {
-	t.Logf("Creating floating IP on network [%s] with port [%s]", networkID, portID)
-
-	opts := floatingips.CreateOpts{
-		FloatingNetworkID: networkID,
-		PortID:            portID,
-	}
-
-	ip, err := floatingips.Create(base.Client, opts).Extract()
-
-	th.AssertNoErr(t, err)
-
-	t.Logf("Floating IP created: ID [%s] Status [%s] Fixed (internal) IP: [%s] Floating (external) IP: [%s]",
-		ip.ID, ip.Status, ip.FixedIP, ip.FloatingIP)
-
-	return ip.ID
-}
-
-func getFloatingIP(t *testing.T, ipID string) {
-	ip, err := floatingips.Get(base.Client, ipID).Extract()
-	th.AssertNoErr(t, err)
-
-	t.Logf("Getting floating IP: ID [%s] Status [%s]", ip.ID, ip.Status)
-}
-
-func updateFloatingIP(t *testing.T, ipID, portID string) {
-	t.Logf("Disassociate all ports from IP %s", ipID)
-	_, err := floatingips.Update(base.Client, ipID, floatingips.UpdateOpts{PortID: ""}).Extract()
-	th.AssertNoErr(t, err)
-
-	t.Logf("Re-associate the port %s", portID)
-	_, err = floatingips.Update(base.Client, ipID, floatingips.UpdateOpts{PortID: portID}).Extract()
-	th.AssertNoErr(t, err)
-}
-
-func deleteFloatingIP(t *testing.T, ipID string) {
-	t.Logf("Deleting IP %s", ipID)
-	res := floatingips.Delete(base.Client, ipID)
-	th.AssertNoErr(t, res.Err)
-}
diff --git a/acceptance/openstack/networking/v2/extensions/lbaas/common.go b/acceptance/openstack/networking/v2/extensions/lbaas/common.go
deleted file mode 100644
index 760ee5b..0000000
--- a/acceptance/openstack/networking/v2/extensions/lbaas/common.go
+++ /dev/null
@@ -1,80 +0,0 @@
-package lbaas
-
-import (
-	"testing"
-
-	"github.com/gophercloud/gophercloud"
-	base "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
-	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/monitors"
-	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/pools"
-	"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
-	"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
-	th "github.com/gophercloud/gophercloud/testhelper"
-)
-
-func SetupTopology(t *testing.T) (string, string) {
-	// create network
-	n, err := networks.Create(base.Client, networks.CreateOpts{Name: "tmp_network"}).Extract()
-	th.AssertNoErr(t, err)
-
-	t.Logf("Created network %s", n.ID)
-
-	// create subnet
-	s, err := subnets.Create(base.Client, subnets.CreateOpts{
-		NetworkID: n.ID,
-		CIDR:      "192.168.199.0/24",
-		IPVersion: gophercloud.IPv4,
-		Name:      "tmp_subnet",
-	}).Extract()
-	th.AssertNoErr(t, err)
-
-	t.Logf("Created subnet %s", s.ID)
-
-	return n.ID, s.ID
-}
-
-func DeleteTopology(t *testing.T, networkID string) {
-	res := networks.Delete(base.Client, networkID)
-	th.AssertNoErr(t, res.Err)
-	t.Logf("Deleted network %s", networkID)
-}
-
-func CreatePool(t *testing.T, subnetID string) string {
-	p, err := pools.Create(base.Client, pools.CreateOpts{
-		LBMethod: pools.LBMethodRoundRobin,
-		Protocol: "HTTP",
-		Name:     "tmp_pool",
-		SubnetID: subnetID,
-		Provider: "haproxy",
-	}).Extract()
-
-	th.AssertNoErr(t, err)
-
-	t.Logf("Created pool %s", p.ID)
-
-	return p.ID
-}
-
-func DeletePool(t *testing.T, poolID string) {
-	res := pools.Delete(base.Client, poolID)
-	th.AssertNoErr(t, res.Err)
-	t.Logf("Deleted pool %s", poolID)
-}
-
-func CreateMonitor(t *testing.T) string {
-	m, err := monitors.Create(base.Client, monitors.CreateOpts{
-		Delay:         10,
-		Timeout:       10,
-		MaxRetries:    3,
-		Type:          monitors.TypeHTTP,
-		ExpectedCodes: "200",
-		URLPath:       "/login",
-		HTTPMethod:    "GET",
-	}).Extract()
-
-	th.AssertNoErr(t, err)
-
-	t.Logf("Created monitor ID [%s]", m.ID)
-
-	return m.ID
-}
diff --git a/acceptance/openstack/networking/v2/extensions/lbaas/lbaas.go b/acceptance/openstack/networking/v2/extensions/lbaas/lbaas.go
new file mode 100644
index 0000000..32bfcd4
--- /dev/null
+++ b/acceptance/openstack/networking/v2/extensions/lbaas/lbaas.go
@@ -0,0 +1,220 @@
+package lbaas
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/acceptance/tools"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/members"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/monitors"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/pools"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/vips"
+)
+
+// CreateMember will create a load balancer member in a specified pool on a
+// random port. An error will be returned if the member could not be created.
+func CreateMember(t *testing.T, client *gophercloud.ServiceClient, poolID string) (*members.Member, error) {
+	protocolPort := tools.RandomInt(100, 1000)
+	address := tools.RandomInt(2, 200)
+	t.Logf("Attempting to create member in port %d", protocolPort)
+
+	createOpts := members.CreateOpts{
+		PoolID:       poolID,
+		ProtocolPort: protocolPort,
+		Address:      fmt.Sprintf("192.168.1.%d", address),
+	}
+
+	member, err := members.Create(client, createOpts).Extract()
+	if err != nil {
+		return member, err
+	}
+
+	t.Logf("Successfully created member %s")
+
+	return member, nil
+}
+
+// CreateMonitor will create a monitor with a random name for a specific pool.
+// An error will be returned if the monitor could not be created.
+func CreateMonitor(t *testing.T, client *gophercloud.ServiceClient) (*monitors.Monitor, error) {
+	t.Logf("Attempting to create monitor.")
+
+	createOpts := monitors.CreateOpts{
+		Type:         monitors.TypePING,
+		Delay:        90,
+		Timeout:      60,
+		MaxRetries:   10,
+		AdminStateUp: gophercloud.Enabled,
+	}
+
+	monitor, err := monitors.Create(client, createOpts).Extract()
+	if err != nil {
+		return monitor, err
+	}
+
+	t.Logf("Successfully created monitor")
+
+	return monitor, nil
+}
+
+// CreatePool will create a pool with a random name. An error will be returned
+// if the pool could not be deleted.
+func CreatePool(t *testing.T, client *gophercloud.ServiceClient, subnetID string) (*pools.Pool, error) {
+	poolName := tools.RandomString("TESTACCT-", 8)
+
+	t.Logf("Attempting to create pool %s", poolName)
+
+	createOpts := pools.CreateOpts{
+		Name:     poolName,
+		SubnetID: subnetID,
+		Protocol: pools.ProtocolTCP,
+		LBMethod: pools.LBMethodRoundRobin,
+	}
+
+	pool, err := pools.Create(client, createOpts).Extract()
+	if err != nil {
+		return pool, err
+	}
+
+	t.Logf("Successfully created pool %s", poolName)
+
+	return pool, nil
+}
+
+// CreateVIP will create a vip with a random name and a random port in a
+// specified subnet and pool. An error will be returned if the vip could
+// not be created.
+func CreateVIP(t *testing.T, client *gophercloud.ServiceClient, subnetID, poolID string) (*vips.VirtualIP, error) {
+	vipName := tools.RandomString("TESTACCT-", 8)
+	vipPort := tools.RandomInt(100, 10000)
+
+	t.Logf("Attempting to create VIP %s", vipName)
+
+	createOpts := vips.CreateOpts{
+		Name:         vipName,
+		SubnetID:     subnetID,
+		PoolID:       poolID,
+		Protocol:     "TCP",
+		ProtocolPort: vipPort,
+	}
+
+	vip, err := vips.Create(client, createOpts).Extract()
+	if err != nil {
+		return vip, err
+	}
+
+	t.Logf("Successfully created vip %s", vipName)
+
+	return vip, nil
+}
+
+// DeleteMember will delete a specified member. A fatal error will occur if
+// the member could not be deleted. This works best when used as a deferred
+// function.
+func DeleteMember(t *testing.T, client *gophercloud.ServiceClient, memberID string) {
+	t.Logf("Attempting to delete member %s", memberID)
+
+	if err := members.Delete(client, memberID).ExtractErr(); err != nil {
+		t.Fatalf("Unable to delete member: %v", err)
+	}
+
+	t.Logf("Successfully deleted member %s", memberID)
+}
+
+// DeleteMonitor will delete a specified monitor. A fatal error will occur if
+// the monitor could not be deleted. This works best when used as a deferred
+// function.
+func DeleteMonitor(t *testing.T, client *gophercloud.ServiceClient, monitorID string) {
+	t.Logf("Attempting to delete monitor %s", monitorID)
+
+	if err := monitors.Delete(client, monitorID).ExtractErr(); err != nil {
+		t.Fatalf("Unable to delete monitor: %v", err)
+	}
+
+	t.Logf("Successfully deleted monitor %s", monitorID)
+}
+
+// DeletePool will delete a specified pool. A fatal error will occur if the
+// pool could not be deleted. This works best when used as a deferred function.
+func DeletePool(t *testing.T, client *gophercloud.ServiceClient, poolID string) {
+	t.Logf("Attempting to delete pool %s", poolID)
+
+	if err := pools.Delete(client, poolID).ExtractErr(); err != nil {
+		t.Fatalf("Unable to delete pool: %v", err)
+	}
+
+	t.Logf("Successfully deleted pool %s", poolID)
+}
+
+// DeleteVIP will delete a specified vip. A fatal error will occur if the vip
+// could not be deleted. This works best when used as a deferred function.
+func DeleteVIP(t *testing.T, client *gophercloud.ServiceClient, vipID string) {
+	t.Logf("Attempting to delete vip %s", vipID)
+
+	if err := vips.Delete(client, vipID).ExtractErr(); err != nil {
+		t.Fatalf("Unable to delete vip: %v", err)
+	}
+
+	t.Logf("Successfully deleted vip %s", vipID)
+}
+
+// PrintMember will print a member and all of its attributes.
+func PrintMember(t *testing.T, member *members.Member) {
+	t.Logf("ID: %s", member.ID)
+	t.Logf("TenantID: %s", member.TenantID)
+	t.Logf("Status: %s", member.Status)
+	t.Logf("Weight: %d", member.Weight)
+	t.Logf("AdminStateUp: %t", member.AdminStateUp)
+	t.Logf("PoolID: %s", member.PoolID)
+	t.Logf("Address: %s", member.Address)
+	t.Logf("ProtocolPort: %s", member.ProtocolPort)
+}
+
+// PrintMonitor will print a monitor and all of its attributes.
+func PrintMonitor(t *testing.T, monitor *monitors.Monitor) {
+	t.Logf("ID: %s", monitor.ID)
+	t.Logf("Name: %s", monitor.Name)
+	t.Logf("TenantID: %s", monitor.TenantID)
+	t.Logf("Status: %s", monitor.Status)
+	t.Logf("Type: %s", monitor.Type)
+	t.Logf("Delay: %d", monitor.Delay)
+	t.Logf("Timeout: %d", monitor.Timeout)
+	t.Logf("MaxRetries: %d", monitor.MaxRetries)
+	t.Logf("HTTPMethod: %s", monitor.HTTPMethod)
+	t.Logf("URLPath: %s", monitor.URLPath)
+	t.Logf("ExpectedCodes: %s", monitor.ExpectedCodes)
+	t.Logf("AdminStateUp: %t", monitor.AdminStateUp)
+}
+
+// PrintPool will print a pool and all of its attributes.
+func PrintPool(t *testing.T, pool *pools.Pool) {
+	t.Logf("ID: %s", pool.ID)
+	t.Logf("Name: %s", pool.Name)
+	t.Logf("TenantID: %s", pool.TenantID)
+	t.Logf("Status: %s", pool.Status)
+	t.Logf("LBMethod: %s", pool.LBMethod)
+	t.Logf("Description: %s", pool.Description)
+	t.Logf("SubnetID: %s", pool.SubnetID)
+	t.Logf("AdminStateUp: %t", pool.AdminStateUp)
+	t.Logf("MonitorIDs: %s", pool.MonitorIDs)
+	t.Logf("MemberIDs: %s", pool.MemberIDs)
+}
+
+// PrintVIP will print a vip and all of its attributes.
+func PrintVIP(t *testing.T, vip *vips.VirtualIP) {
+	t.Logf("ID: %s", vip.ID)
+	t.Logf("Name: %s", vip.Name)
+	t.Logf("TenantID: %s", vip.TenantID)
+	t.Logf("Status: %s", vip.Status)
+	t.Logf("Description: %s", vip.Description)
+	t.Logf("SubnetID: %s", vip.SubnetID)
+	t.Logf("Address: %s", vip.Address)
+	t.Logf("Protocol: %s", vip.Protocol)
+	t.Logf("ProtocolPort: %d", vip.ProtocolPort)
+	t.Logf("PoolID: %s", vip.PoolID)
+	t.Logf("PortID: %s", vip.PortID)
+	t.Logf("Persistence: %s", vip.Persistence)
+	t.Logf("ConnLimit: %d", vip.ConnLimit)
+	t.Logf("AdminStateUp: %t", vip.AdminStateUp)
+}
diff --git a/acceptance/openstack/networking/v2/extensions/lbaas/member_test.go b/acceptance/openstack/networking/v2/extensions/lbaas/member_test.go
deleted file mode 100644
index dce3bbb..0000000
--- a/acceptance/openstack/networking/v2/extensions/lbaas/member_test.go
+++ /dev/null
@@ -1,95 +0,0 @@
-// +build acceptance networking lbaas lbaasmember
-
-package lbaas
-
-import (
-	"testing"
-
-	base "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
-	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/members"
-	"github.com/gophercloud/gophercloud/pagination"
-	th "github.com/gophercloud/gophercloud/testhelper"
-)
-
-func TestMembers(t *testing.T) {
-	base.Setup(t)
-	defer base.Teardown()
-
-	// setup
-	networkID, subnetID := SetupTopology(t)
-	poolID := CreatePool(t, subnetID)
-
-	// create member
-	memberID := createMember(t, poolID)
-
-	// list members
-	listMembers(t)
-
-	// update member
-	updateMember(t, memberID)
-
-	// get member
-	getMember(t, memberID)
-
-	// delete member
-	deleteMember(t, memberID)
-
-	// teardown
-	DeletePool(t, poolID)
-	DeleteTopology(t, networkID)
-}
-
-func createMember(t *testing.T, poolID string) string {
-	m, err := members.Create(base.Client, members.CreateOpts{
-		Address:      "192.168.199.1",
-		ProtocolPort: 8080,
-		PoolID:       poolID,
-	}).Extract()
-
-	th.AssertNoErr(t, err)
-
-	t.Logf("Created member: ID [%s] Status [%s] Weight [%d] Address [%s] Port [%d]",
-		m.ID, m.Status, m.Weight, m.Address, m.ProtocolPort)
-
-	return m.ID
-}
-
-func listMembers(t *testing.T) {
-	err := members.List(base.Client, members.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		memberList, err := members.ExtractMembers(page)
-		if err != nil {
-			t.Errorf("Failed to extract members: %v", err)
-			return false, err
-		}
-
-		for _, m := range memberList {
-			t.Logf("Listing member: ID [%s] Status [%s]", m.ID, m.Status)
-		}
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-}
-
-func updateMember(t *testing.T, memberID string) {
-	m, err := members.Update(base.Client, memberID, members.UpdateOpts{AdminStateUp: true}).Extract()
-
-	th.AssertNoErr(t, err)
-
-	t.Logf("Updated member ID [%s]", m.ID)
-}
-
-func getMember(t *testing.T, memberID string) {
-	m, err := members.Get(base.Client, memberID).Extract()
-
-	th.AssertNoErr(t, err)
-
-	t.Logf("Getting member ID [%s]", m.ID)
-}
-
-func deleteMember(t *testing.T, memberID string) {
-	res := members.Delete(base.Client, memberID)
-	th.AssertNoErr(t, res.Err)
-	t.Logf("Deleted member %s", memberID)
-}
diff --git a/acceptance/openstack/networking/v2/extensions/lbaas/members_test.go b/acceptance/openstack/networking/v2/extensions/lbaas/members_test.go
new file mode 100644
index 0000000..004036e
--- /dev/null
+++ b/acceptance/openstack/networking/v2/extensions/lbaas/members_test.go
@@ -0,0 +1,82 @@
+// +build acceptance networking lbaas member
+
+package lbaas
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/acceptance/clients"
+	networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/members"
+)
+
+func TestMembersList(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
+
+	allPages, err := members.List(client, members.ListOpts{}).AllPages()
+	if err != nil {
+		t.Fatalf("Unable to list members: %v", err)
+	}
+
+	allMembers, err := members.ExtractMembers(allPages)
+	if err != nil {
+		t.Fatalf("Unable to extract members: %v", err)
+	}
+
+	for _, member := range allMembers {
+		PrintMember(t, &member)
+	}
+}
+
+func TestMembersCRUD(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
+
+	network, err := networking.CreateNetwork(t, client)
+	if err != nil {
+		t.Fatalf("Unable to create network: %v", err)
+	}
+	defer networking.DeleteNetwork(t, client, network.ID)
+
+	subnet, err := networking.CreateSubnet(t, client, network.ID)
+	if err != nil {
+		t.Fatalf("Unable to create subnet: %v", err)
+	}
+	defer networking.DeleteSubnet(t, client, subnet.ID)
+
+	pool, err := CreatePool(t, client, subnet.ID)
+	if err != nil {
+		t.Fatalf("Unable to create pool: %v", err)
+	}
+	defer DeletePool(t, client, pool.ID)
+
+	member, err := CreateMember(t, client, pool.ID)
+	if err != nil {
+		t.Fatalf("Unable to create member: %v", err)
+	}
+	defer DeleteMember(t, client, member.ID)
+
+	PrintMember(t, member)
+
+	updateOpts := members.UpdateOpts{
+		AdminStateUp: gophercloud.Enabled,
+	}
+
+	_, err = members.Update(client, member.ID, updateOpts).Extract()
+	if err != nil {
+		t.Fatalf("Unable to update member: %v")
+	}
+
+	newMember, err := members.Get(client, member.ID).Extract()
+	if err != nil {
+		t.Fatalf("Unable to get member: %v")
+	}
+
+	PrintMember(t, newMember)
+}
diff --git a/acceptance/openstack/networking/v2/extensions/lbaas/monitor_test.go b/acceptance/openstack/networking/v2/extensions/lbaas/monitor_test.go
deleted file mode 100644
index e8e7192..0000000
--- a/acceptance/openstack/networking/v2/extensions/lbaas/monitor_test.go
+++ /dev/null
@@ -1,77 +0,0 @@
-// +build acceptance networking lbaas lbaasmonitor
-
-package lbaas
-
-import (
-	"testing"
-
-	base "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
-	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/monitors"
-	"github.com/gophercloud/gophercloud/pagination"
-	th "github.com/gophercloud/gophercloud/testhelper"
-)
-
-func TestMonitors(t *testing.T) {
-	base.Setup(t)
-	defer base.Teardown()
-
-	// create monitor
-	monitorID := CreateMonitor(t)
-
-	// list monitors
-	listMonitors(t)
-
-	// update monitor
-	updateMonitor(t, monitorID)
-
-	// get monitor
-	getMonitor(t, monitorID)
-
-	// delete monitor
-	deleteMonitor(t, monitorID)
-}
-
-func listMonitors(t *testing.T) {
-	err := monitors.List(base.Client, monitors.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		monitorList, err := monitors.ExtractMonitors(page)
-		if err != nil {
-			t.Errorf("Failed to extract monitors: %v", err)
-			return false, err
-		}
-
-		for _, m := range monitorList {
-			t.Logf("Listing monitor: ID [%s] Type [%s] Delay [%ds] Timeout [%d] Retries [%d] Status [%s]",
-				m.ID, m.Type, m.Delay, m.Timeout, m.MaxRetries, m.Status)
-		}
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-}
-
-func updateMonitor(t *testing.T, monitorID string) {
-	opts := monitors.UpdateOpts{Delay: 10, Timeout: 10, MaxRetries: 3}
-	m, err := monitors.Update(base.Client, monitorID, opts).Extract()
-
-	th.AssertNoErr(t, err)
-
-	t.Logf("Updated monitor ID [%s]", m.ID)
-}
-
-func getMonitor(t *testing.T, monitorID string) {
-	m, err := monitors.Get(base.Client, monitorID).Extract()
-
-	th.AssertNoErr(t, err)
-
-	t.Logf("Getting monitor ID [%s]: URL path [%s] HTTP Method [%s] Accepted codes [%s]",
-		m.ID, m.URLPath, m.HTTPMethod, m.ExpectedCodes)
-}
-
-func deleteMonitor(t *testing.T, monitorID string) {
-	res := monitors.Delete(base.Client, monitorID)
-
-	th.AssertNoErr(t, res.Err)
-
-	t.Logf("Deleted monitor %s", monitorID)
-}
diff --git a/acceptance/openstack/networking/v2/extensions/lbaas/monitors_test.go b/acceptance/openstack/networking/v2/extensions/lbaas/monitors_test.go
new file mode 100644
index 0000000..e9a7dbb
--- /dev/null
+++ b/acceptance/openstack/networking/v2/extensions/lbaas/monitors_test.go
@@ -0,0 +1,62 @@
+// +build acceptance networking lbaas monitors
+
+package lbaas
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/acceptance/clients"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/monitors"
+)
+
+func TestMonitorsList(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
+
+	allPages, err := monitors.List(client, monitors.ListOpts{}).AllPages()
+	if err != nil {
+		t.Fatalf("Unable to list monitors: %v", err)
+	}
+
+	allMonitors, err := monitors.ExtractMonitors(allPages)
+	if err != nil {
+		t.Fatalf("Unable to extract monitors: %v", err)
+	}
+
+	for _, monitor := range allMonitors {
+		PrintMonitor(t, &monitor)
+	}
+}
+
+func TestMonitorsCRUD(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
+
+	monitor, err := CreateMonitor(t, client)
+	if err != nil {
+		t.Fatalf("Unable to create monitor: %v", err)
+	}
+	defer DeleteMonitor(t, client, monitor.ID)
+
+	PrintMonitor(t, monitor)
+
+	updateOpts := monitors.UpdateOpts{
+		Delay: 999,
+	}
+
+	_, err = monitors.Update(client, monitor.ID, updateOpts).Extract()
+	if err != nil {
+		t.Fatalf("Unable to update monitor: %v")
+	}
+
+	newMonitor, err := monitors.Get(client, monitor.ID).Extract()
+	if err != nil {
+		t.Fatalf("Unable to get monitor: %v")
+	}
+
+	PrintMonitor(t, newMonitor)
+}
diff --git a/acceptance/openstack/networking/v2/extensions/lbaas/pool_test.go b/acceptance/openstack/networking/v2/extensions/lbaas/pool_test.go
deleted file mode 100644
index 6151217..0000000
--- a/acceptance/openstack/networking/v2/extensions/lbaas/pool_test.go
+++ /dev/null
@@ -1,98 +0,0 @@
-// +build acceptance networking lbaas lbaaspool
-
-package lbaas
-
-import (
-	"testing"
-
-	base "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
-	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/pools"
-	"github.com/gophercloud/gophercloud/pagination"
-	th "github.com/gophercloud/gophercloud/testhelper"
-)
-
-func TestPools(t *testing.T) {
-	base.Setup(t)
-	defer base.Teardown()
-
-	// setup
-	networkID, subnetID := SetupTopology(t)
-
-	// create pool
-	poolID := CreatePool(t, subnetID)
-
-	// list pools
-	listPools(t)
-
-	// update pool
-	updatePool(t, poolID)
-
-	// get pool
-	getPool(t, poolID)
-
-	// create monitor
-	monitorID := CreateMonitor(t)
-
-	// associate health monitor
-	associateMonitor(t, poolID, monitorID)
-
-	// disassociate health monitor
-	disassociateMonitor(t, poolID, monitorID)
-
-	// delete pool
-	DeletePool(t, poolID)
-
-	// teardown
-	DeleteTopology(t, networkID)
-}
-
-func listPools(t *testing.T) {
-	err := pools.List(base.Client, pools.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		poolList, err := pools.ExtractPools(page)
-		if err != nil {
-			t.Errorf("Failed to extract pools: %v", err)
-			return false, err
-		}
-
-		for _, p := range poolList {
-			t.Logf("Listing pool: ID [%s] Name [%s] Status [%s] LB algorithm [%s] Provider [%s]", p.ID, p.Name, p.Status, p.LBMethod, p.Provider)
-		}
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-}
-
-func updatePool(t *testing.T, poolID string) {
-	opts := pools.UpdateOpts{Name: "SuperPool", LBMethod: pools.LBMethodLeastConnections}
-	p, err := pools.Update(base.Client, poolID, opts).Extract()
-
-	th.AssertNoErr(t, err)
-
-	t.Logf("Updated pool ID [%s]", p.ID)
-}
-
-func getPool(t *testing.T, poolID string) {
-	p, err := pools.Get(base.Client, poolID).Extract()
-
-	th.AssertNoErr(t, err)
-
-	t.Logf("Getting pool ID [%s]", p.ID)
-}
-
-func associateMonitor(t *testing.T, poolID, monitorID string) {
-	res := pools.AssociateMonitor(base.Client, poolID, monitorID)
-
-	th.AssertNoErr(t, res.Err)
-
-	t.Logf("Associated pool %s with monitor %s", poolID, monitorID)
-}
-
-func disassociateMonitor(t *testing.T, poolID, monitorID string) {
-	res := pools.DisassociateMonitor(base.Client, poolID, monitorID)
-
-	th.AssertNoErr(t, res.Err)
-
-	t.Logf("Disassociated pool %s with monitor %s", poolID, monitorID)
-}
diff --git a/acceptance/openstack/networking/v2/extensions/lbaas/pools_test.go b/acceptance/openstack/networking/v2/extensions/lbaas/pools_test.go
new file mode 100644
index 0000000..8af4d19
--- /dev/null
+++ b/acceptance/openstack/networking/v2/extensions/lbaas/pools_test.go
@@ -0,0 +1,117 @@
+// +build acceptance networking lbaas pool
+
+package lbaas
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/acceptance/clients"
+	networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/pools"
+)
+
+func TestPoolsList(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
+
+	allPages, err := pools.List(client, pools.ListOpts{}).AllPages()
+	if err != nil {
+		t.Fatalf("Unable to list pools: %v", err)
+	}
+
+	allPools, err := pools.ExtractPools(allPages)
+	if err != nil {
+		t.Fatalf("Unable to extract pools: %v", err)
+	}
+
+	for _, pool := range allPools {
+		PrintPool(t, &pool)
+	}
+}
+
+func TestPoolsCRUD(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
+
+	network, err := networking.CreateNetwork(t, client)
+	if err != nil {
+		t.Fatalf("Unable to create network: %v", err)
+	}
+	defer networking.DeleteNetwork(t, client, network.ID)
+
+	subnet, err := networking.CreateSubnet(t, client, network.ID)
+	if err != nil {
+		t.Fatalf("Unable to create subnet: %v", err)
+	}
+	defer networking.DeleteSubnet(t, client, subnet.ID)
+
+	pool, err := CreatePool(t, client, subnet.ID)
+	if err != nil {
+		t.Fatalf("Unable to create pool: %v", err)
+	}
+	defer DeletePool(t, client, pool.ID)
+
+	PrintPool(t, pool)
+
+	updateOpts := pools.UpdateOpts{
+		LBMethod: pools.LBMethodLeastConnections,
+	}
+
+	_, err = pools.Update(client, pool.ID, updateOpts).Extract()
+	if err != nil {
+		t.Fatalf("Unable to update pool: %v")
+	}
+
+	newPool, err := pools.Get(client, pool.ID).Extract()
+	if err != nil {
+		t.Fatalf("Unable to get pool: %v")
+	}
+
+	PrintPool(t, newPool)
+}
+
+func TestPoolsMonitors(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
+
+	network, err := networking.CreateNetwork(t, client)
+	if err != nil {
+		t.Fatalf("Unable to create network: %v", err)
+	}
+	defer networking.DeleteNetwork(t, client, network.ID)
+
+	subnet, err := networking.CreateSubnet(t, client, network.ID)
+	if err != nil {
+		t.Fatalf("Unable to create subnet: %v", err)
+	}
+	defer networking.DeleteSubnet(t, client, subnet.ID)
+
+	pool, err := CreatePool(t, client, subnet.ID)
+	if err != nil {
+		t.Fatalf("Unable to create pool: %v", err)
+	}
+	defer DeletePool(t, client, pool.ID)
+
+	monitor, err := CreateMonitor(t, client)
+	if err != nil {
+		t.Fatalf("Unable to create monitor: %v", err)
+	}
+	defer DeleteMonitor(t, client, monitor.ID)
+
+	t.Logf("Associating monitor %s with pool %s", monitor.ID, pool.ID)
+	if res := pools.AssociateMonitor(client, pool.ID, monitor.ID); res.Err != nil {
+		t.Fatalf("Unable to associate monitor to pool")
+	}
+
+	t.Logf("Disassociating monitor %s with pool %s", monitor.ID, pool.ID)
+	if res := pools.DisassociateMonitor(client, pool.ID, monitor.ID); res.Err != nil {
+		t.Fatalf("Unable to disassociate monitor from pool")
+	}
+
+}
diff --git a/acceptance/openstack/networking/v2/extensions/lbaas/vip_test.go b/acceptance/openstack/networking/v2/extensions/lbaas/vip_test.go
deleted file mode 100644
index d38e9c1..0000000
--- a/acceptance/openstack/networking/v2/extensions/lbaas/vip_test.go
+++ /dev/null
@@ -1,101 +0,0 @@
-// +build acceptance networking lbaas lbaasvip
-
-package lbaas
-
-import (
-	"testing"
-
-	base "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
-	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/vips"
-	"github.com/gophercloud/gophercloud/pagination"
-	th "github.com/gophercloud/gophercloud/testhelper"
-)
-
-func TestVIPs(t *testing.T) {
-	base.Setup(t)
-	defer base.Teardown()
-
-	// setup
-	networkID, subnetID := SetupTopology(t)
-	poolID := CreatePool(t, subnetID)
-
-	// create VIP
-	VIPID := createVIP(t, subnetID, poolID)
-
-	// list VIPs
-	listVIPs(t)
-
-	// update VIP
-	updateVIP(t, VIPID)
-
-	// get VIP
-	getVIP(t, VIPID)
-
-	// delete VIP
-	deleteVIP(t, VIPID)
-
-	// teardown
-	DeletePool(t, poolID)
-	DeleteTopology(t, networkID)
-}
-
-func createVIP(t *testing.T, subnetID, poolID string) string {
-	p, err := vips.Create(base.Client, vips.CreateOpts{
-		Protocol:     "HTTP",
-		Name:         "New_VIP",
-		AdminStateUp: vips.Up,
-		SubnetID:     subnetID,
-		PoolID:       poolID,
-		ProtocolPort: 80,
-	}).Extract()
-
-	th.AssertNoErr(t, err)
-
-	t.Logf("Created pool %s", p.ID)
-
-	return p.ID
-}
-
-func listVIPs(t *testing.T) {
-	err := vips.List(base.Client, vips.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		vipList, err := vips.ExtractVIPs(page)
-		if err != nil {
-			t.Errorf("Failed to extract VIPs: %v", err)
-			return false, err
-		}
-
-		for _, vip := range vipList {
-			t.Logf("Listing VIP: ID [%s] Name [%s] Address [%s] Port [%s] Connection Limit [%d]",
-				vip.ID, vip.Name, vip.Address, vip.ProtocolPort, vip.ConnLimit)
-		}
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-}
-
-func updateVIP(t *testing.T, VIPID string) {
-	i1000 := 1000
-	_, err := vips.Update(base.Client, VIPID, vips.UpdateOpts{ConnLimit: &i1000}).Extract()
-
-	th.AssertNoErr(t, err)
-
-	t.Logf("Updated VIP ID [%s]", VIPID)
-}
-
-func getVIP(t *testing.T, VIPID string) {
-	vip, err := vips.Get(base.Client, VIPID).Extract()
-
-	th.AssertNoErr(t, err)
-
-	t.Logf("Getting VIP ID [%s]: Status [%s]", vip.ID, vip.Status)
-}
-
-func deleteVIP(t *testing.T, VIPID string) {
-	res := vips.Delete(base.Client, VIPID)
-
-	th.AssertNoErr(t, res.Err)
-
-	t.Logf("Deleted VIP %s", VIPID)
-}
diff --git a/acceptance/openstack/networking/v2/extensions/lbaas/vips_test.go b/acceptance/openstack/networking/v2/extensions/lbaas/vips_test.go
new file mode 100644
index 0000000..1f61b36
--- /dev/null
+++ b/acceptance/openstack/networking/v2/extensions/lbaas/vips_test.go
@@ -0,0 +1,82 @@
+// +build acceptance networking lbaas vip
+
+package lbaas
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/acceptance/clients"
+	networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/vips"
+)
+
+func TestVIPsList(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
+
+	allPages, err := vips.List(client, vips.ListOpts{}).AllPages()
+	if err != nil {
+		t.Fatalf("Unable to list vips: %v", err)
+	}
+
+	allVIPs, err := vips.ExtractVIPs(allPages)
+	if err != nil {
+		t.Fatalf("Unable to extract vips: %v", err)
+	}
+
+	for _, vip := range allVIPs {
+		PrintVIP(t, &vip)
+	}
+}
+
+func TestVIPsCRUD(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
+
+	network, err := networking.CreateNetwork(t, client)
+	if err != nil {
+		t.Fatalf("Unable to create network: %v", err)
+	}
+	defer networking.DeleteNetwork(t, client, network.ID)
+
+	subnet, err := networking.CreateSubnet(t, client, network.ID)
+	if err != nil {
+		t.Fatalf("Unable to create subnet: %v", err)
+	}
+	defer networking.DeleteSubnet(t, client, subnet.ID)
+
+	pool, err := CreatePool(t, client, subnet.ID)
+	if err != nil {
+		t.Fatalf("Unable to create pool: %v", err)
+	}
+	defer DeletePool(t, client, pool.ID)
+
+	vip, err := CreateVIP(t, client, subnet.ID, pool.ID)
+	if err != nil {
+		t.Fatalf("Unable to create vip: %v", err)
+	}
+	defer DeleteVIP(t, client, vip.ID)
+
+	PrintVIP(t, vip)
+
+	connLimit := 100
+	updateOpts := vips.UpdateOpts{
+		ConnLimit: &connLimit,
+	}
+
+	_, err = vips.Update(client, vip.ID, updateOpts).Extract()
+	if err != nil {
+		t.Fatalf("Unable to update vip: %v")
+	}
+
+	newVIP, err := vips.Get(client, vip.ID).Extract()
+	if err != nil {
+		t.Fatalf("Unable to get vip: %v")
+	}
+
+	PrintVIP(t, newVIP)
+}
diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go b/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go
new file mode 100644
index 0000000..d2e7195
--- /dev/null
+++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go
@@ -0,0 +1,392 @@
+package lbaas_v2
+
+import (
+	"fmt"
+	"strings"
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/acceptance/tools"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/loadbalancers"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools"
+)
+
+const loadbalancerActiveTimeoutSeconds = 300
+const loadbalancerDeleteTimeoutSeconds = 300
+
+// CreateListener will create a listener for a given load balancer on a random
+// port with a random name. An error will be returned if the listener could not
+// be created.
+func CreateListener(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer) (*listeners.Listener, error) {
+	listenerName := tools.RandomString("TESTACCT-", 8)
+	listenerPort := tools.RandomInt(1, 100)
+
+	t.Logf("Attempting to create listener %s on port %d", listenerName, listenerPort)
+
+	createOpts := listeners.CreateOpts{
+		Name:           listenerName,
+		LoadbalancerID: lb.ID,
+		Protocol:       "TCP",
+		ProtocolPort:   listenerPort,
+	}
+
+	listener, err := listeners.Create(client, createOpts).Extract()
+	if err != nil {
+		return listener, err
+	}
+
+	t.Logf("Successfully created listener %s", listenerName)
+
+	if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil {
+		return listener, fmt.Errorf("Timed out waiting for loadbalancer to become active")
+	}
+
+	return listener, nil
+}
+
+// CreateLoadBalancer will create a load balancer with a random name on a given
+// subnet. An error will be returned if the loadbalancer could not be created.
+func CreateLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, subnetID string) (*loadbalancers.LoadBalancer, error) {
+	lbName := tools.RandomString("TESTACCT-", 8)
+
+	t.Logf("Attempting to create loadbalancer %s on subnet %s", lbName, subnetID)
+
+	createOpts := loadbalancers.CreateOpts{
+		Name:         lbName,
+		VipSubnetID:  subnetID,
+		AdminStateUp: gophercloud.Enabled,
+	}
+
+	lb, err := loadbalancers.Create(client, createOpts).Extract()
+	if err != nil {
+		return lb, err
+	}
+
+	t.Logf("Successfully created loadbalancer %s on subnet %s", lbName, subnetID)
+	t.Logf("Waiting for loadbalancer %s to become active", lbName)
+
+	if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil {
+		return lb, err
+	}
+
+	t.Logf("LoadBalancer %s is active", lbName)
+
+	return lb, nil
+}
+
+// CreateMember will create a member with a random name, port, address, and
+// weight. An error will be returned if the member could not be created.
+func CreateMember(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer, pool *pools.Pool, subnetID, subnetCIDR string) (*pools.Member, error) {
+	memberName := tools.RandomString("TESTACCT-", 8)
+	memberPort := tools.RandomInt(100, 1000)
+	memberWeight := tools.RandomInt(1, 10)
+
+	cidrParts := strings.Split(subnetCIDR, "/")
+	subnetParts := strings.Split(cidrParts[0], ".")
+	memberAddress := fmt.Sprintf("%s.%s.%s.%d", subnetParts[0], subnetParts[1], subnetParts[2], tools.RandomInt(10, 100))
+
+	t.Logf("Attempting to create member %s", memberName)
+
+	createOpts := pools.CreateMemberOpts{
+		Name:         memberName,
+		ProtocolPort: memberPort,
+		Weight:       memberWeight,
+		Address:      memberAddress,
+		SubnetID:     subnetID,
+	}
+
+	t.Logf("Member create opts: %#v", createOpts)
+
+	member, err := pools.CreateMember(client, pool.ID, createOpts).Extract()
+	if err != nil {
+		return member, err
+	}
+
+	t.Logf("Successfully created member %s", memberName)
+
+	if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil {
+		return member, fmt.Errorf("Timed out waiting for loadbalancer to become active")
+	}
+
+	return member, nil
+}
+
+// CreateMonitor will create a monitor with a random name for a specific pool.
+// An error will be returned if the monitor could not be created.
+func CreateMonitor(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer, pool *pools.Pool) (*monitors.Monitor, error) {
+	monitorName := tools.RandomString("TESTACCT-", 8)
+
+	t.Logf("Attempting to create monitor %s", monitorName)
+
+	createOpts := monitors.CreateOpts{
+		PoolID:     pool.ID,
+		Name:       monitorName,
+		Delay:      10,
+		Timeout:    5,
+		MaxRetries: 5,
+		Type:       "PING",
+	}
+
+	monitor, err := monitors.Create(client, createOpts).Extract()
+	if err != nil {
+		return monitor, err
+	}
+
+	t.Logf("Successfully created monitor: %s", monitorName)
+
+	if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil {
+		return monitor, fmt.Errorf("Timed out waiting for loadbalancer to become active")
+	}
+
+	return monitor, nil
+}
+
+// CreatePool will create a pool with a random name with a specified listener
+// and loadbalancer. An error will be returned if the pool could not be
+// created.
+func CreatePool(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer) (*pools.Pool, error) {
+	poolName := tools.RandomString("TESTACCT-", 8)
+
+	t.Logf("Attempting to create pool %s", poolName)
+
+	createOpts := pools.CreateOpts{
+		Name:           poolName,
+		Protocol:       pools.ProtocolTCP,
+		LoadbalancerID: lb.ID,
+		LBMethod:       pools.LBMethodLeastConnections,
+	}
+
+	pool, err := pools.Create(client, createOpts).Extract()
+	if err != nil {
+		return pool, err
+	}
+
+	t.Logf("Successfully created pool %s", poolName)
+
+	if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil {
+		return pool, fmt.Errorf("Timed out waiting for loadbalancer to become active")
+	}
+
+	return pool, nil
+}
+
+// DeleteListener will delete a specified listener. A fatal error will occur if
+// the listener could not be deleted. This works best when used as a deferred
+// function.
+func DeleteListener(t *testing.T, client *gophercloud.ServiceClient, lbID, listenerID string) {
+	t.Logf("Attempting to delete listener %s", listenerID)
+
+	if err := listeners.Delete(client, listenerID).ExtractErr(); err != nil {
+		t.Fatalf("Unable to delete listener: %v", err)
+	}
+
+	if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil {
+		t.Fatalf("Timed out waiting for loadbalancer to become active")
+	}
+
+	t.Logf("Successfully deleted listener %s", listenerID)
+}
+
+// DeleteMember will delete a specified member. A fatal error will occur if the
+// member could not be deleted. This works best when used as a deferred
+// function.
+func DeleteMember(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID, memberID string) {
+	t.Logf("Attempting to delete member %s", memberID)
+
+	if err := pools.DeleteMember(client, poolID, memberID).ExtractErr(); err != nil {
+		t.Fatalf("Unable to delete member: %s", memberID)
+	}
+
+	if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil {
+		t.Fatalf("Timed out waiting for loadbalancer to become active")
+	}
+
+	t.Logf("Successfully deleted member %s", memberID)
+}
+
+// DeleteLoadBalancer will delete a specified loadbalancer. A fatal error will
+// occur if the loadbalancer could not be deleted. This works best when used
+// as a deferred function.
+func DeleteLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, lbID string) {
+	t.Logf("Attempting to delete loadbalancer %s", lbID)
+
+	if err := loadbalancers.Delete(client, lbID).ExtractErr(); err != nil {
+		t.Fatalf("Unable to delete loadbalancer: %v", err)
+	}
+
+	t.Logf("Waiting for loadbalancer %s to delete", lbID)
+
+	if err := WaitForLoadBalancerState(client, lbID, "DELETED", loadbalancerActiveTimeoutSeconds); err != nil {
+		t.Fatalf("Loadbalancer did not delete in time.")
+	}
+
+	t.Logf("Successfully deleted loadbalancer %s", lbID)
+}
+
+// DeleteMonitor will delete a specified monitor. A fatal error will occur if
+// the monitor could not be deleted. This works best when used as a deferred
+// function.
+func DeleteMonitor(t *testing.T, client *gophercloud.ServiceClient, lbID, monitorID string) {
+	t.Logf("Attempting to delete monitor %s", monitorID)
+
+	if err := monitors.Delete(client, monitorID).ExtractErr(); err != nil {
+		t.Fatalf("Unable to delete monitor: %v", err)
+	}
+
+	if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil {
+		t.Fatalf("Timed out waiting for loadbalancer to become active")
+	}
+
+	t.Logf("Successfully deleted monitor %s", monitorID)
+}
+
+// DeletePool will delete a specified pool. A fatal error will occur if the
+// pool could not be deleted. This works best when used as a deferred function.
+func DeletePool(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID string) {
+	t.Logf("Attempting to delete pool %s", poolID)
+
+	if err := pools.Delete(client, poolID).ExtractErr(); err != nil {
+		t.Fatalf("Unable to delete pool: %v", err)
+	}
+
+	if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil {
+		t.Fatalf("Timed out waiting for loadbalancer to become active")
+	}
+
+	t.Logf("Successfully deleted pool %s", poolID)
+}
+
+// PrintListener will print a listener and all of its attributes.
+func PrintListener(t *testing.T, listener *listeners.Listener) {
+	t.Logf("ID: %s", listener.ID)
+	t.Logf("TenantID: %s", listener.TenantID)
+	t.Logf("Name: %s", listener.Name)
+	t.Logf("Description: %s", listener.Description)
+	t.Logf("Protocol: %s", listener.Protocol)
+	t.Logf("DefaultPoolID: %s", listener.DefaultPoolID)
+	t.Logf("ConnLimit: %d", listener.ConnLimit)
+	t.Logf("SniContainerRefs: %s", listener.SniContainerRefs)
+	t.Logf("DefaultTlsContainerRef: %s", listener.DefaultTlsContainerRef)
+	t.Logf("AdminStateUp: %t", listener.AdminStateUp)
+
+	t.Logf("Pools:")
+	for _, pool := range listener.Pools {
+		t.Logf("\t%#v", pool)
+	}
+
+	t.Logf("LoadBalancers")
+	for _, lb := range listener.Loadbalancers {
+		t.Logf("\t%#v", lb)
+	}
+}
+
+// PrintLoadBalancer will print a load balancer and all of its attributes.
+func PrintLoadBalancer(t *testing.T, lb *loadbalancers.LoadBalancer) {
+	t.Logf("ID: %s", lb.ID)
+	t.Logf("Name: %s", lb.Name)
+	t.Logf("TenantID: %s", lb.TenantID)
+	t.Logf("Description: %s", lb.Description)
+	t.Logf("ProvisioningStatus: %s", lb.ProvisioningStatus)
+	t.Logf("VipAddress: %s", lb.VipAddress)
+	t.Logf("VipPortID: %s", lb.VipPortID)
+	t.Logf("VipSubnetID: %s", lb.VipSubnetID)
+	t.Logf("OperatingStatus: %s", lb.OperatingStatus)
+	t.Logf("Flavor: %s", lb.Flavor)
+	t.Logf("Provider: %s", lb.Provider)
+	t.Logf("AdminStateUp: %t", lb.AdminStateUp)
+
+	t.Logf("Listeners")
+	for _, listener := range lb.Listeners {
+		t.Logf("\t%#v", listener)
+	}
+}
+
+// PrintMember will print a member and all of its attributes.
+func PrintMember(t *testing.T, member *pools.Member) {
+	t.Logf("ID: %s", member.ID)
+	t.Logf("Name: %s", member.Name)
+	t.Logf("TenantID: %s", member.TenantID)
+	t.Logf("Weight: %d", member.Weight)
+	t.Logf("SubnetID: %s", member.SubnetID)
+	t.Logf("PoolID: %s", member.PoolID)
+	t.Logf("Address: %s", member.Address)
+	t.Logf("ProtocolPort: %d", member.ProtocolPort)
+	t.Logf("AdminStateUp: %t", member.AdminStateUp)
+}
+
+// PrintMonitor will print a monitor and all of its attributes.
+func PrintMonitor(t *testing.T, monitor *monitors.Monitor) {
+	t.Logf("ID: %s", monitor.ID)
+	t.Logf("Name: %s", monitor.Name)
+	t.Logf("TenantID: %s", monitor.TenantID)
+	t.Logf("Type: %s", monitor.Type)
+	t.Logf("Delay: %d", monitor.Delay)
+	t.Logf("Timeout: %d", monitor.Timeout)
+	t.Logf("MaxRetries: %d", monitor.MaxRetries)
+	t.Logf("HTTPMethod: %s", monitor.HTTPMethod)
+	t.Logf("URLPath: %s", monitor.URLPath)
+	t.Logf("ExpectedCodes: %s", monitor.ExpectedCodes)
+	t.Logf("AdminStateUp: %t", monitor.AdminStateUp)
+	t.Logf("Status: %s", monitor.Status)
+
+	t.Logf("Pools")
+	for _, pool := range monitor.Pools {
+		t.Logf("\t%#v", pool)
+	}
+}
+
+// PrintPool will print a pool and all of its attributes.
+func PrintPool(t *testing.T, pool *pools.Pool) {
+	t.Logf("ID: %s", pool.ID)
+	t.Logf("Name: %s", pool.Name)
+	t.Logf("TenantID: %s", pool.TenantID)
+	t.Logf("Description: %s", pool.Description)
+	t.Logf("LBMethod: %s", pool.LBMethod)
+	t.Logf("Protocol: %s", pool.Protocol)
+	t.Logf("MonitorID: %s", pool.MonitorID)
+	t.Logf("SubnetID: %s", pool.SubnetID)
+	t.Logf("AdminStateUp: %t", pool.AdminStateUp)
+	t.Logf("Persistence: %s", pool.Persistence)
+	t.Logf("Provider: %s", pool.Provider)
+	t.Logf("Monitor: %#v", pool.Monitor)
+
+	t.Logf("Listeners")
+	for _, listener := range pool.Listeners {
+		t.Logf("\t%#v", listener)
+	}
+
+	t.Logf("Members")
+	for _, member := range pool.Members {
+		t.Logf("\t%#v", member)
+	}
+
+	t.Logf("Loadbalancers")
+	for _, lb := range pool.Loadbalancers {
+		t.Logf("\t%#v", lb)
+	}
+}
+
+// WaitForLoadBalancerState will wait until a loadbalancer reaches a given state.
+func WaitForLoadBalancerState(client *gophercloud.ServiceClient, lbID, status string, secs int) error {
+	return gophercloud.WaitFor(secs, func() (bool, error) {
+		current, err := loadbalancers.Get(client, lbID).Extract()
+		if err != nil {
+			if httpStatus, ok := err.(gophercloud.ErrDefault404); ok {
+				if httpStatus.Actual == 404 {
+					if status == "DELETED" {
+						return true, nil
+					}
+				}
+			}
+			return false, err
+		}
+
+		if current.ProvisioningStatus == status {
+			return true, nil
+		}
+
+		return false, nil
+	})
+}
diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go b/acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go
new file mode 100644
index 0000000..ac2681e
--- /dev/null
+++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go
@@ -0,0 +1,31 @@
+// +build acceptance networking lbaas_v2 listeners
+
+package lbaas_v2
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/acceptance/clients"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners"
+)
+
+func TestListenersList(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
+
+	allPages, err := listeners.List(client, nil).AllPages()
+	if err != nil {
+		t.Fatalf("Unable to list listeners: %v", err)
+	}
+
+	allListeners, err := listeners.ExtractListeners(allPages)
+	if err != nil {
+		t.Fatalf("Unable to extract listeners: %v", err)
+	}
+
+	for _, listener := range allListeners {
+		PrintListener(t, &listener)
+	}
+}
diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancer_test.go b/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancer_test.go
deleted file mode 100644
index 051b7eb..0000000
--- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancer_test.go
+++ /dev/null
@@ -1,493 +0,0 @@
-// +build acceptance networking lbaas_v2 lbaasloadbalancer
-
-package lbaas_v2
-
-import (
-	"testing"
-	"time"
-
-	"github.com/gophercloud/gophercloud"
-	base "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
-	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners"
-	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/loadbalancers"
-	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors"
-	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools"
-	"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
-	"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
-	"github.com/gophercloud/gophercloud/pagination"
-	th "github.com/gophercloud/gophercloud/testhelper"
-)
-
-// Note: when creating a new Loadbalancer (VM), it can take some time before it is ready for use,
-// this timeout is used for waiting until the Loadbalancer provisioning status goes to ACTIVE state.
-const loadbalancerActiveTimeoutSeconds = 120
-const loadbalancerDeleteTimeoutSeconds = 10
-
-func setupTopology(t *testing.T) (string, string) {
-	// create network
-	n, err := networks.Create(base.Client, networks.CreateOpts{Name: "tmp_network"}).Extract()
-	th.AssertNoErr(t, err)
-
-	t.Logf("Created network, ID %s", n.ID)
-
-	// create subnet
-	s, err := subnets.Create(base.Client, subnets.CreateOpts{
-		NetworkID: n.ID,
-		CIDR:      "192.168.199.0/24",
-		IPVersion: subnets.IPv4,
-		Name:      "tmp_subnet",
-	}).Extract()
-	th.AssertNoErr(t, err)
-
-	t.Logf("Created subnet, ID %s", s.ID)
-
-	return n.ID, s.ID
-}
-
-func deleteTopology(t *testing.T, networkID string) {
-	res := networks.Delete(base.Client, networkID)
-	th.AssertNoErr(t, res.Err)
-	t.Logf("deleted network, ID %s", networkID)
-}
-
-func TestLoadbalancers(t *testing.T) {
-	base.Setup(t)
-	defer base.Teardown()
-
-	// setup network topology
-	networkID, subnetID := setupTopology(t)
-
-	// create Loadbalancer
-	loadbalancerID := createLoadbalancer(t, subnetID)
-
-	// list Loadbalancers
-	listLoadbalancers(t)
-
-	// get Loadbalancer and wait until ACTIVE
-	getLoadbalancerWaitActive(t, loadbalancerID)
-
-	// update Loadbalancer
-	updateLoadbalancer(t, loadbalancerID)
-
-	// get Loadbalancer and wait until ACTIVE
-	getLoadbalancerWaitActive(t, loadbalancerID)
-
-	// create listener
-	listenerID := createListener(t, listeners.ProtocolHTTP, 80, loadbalancerID)
-
-	// list listeners
-	listListeners(t)
-
-	// get Loadbalancer and wait until ACTIVE
-	getLoadbalancerWaitActive(t, loadbalancerID)
-
-	// update listener
-	updateListener(t, listenerID)
-
-	// get listener
-	getListener(t, listenerID)
-
-	// get Loadbalancer and wait until ACTIVE
-	getLoadbalancerWaitActive(t, loadbalancerID)
-
-	// create pool
-	poolID := createPool(t, pools.ProtocolHTTP, listenerID, pools.LBMethodRoundRobin)
-
-	// list pools
-	listPools(t)
-
-	// get Loadbalancer and wait until ACTIVE
-	getLoadbalancerWaitActive(t, loadbalancerID)
-
-	// update pool
-	updatePool(t, poolID)
-
-	// get pool
-	getPool(t, poolID)
-
-	// get Loadbalancer and wait until ACTIVE
-	getLoadbalancerWaitActive(t, loadbalancerID)
-
-	// create member
-	memberID := createMember(t, subnetID, poolID, "1.2.3.4", 80, 5)
-
-	// list members
-	listMembers(t, poolID)
-
-	// get Loadbalancer and wait until ACTIVE
-	getLoadbalancerWaitActive(t, loadbalancerID)
-
-	// update member
-	updateMember(t, poolID, memberID)
-
-	// get member
-	getMember(t, poolID, memberID)
-
-	// get Loadbalancer and wait until ACTIVE
-	getLoadbalancerWaitActive(t, loadbalancerID)
-
-	// create monitor
-	monitorID := createMonitor(t, poolID, monitors.TypePING, 10, 10, 3)
-
-	// list monitors
-	listMonitors(t)
-
-	// get Loadbalancer and wait until ACTIVE
-	getLoadbalancerWaitActive(t, loadbalancerID)
-
-	// update monitor
-	updateMonitor(t, monitorID)
-
-	// get monitor
-	getMonitor(t, monitorID)
-
-	// get loadbalancer statuses tree
-	rawStatusTree, err := loadbalancers.GetStatuses(base.Client, loadbalancerID).ExtractStatuses()
-	if err == nil {
-		// verify statuses tree ID's of relevant objects
-		if rawStatusTree.Loadbalancer.ID != loadbalancerID {
-			t.Errorf("Loadbalancer ID did not match")
-		}
-		if rawStatusTree.Loadbalancer.Listeners[0].ID != listenerID {
-			t.Errorf("Listner ID did not match")
-		}
-		if rawStatusTree.Loadbalancer.Listeners[0].Pools[0].ID != poolID {
-			t.Errorf("Pool ID did not match")
-		}
-		if rawStatusTree.Loadbalancer.Listeners[0].Pools[0].Members[0].ID != memberID {
-			t.Errorf("Member ID did not match")
-		}
-		if rawStatusTree.Loadbalancer.Listeners[0].Pools[0].Monitor.ID != monitorID {
-			t.Errorf("Monitor ID did not match")
-		}
-	} else {
-		t.Errorf("Failed to extract Loadbalancer statuses tree: %v", err)
-	}
-
-	getLoadbalancerWaitActive(t, loadbalancerID)
-	deleteMonitor(t, monitorID)
-	getLoadbalancerWaitActive(t, loadbalancerID)
-	deleteMember(t, poolID, memberID)
-	getLoadbalancerWaitActive(t, loadbalancerID)
-	deletePool(t, poolID)
-	getLoadbalancerWaitActive(t, loadbalancerID)
-	deleteListener(t, listenerID)
-	getLoadbalancerWaitActive(t, loadbalancerID)
-	deleteLoadbalancer(t, loadbalancerID)
-	getLoadbalancerWaitDeleted(t, loadbalancerID)
-	deleteTopology(t, networkID)
-}
-
-func createLoadbalancer(t *testing.T, subnetID string) string {
-	lb, err := loadbalancers.Create(base.Client, loadbalancers.CreateOpts{
-		VipSubnetID:  subnetID,
-		Name:         "tmp_loadbalancer",
-		AdminStateUp: loadbalancers.Up,
-	}).Extract()
-
-	th.AssertNoErr(t, err)
-	t.Logf("Created Loadbalancer, ID %s", lb.ID)
-
-	return lb.ID
-}
-
-func deleteLoadbalancer(t *testing.T, loadbalancerID string) {
-	res := loadbalancers.Delete(base.Client, loadbalancerID)
-	th.AssertNoErr(t, res.Err)
-	t.Logf("deleted Loadbalancer, ID %s", loadbalancerID)
-}
-
-func listLoadbalancers(t *testing.T) {
-	err := loadbalancers.List(base.Client, loadbalancers.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		loadbalancerList, err := loadbalancers.ExtractLoadbalancers(page)
-		if err != nil {
-			t.Errorf("Failed to extract Loadbalancers: %v", err)
-			return false, err
-		}
-
-		for _, loadbalancer := range loadbalancerList {
-			t.Logf("Listing Loadbalancer: ID [%s] Name [%s] Address [%s]",
-				loadbalancer.ID, loadbalancer.Name, loadbalancer.VipAddress)
-		}
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-}
-
-func getLoadbalancerWaitDeleted(t *testing.T, loadbalancerID string) {
-	start := time.Now().Second()
-	for {
-		time.Sleep(1 * time.Second)
-
-		if time.Now().Second()-start >= loadbalancerDeleteTimeoutSeconds {
-			t.Errorf("Loadbalancer failed to delete")
-			return
-		}
-
-		_, err := loadbalancers.Get(base.Client, loadbalancerID).Extract()
-		if err != nil {
-			if errData, ok := err.(*(gophercloud.UnexpectedResponseCodeError)); ok {
-				if errData.Actual == 404 {
-					return
-				}
-			} else {
-				th.AssertNoErr(t, err)
-			}
-		}
-	}
-}
-
-func getLoadbalancerWaitActive(t *testing.T, loadbalancerID string) {
-	start := time.Now().Second()
-	for {
-		time.Sleep(1 * time.Second)
-
-		if time.Now().Second()-start >= loadbalancerActiveTimeoutSeconds {
-			t.Errorf("Loadbalancer failed to go into ACTIVE provisioning status")
-			return
-		}
-
-		loadbalancer, err := loadbalancers.Get(base.Client, loadbalancerID).Extract()
-		th.AssertNoErr(t, err)
-		if loadbalancer.ProvisioningStatus == "ACTIVE" {
-			t.Logf("Retrieved Loadbalancer, ID [%s]: OperatingStatus [%s]", loadbalancer.ID, loadbalancer.ProvisioningStatus)
-			return
-		}
-	}
-}
-
-func updateLoadbalancer(t *testing.T, loadbalancerID string) {
-	_, err := loadbalancers.Update(base.Client, loadbalancerID, loadbalancers.UpdateOpts{Name: "tmp_newName"}).Extract()
-
-	th.AssertNoErr(t, err)
-
-	t.Logf("Updated Loadbalancer ID [%s]", loadbalancerID)
-}
-
-func listListeners(t *testing.T) {
-	err := listeners.List(base.Client, listeners.ListOpts{Name: "tmp_listener"}).EachPage(func(page pagination.Page) (bool, error) {
-		listenerList, err := listeners.ExtractListeners(page)
-		if err != nil {
-			t.Errorf("Failed to extract Listeners: %v", err)
-			return false, err
-		}
-
-		for _, listener := range listenerList {
-			t.Logf("Listing Listener: ID [%s] Name [%s] Loadbalancers [%s]",
-				listener.ID, listener.Name, listener.Loadbalancers)
-		}
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-}
-
-func createListener(t *testing.T, protocol listeners.Protocol, protocolPort int, loadbalancerID string) string {
-	l, err := listeners.Create(base.Client, listeners.CreateOpts{
-		Protocol:       protocol,
-		ProtocolPort:   protocolPort,
-		LoadbalancerID: loadbalancerID,
-		Name:           "tmp_listener",
-	}).Extract()
-
-	th.AssertNoErr(t, err)
-	t.Logf("Created Listener, ID %s", l.ID)
-
-	return l.ID
-}
-
-func deleteListener(t *testing.T, listenerID string) {
-	res := listeners.Delete(base.Client, listenerID)
-	th.AssertNoErr(t, res.Err)
-	t.Logf("Deleted Loadbalancer, ID %s", listenerID)
-}
-
-func getListener(t *testing.T, listenerID string) {
-	listener, err := listeners.Get(base.Client, listenerID).Extract()
-
-	th.AssertNoErr(t, err)
-
-	t.Logf("Getting Listener, ID [%s]: ", listener.ID)
-}
-
-func updateListener(t *testing.T, listenerID string) {
-	_, err := listeners.Update(base.Client, listenerID, listeners.UpdateOpts{Name: "tmp_newName"}).Extract()
-
-	th.AssertNoErr(t, err)
-
-	t.Logf("Updated Listener, ID [%s]", listenerID)
-}
-
-func listPools(t *testing.T) {
-	err := pools.List(base.Client, pools.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		poolsList, err := pools.ExtractPools(page)
-		if err != nil {
-			t.Errorf("Failed to extract Pools: %v", err)
-			return false, err
-		}
-
-		for _, pool := range poolsList {
-			t.Logf("Listing Pool: ID [%s] Name [%s] Listeners [%s] LBMethod [%s]",
-				pool.ID, pool.Name, pool.Listeners, pool.LBMethod)
-		}
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-}
-
-func createPool(t *testing.T, protocol pools.Protocol, listenerID string, lbMethod pools.LBMethod) string {
-	p, err := pools.Create(base.Client, pools.CreateOpts{
-		LBMethod:   lbMethod,
-		Protocol:   protocol,
-		Name:       "tmp_pool",
-		ListenerID: listenerID,
-	}).Extract()
-
-	th.AssertNoErr(t, err)
-
-	t.Logf("Created Pool, ID %s", p.ID)
-
-	return p.ID
-}
-
-func deletePool(t *testing.T, poolID string) {
-	res := pools.Delete(base.Client, poolID)
-	th.AssertNoErr(t, res.Err)
-	t.Logf("Deleted Pool, ID %s", poolID)
-}
-
-func getPool(t *testing.T, poolID string) {
-	pool, err := pools.Get(base.Client, poolID).Extract()
-
-	th.AssertNoErr(t, err)
-
-	t.Logf("Getting Pool, ID [%s]: ", pool.ID)
-}
-
-func updatePool(t *testing.T, poolID string) {
-	_, err := pools.Update(base.Client, poolID, pools.UpdateOpts{Name: "tmp_newName"}).Extract()
-
-	th.AssertNoErr(t, err)
-
-	t.Logf("Updated Pool, ID [%s]", poolID)
-}
-
-func createMember(t *testing.T, subnetID string, poolID string, address string, protocolPort int, weight int) string {
-	m, err := pools.CreateAssociateMember(base.Client, poolID, pools.MemberCreateOpts{
-		SubnetID:     subnetID,
-		Address:      address,
-		ProtocolPort: protocolPort,
-		Weight:       weight,
-		Name:         "tmp_member",
-	}).ExtractMember()
-
-	th.AssertNoErr(t, err)
-
-	t.Logf("Created Member, ID %s", m.ID)
-
-	return m.ID
-}
-
-func deleteMember(t *testing.T, poolID string, memberID string) {
-	res := pools.DeleteMember(base.Client, poolID, memberID)
-	th.AssertNoErr(t, res.Err)
-	t.Logf("Deleted Member, ID %s", memberID)
-}
-
-func listMembers(t *testing.T, poolID string) {
-	err := pools.ListAssociateMembers(base.Client, poolID, pools.MemberListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		membersList, err := pools.ExtractMembers(page)
-		if err != nil {
-			t.Errorf("Failed to extract Members: %v", err)
-			return false, err
-		}
-
-		for _, member := range membersList {
-			t.Logf("Listing Member: ID [%s] Name [%s] Pool ID [%s] Weight [%s]",
-				member.ID, member.Name, member.PoolID, member.Weight)
-		}
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-}
-
-func getMember(t *testing.T, poolID string, memberID string) {
-	member, err := pools.GetAssociateMember(base.Client, poolID, memberID).ExtractMember()
-
-	th.AssertNoErr(t, err)
-
-	t.Logf("Getting Member, ID [%s]: ", member.ID)
-}
-
-func updateMember(t *testing.T, poolID string, memberID string) {
-	_, err := pools.UpdateAssociateMember(base.Client, poolID, memberID, pools.MemberUpdateOpts{Name: "tmp_newName"}).Extract()
-
-	th.AssertNoErr(t, err)
-
-	t.Logf("Updated Member, ID [%s], in Pool, ID [%s]", memberID, poolID)
-}
-
-func createMonitor(t *testing.T, poolID string, checkType string, delay int, timeout int, maxRetries int) string {
-	m, err := monitors.Create(base.Client, monitors.CreateOpts{
-		PoolID:     poolID,
-		Name:       "tmp_monitor",
-		Delay:      delay,
-		Timeout:    timeout,
-		MaxRetries: maxRetries,
-		Type:       checkType,
-	}).Extract()
-
-	th.AssertNoErr(t, err)
-
-	t.Logf("Created Monitor, ID [%s]", m.ID)
-
-	return m.ID
-}
-
-func deleteMonitor(t *testing.T, monitorID string) {
-	res := monitors.Delete(base.Client, monitorID)
-	th.AssertNoErr(t, res.Err)
-	t.Logf("Deleted Monitor, ID %s", monitorID)
-}
-
-func listMonitors(t *testing.T) {
-	err := monitors.List(base.Client, monitors.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		monitorsList, err := monitors.ExtractMonitors(page)
-		if err != nil {
-			t.Errorf("Failed to extract Monitors: %v", err)
-			return false, err
-		}
-
-		for _, monitor := range monitorsList {
-			t.Logf("Listing Monitors: ID [%s] Type [%s] HTTPMethod [%s] URLPath [%s]",
-				monitor.ID, monitor.Type, monitor.HTTPMethod, monitor.URLPath)
-		}
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-}
-
-func getMonitor(t *testing.T, monitorID string) {
-	monitor, err := monitors.Get(base.Client, monitorID).Extract()
-
-	th.AssertNoErr(t, err)
-
-	t.Logf("Getting Monitor, ID [%s]: ", monitor.ID)
-}
-
-func updateMonitor(t *testing.T, monitorID string) {
-	_, err := monitors.Update(base.Client, monitorID, monitors.UpdateOpts{MaxRetries: 10}).Extract()
-
-	th.AssertNoErr(t, err)
-
-	t.Logf("Updated Monitor, ID [%s]", monitorID)
-}
diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go b/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go
new file mode 100644
index 0000000..7cd49ed
--- /dev/null
+++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go
@@ -0,0 +1,178 @@
+// +build acceptance networking lbaas_v2 loadbalancers
+
+package lbaas_v2
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/acceptance/clients"
+	networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
+	"github.com/gophercloud/gophercloud/acceptance/tools"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/loadbalancers"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools"
+)
+
+func TestLoadbalancersList(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
+
+	allPages, err := loadbalancers.List(client, nil).AllPages()
+	if err != nil {
+		t.Fatalf("Unable to list loadbalancers: %v", err)
+	}
+
+	allLoadbalancers, err := loadbalancers.ExtractLoadBalancers(allPages)
+	if err != nil {
+		t.Fatalf("Unable to extract loadbalancers: %v", err)
+	}
+
+	for _, lb := range allLoadbalancers {
+		PrintLoadBalancer(t, &lb)
+	}
+}
+
+func TestLoadbalancersCRUD(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
+
+	network, err := networking.CreateNetwork(t, client)
+	if err != nil {
+		t.Fatalf("Unable to create network: %v", err)
+	}
+	defer networking.DeleteNetwork(t, client, network.ID)
+
+	subnet, err := networking.CreateSubnet(t, client, network.ID)
+	if err != nil {
+		t.Fatalf("Unable to create subnet: %v", err)
+	}
+	defer networking.DeleteSubnet(t, client, subnet.ID)
+
+	lb, err := CreateLoadBalancer(t, client, subnet.ID)
+	if err != nil {
+		t.Fatalf("Unable to create loadbalancer: %v", err)
+	}
+	defer DeleteLoadBalancer(t, client, lb.ID)
+
+	newLB, err := loadbalancers.Get(client, lb.ID).Extract()
+	if err != nil {
+		t.Fatalf("Unable to get loadbalancer: %v", err)
+	}
+
+	PrintLoadBalancer(t, newLB)
+
+	// Because of the time it takes to create a loadbalancer,
+	// this test will include some other resources.
+
+	// Listener
+	listener, err := CreateListener(t, client, lb)
+	if err != nil {
+		t.Fatalf("Unable to create listener: %v", err)
+	}
+	defer DeleteListener(t, client, lb.ID, listener.ID)
+
+	updateListenerOpts := listeners.UpdateOpts{
+		Description: "Some listener description",
+	}
+	_, err = listeners.Update(client, listener.ID, updateListenerOpts).Extract()
+	if err != nil {
+		t.Fatalf("Unable to update listener")
+	}
+
+	if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil {
+		t.Fatalf("Timed out waiting for loadbalancer to become active")
+	}
+
+	newListener, err := listeners.Get(client, listener.ID).Extract()
+	if err != nil {
+		t.Fatalf("Unable to get listener")
+	}
+
+	PrintListener(t, newListener)
+
+	// Pool
+	pool, err := CreatePool(t, client, lb)
+	if err != nil {
+		t.Fatalf("Unable to create pool: %v", err)
+	}
+	defer DeletePool(t, client, lb.ID, pool.ID)
+
+	updatePoolOpts := pools.UpdateOpts{
+		Description: "Some pool description",
+	}
+	_, err = pools.Update(client, pool.ID, updatePoolOpts).Extract()
+	if err != nil {
+		t.Fatalf("Unable to update pool")
+	}
+
+	if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil {
+		t.Fatalf("Timed out waiting for loadbalancer to become active")
+	}
+
+	newPool, err := pools.Get(client, pool.ID).Extract()
+	if err != nil {
+		t.Fatalf("Unable to get pool")
+	}
+
+	PrintPool(t, newPool)
+
+	// Member
+	member, err := CreateMember(t, client, lb, newPool, subnet.ID, subnet.CIDR)
+	if err != nil {
+		t.Fatalf("Unable to create member: %v", err)
+	}
+	defer DeleteMember(t, client, lb.ID, pool.ID, member.ID)
+
+	newWeight := tools.RandomInt(11, 100)
+	updateMemberOpts := pools.UpdateMemberOpts{
+		Weight: newWeight,
+	}
+	_, err = pools.UpdateMember(client, pool.ID, member.ID, updateMemberOpts).Extract()
+	if err != nil {
+		t.Fatalf("Unable to update pool")
+	}
+
+	if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil {
+		t.Fatalf("Timed out waiting for loadbalancer to become active")
+	}
+
+	newMember, err := pools.GetMember(client, pool.ID, member.ID).Extract()
+	if err != nil {
+		t.Fatalf("Unable to get member")
+	}
+
+	PrintMember(t, newMember)
+
+	// Monitor
+	monitor, err := CreateMonitor(t, client, lb, newPool)
+	if err != nil {
+		t.Fatalf("Unable to create monitor: %v", err)
+	}
+	defer DeleteMonitor(t, client, lb.ID, monitor.ID)
+
+	newDelay := tools.RandomInt(20, 30)
+	updateMonitorOpts := monitors.UpdateOpts{
+		Delay: newDelay,
+	}
+	_, err = monitors.Update(client, monitor.ID, updateMonitorOpts).Extract()
+	if err != nil {
+		t.Fatalf("Unable to update monitor")
+	}
+
+	if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil {
+		t.Fatalf("Timed out waiting for loadbalancer to become active")
+	}
+
+	newMonitor, err := monitors.Get(client, monitor.ID).Extract()
+	if err != nil {
+		t.Fatalf("Unable to get monitor")
+	}
+
+	PrintMonitor(t, newMonitor)
+
+}
diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go b/acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go
new file mode 100644
index 0000000..2e587d9
--- /dev/null
+++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go
@@ -0,0 +1,31 @@
+// +build acceptance networking lbaas_v2 monitors
+
+package lbaas_v2
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/acceptance/clients"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors"
+)
+
+func TestMonitorsList(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
+
+	allPages, err := monitors.List(client, nil).AllPages()
+	if err != nil {
+		t.Fatalf("Unable to list monitors: %v", err)
+	}
+
+	allMonitors, err := monitors.ExtractMonitors(allPages)
+	if err != nil {
+		t.Fatalf("Unable to extract monitors: %v", err)
+	}
+
+	for _, monitor := range allMonitors {
+		PrintMonitor(t, &monitor)
+	}
+}
diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go b/acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go
new file mode 100644
index 0000000..42ba5a2
--- /dev/null
+++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go
@@ -0,0 +1,31 @@
+// +build acceptance networking lbaas_v2 pools
+
+package lbaas_v2
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/acceptance/clients"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools"
+)
+
+func TestPoolsList(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
+
+	allPages, err := pools.List(client, nil).AllPages()
+	if err != nil {
+		t.Fatalf("Unable to list pools: %v", err)
+	}
+
+	allPools, err := pools.ExtractPools(allPages)
+	if err != nil {
+		t.Fatalf("Unable to extract pools: %v", err)
+	}
+
+	for _, pool := range allPools {
+		PrintPool(t, &pool)
+	}
+}
diff --git a/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go b/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go
new file mode 100644
index 0000000..b52cd80
--- /dev/null
+++ b/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go
@@ -0,0 +1,57 @@
+package portsbinding
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/acceptance/tools"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsbinding"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
+)
+
+// CreatePortsbinding will create a port on the specified subnet. An error will be
+// returned if the port could not be created.
+func CreatePortsbinding(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID, hostID string) (*portsbinding.Port, error) {
+	portName := tools.RandomString("TESTACC-", 8)
+	iFalse := false
+
+	t.Logf("Attempting to create port: %s", portName)
+
+	createOpts := portsbinding.CreateOpts{
+		CreateOptsBuilder: ports.CreateOpts{
+			NetworkID:    networkID,
+			Name:         portName,
+			AdminStateUp: &iFalse,
+			FixedIPs:     []ports.IP{ports.IP{SubnetID: subnetID}},
+		},
+		HostID: hostID,
+	}
+
+	port, err := portsbinding.Create(client, createOpts).Extract()
+	if err != nil {
+		return port, err
+	}
+
+	t.Logf("Successfully created port: %s", portName)
+
+	return port, nil
+}
+
+// PrintPortsbinging will print a port and all of its attributes.
+func PrintPortsbinding(t *testing.T, port *portsbinding.Port) {
+	t.Logf("ID: %s", port.ID)
+	t.Logf("NetworkID: %s", port.NetworkID)
+	t.Logf("Name: %s", port.Name)
+	t.Logf("AdminStateUp: %t", port.AdminStateUp)
+	t.Logf("Status: %s", port.Status)
+	t.Logf("MACAddress: %s", port.MACAddress)
+	t.Logf("FixedIPs: %s", port.FixedIPs)
+	t.Logf("TenantID: %s", port.TenantID)
+	t.Logf("DeviceOwner: %s", port.DeviceOwner)
+	t.Logf("SecurityGroups: %s", port.SecurityGroups)
+	t.Logf("DeviceID: %s", port.DeviceID)
+	t.Logf("DeviceOwner: %s", port.DeviceOwner)
+	t.Logf("AllowedAddressPairs: %s", port.AllowedAddressPairs)
+	t.Logf("HostID: %s", port.HostID)
+	t.Logf("VNICType: %s", port.VNICType)
+}
diff --git a/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go b/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go
index b703e3b..2f2f618 100644
--- a/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go
+++ b/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go
@@ -1,129 +1,58 @@
-// +build acceptance networking portsbinding
+// +build acceptance networking
 
 package portsbinding
 
 import (
 	"testing"
 
-	base "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
+	"github.com/gophercloud/gophercloud/acceptance/clients"
+	networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
+	"github.com/gophercloud/gophercloud/acceptance/tools"
 	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsbinding"
-	"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
 	"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
-	"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
-	"github.com/gophercloud/gophercloud/pagination"
-	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
-func TestPortBinding(t *testing.T) {
-	base.Setup(t)
-	defer base.Teardown()
+func TestPortsbindingCRUD(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
 
-	// Setup network
-	t.Log("Setting up network")
-	networkID, err := createNetwork()
-	th.AssertNoErr(t, err)
-	defer networks.Delete(base.Client, networkID)
+	// Create Network
+	network, err := networking.CreateNetwork(t, client)
+	if err != nil {
+		t.Fatalf("Unable to create network: %v", err)
+	}
+	defer networking.DeleteNetwork(t, client, network.ID)
 
-	// Setup subnet
-	t.Logf("Setting up subnet on network %s", networkID)
-	subnetID, err := createSubnet(networkID)
-	th.AssertNoErr(t, err)
-	defer subnets.Delete(base.Client, subnetID)
+	// Create Subnet
+	subnet, err := networking.CreateSubnet(t, client, network.ID)
+	if err != nil {
+		t.Fatalf("Unable to create subnet: %v", err)
+	}
+	defer networking.DeleteSubnet(t, client, subnet.ID)
+
+	// Define a host
+	hostID := "localhost"
 
 	// Create port
-	t.Logf("Create port based on subnet %s", subnetID)
-	hostID := "localhost"
-	portID := createPort(t, networkID, subnetID, hostID)
-
-	// Get port
-	if portID == "" {
-		t.Fatalf("In order to retrieve a port, the portID must be set")
+	port, err := CreatePortsbinding(t, client, network.ID, subnet.ID, hostID)
+	if err != nil {
+		t.Fatalf("Unable to create port: %v", err)
 	}
-	p, err := portsbinding.Get(base.Client, portID).Extract()
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, p.ID, portID)
-	th.AssertEquals(t, p.HostID, hostID)
+	defer networking.DeletePort(t, client, port.ID)
+
+	PrintPortsbinding(t, port)
 
 	// Update port
-	newHostID := "openstack"
-	updateOpts := portsbinding.UpdateOpts{
-		HostID: newHostID,
+	newPortName := tools.RandomString("TESTACC-", 8)
+	updateOpts := ports.UpdateOpts{
+		Name: newPortName,
 	}
-	p, err = portsbinding.Update(base.Client, portID, updateOpts).Extract()
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, p.HostID, newHostID)
-
-	// List ports
-	t.Logf("Listing all ports")
-	listPorts(t)
-
-	// Delete port
-	res := ports.Delete(base.Client, portID)
-	th.AssertNoErr(t, res.Err)
-}
-
-func listPorts(t *testing.T) {
-	count := 0
-	pager := ports.List(base.Client, ports.ListOpts{})
-	err := pager.EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		t.Logf("--- Page ---")
-
-		portList, err := portsbinding.ExtractPorts(page)
-		th.AssertNoErr(t, err)
-
-		for _, p := range portList {
-			t.Logf("Port: ID [%s] Name [%s] HostID [%s] VNICType [%s] VIFType [%s]",
-				p.ID, p.Name, p.HostID, p.VNICType, p.VIFType)
-		}
-
-		return true, nil
-	})
-
-	th.CheckNoErr(t, err)
-
-	if count == 0 {
-		t.Logf("No pages were iterated over when listing ports")
-	}
-}
-
-func createPort(t *testing.T, networkID, subnetID, hostID string) string {
-	enable := false
-	opts := portsbinding.CreateOpts{
-		CreateOptsBuilder: ports.CreateOpts{
-			NetworkID:    networkID,
-			Name:         "my_port",
-			AdminStateUp: &enable,
-			FixedIPs:     []ports.IP{{SubnetID: subnetID}},
-		},
-		HostID: hostID,
+	newPort, err := portsbinding.Update(client, port.ID, updateOpts).Extract()
+	if err != nil {
+		t.Fatalf("Could not update port: %v", err)
 	}
 
-	p, err := portsbinding.Create(base.Client, opts).Extract()
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, p.NetworkID, networkID)
-	th.AssertEquals(t, p.Name, "my_port")
-	th.AssertEquals(t, p.AdminStateUp, false)
-
-	return p.ID
-}
-
-func createNetwork() (string, error) {
-	res, err := networks.Create(base.Client, networks.CreateOpts{Name: "tmp_network", AdminStateUp: networks.Up}).Extract()
-	return res.ID, err
-}
-
-func createSubnet(networkID string) (string, error) {
-	s, err := subnets.Create(base.Client, subnets.CreateOpts{
-		NetworkID:  networkID,
-		CIDR:       "192.168.199.0/24",
-		IPVersion:  subnets.IPv4,
-		Name:       "my_subnet",
-		EnableDHCP: subnets.Down,
-		AllocationPools: []subnets.AllocationPool{
-			{Start: "192.168.199.2", End: "192.168.199.200"},
-		},
-	}).Extract()
-	return s.ID, err
+	PrintPortsbinding(t, newPort)
 }
diff --git a/acceptance/openstack/networking/v2/extensions/provider_test.go b/acceptance/openstack/networking/v2/extensions/provider_test.go
index 55acbc9..8136581 100644
--- a/acceptance/openstack/networking/v2/extensions/provider_test.go
+++ b/acceptance/openstack/networking/v2/extensions/provider_test.go
@@ -1,68 +1,34 @@
-// +build acceptance networking
+// +build acceptance networking provider
 
 package extensions
 
 import (
-	"strconv"
 	"testing"
 
-	base "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
+	"github.com/gophercloud/gophercloud/acceptance/clients"
+	networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/provider"
 	"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
-	"github.com/gophercloud/gophercloud/pagination"
-	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
-func TestNetworkCRUDOperations(t *testing.T) {
-	base.Setup(t)
-	defer base.Teardown()
+func TestNetworksProviderCRUD(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
 
 	// Create a network
-	n, err := networks.Create(base.Client, networks.CreateOpts{Name: "sample_network", AdminStateUp: networks.Up}).Extract()
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, n.Name, "sample_network")
-	th.AssertEquals(t, n.AdminStateUp, true)
-	networkID := n.ID
-
-	// List networks
-	pager := networks.List(base.Client, networks.ListOpts{Limit: 2})
-	err = pager.EachPage(func(page pagination.Page) (bool, error) {
-		t.Logf("--- Page ---")
-
-		networkList, err := networks.ExtractNetworks(page)
-		th.AssertNoErr(t, err)
-
-		for _, n := range networkList {
-			t.Logf("Network: ID [%s] Name [%s] Status [%s] Is shared? [%s]",
-				n.ID, n.Name, n.Status, strconv.FormatBool(n.Shared))
-		}
-
-		return true, nil
-	})
-	th.CheckNoErr(t, err)
-
-	// Get a network
-	if networkID == "" {
-		t.Fatalf("In order to retrieve a network, the NetworkID must be set")
+	network, err := networking.CreateNetwork(t, client)
+	if err != nil {
+		t.Fatalf("Unable to create network: %v", err)
 	}
-	n, err = networks.Get(base.Client, networkID).Extract()
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, n.Status, "ACTIVE")
-	th.AssertDeepEquals(t, n.Subnets, []string{})
-	th.AssertEquals(t, n.Name, "sample_network")
-	th.AssertEquals(t, n.AdminStateUp, true)
-	th.AssertEquals(t, n.Shared, false)
-	th.AssertEquals(t, n.ID, networkID)
+	defer networking.DeleteNetwork(t, client, network.ID)
 
-	// Update network
-	n, err = networks.Update(base.Client, networkID, networks.UpdateOpts{Name: "new_network_name"}).Extract()
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, n.Name, "new_network_name")
+	getResult := networks.Get(client, network.ID)
+	newNetwork, err := provider.ExtractGet(getResult)
+	if err != nil {
+		t.Fatalf("Unable to extract network: %v", err)
+	}
 
-	// Delete network
-	res := networks.Delete(base.Client, networkID)
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestCreateMultipleNetworks(t *testing.T) {
-	//networks.CreateMany()
+	PrintNetworkExtAttrs(t, newNetwork)
 }
diff --git a/acceptance/openstack/networking/v2/extensions/security_test.go b/acceptance/openstack/networking/v2/extensions/security_test.go
index fe02ada..88003e7 100644
--- a/acceptance/openstack/networking/v2/extensions/security_test.go
+++ b/acceptance/openstack/networking/v2/extensions/security_test.go
@@ -5,167 +5,89 @@
 import (
 	"testing"
 
-	base "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
+	"github.com/gophercloud/gophercloud/acceptance/clients"
+	networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
 	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups"
-	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules"
-	"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
-	"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
-	"github.com/gophercloud/gophercloud/pagination"
-	th "github.com/gophercloud/gophercloud/testhelper"
 )
 
-func TestSecurityGroups(t *testing.T) {
-	base.Setup(t)
-	defer base.Teardown()
-
-	// create security group
-	groupID := createSecGroup(t)
-
-	// delete security group
-	defer deleteSecGroup(t, groupID)
-
-	// list security group
-	listSecGroups(t)
-
-	// get security group
-	getSecGroup(t, groupID)
-
-	// create port with security group
-	networkID, portID := createPort(t, groupID)
-
-	// teardown
-	defer deleteNetwork(t, networkID)
-
-	// delete port
-	defer deletePort(t, portID)
-}
-
-func TestSecurityGroupRules(t *testing.T) {
-	base.Setup(t)
-	defer base.Teardown()
-
-	// create security group
-	groupID := createSecGroup(t)
-
-	defer deleteSecGroup(t, groupID)
-
-	// create security group rule
-	ruleID := createSecRule(t, groupID)
-
-	// delete security group rule
-	defer deleteSecRule(t, ruleID)
-
-	// list security group rule
-	listSecRules(t)
-
-	// get security group rule
-	getSecRule(t, ruleID)
-}
-
-func createSecGroup(t *testing.T) string {
-	sg, err := groups.Create(base.Client, groups.CreateOpts{
-		Name:        "new-webservers",
-		Description: "security group for webservers",
-	}).Extract()
-
-	th.AssertNoErr(t, err)
-
-	t.Logf("Created security group %s", sg.ID)
-
-	return sg.ID
-}
-
-func listSecGroups(t *testing.T) {
-	err := groups.List(base.Client, groups.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		list, err := groups.ExtractGroups(page)
-		if err != nil {
-			t.Errorf("Failed to extract secgroups: %v", err)
-			return false, err
-		}
-
-		for _, sg := range list {
-			t.Logf("Listing security group: ID [%s] Name [%s]", sg.ID, sg.Name)
-		}
-
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-}
-
-func getSecGroup(t *testing.T, id string) {
-	sg, err := groups.Get(base.Client, id).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Getting security group: ID [%s] Name [%s] Description [%s]", sg.ID, sg.Name, sg.Description)
-}
-
-func createPort(t *testing.T, groupID string) (string, string) {
-	n, err := networks.Create(base.Client, networks.CreateOpts{Name: "tmp_network"}).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Created network %s", n.ID)
-
-	opts := ports.CreateOpts{
-		NetworkID:      n.ID,
-		Name:           "my_port",
-		SecurityGroups: []string{groupID},
+func TestSecurityGroupsList(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
 	}
-	p, err := ports.Create(base.Client, opts).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Created port %s with security group %s", p.ID, groupID)
 
-	return n.ID, p.ID
+	listOpts := groups.ListOpts{}
+	allPages, err := groups.List(client, listOpts).AllPages()
+	if err != nil {
+		t.Fatalf("Unable to list groups: %v", err)
+	}
+
+	allGroups, err := groups.ExtractGroups(allPages)
+	if err != nil {
+		t.Fatalf("Unable to extract groups: %v", err)
+	}
+
+	for _, group := range allGroups {
+		PrintSecurityGroup(t, &group)
+	}
 }
 
-func deleteSecGroup(t *testing.T, groupID string) {
-	res := groups.Delete(base.Client, groupID)
-	th.AssertNoErr(t, res.Err)
-	t.Logf("Deleted security group %s", groupID)
+func TestSecurityGroupsCreateDelete(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
+
+	group, err := CreateSecurityGroup(t, client)
+	if err != nil {
+		t.Fatalf("Unable to create security group: %v", err)
+	}
+	defer DeleteSecurityGroup(t, client, group.ID)
+
+	rule, err := CreateSecurityGroupRule(t, client, group.ID)
+	if err != nil {
+		t.Fatalf("Unable to create security group rule: %v", err)
+	}
+	defer DeleteSecurityGroupRule(t, client, rule.ID)
+
+	PrintSecurityGroup(t, group)
 }
 
-func createSecRule(t *testing.T, groupID string) string {
-	r, err := rules.Create(base.Client, rules.CreateOpts{
-		Direction:    "ingress",
-		PortRangeMin: 80,
-		EtherType:    "IPv4",
-		PortRangeMax: 80,
-		Protocol:     "tcp",
-		SecGroupID:   groupID,
-	}).Extract()
+func TestSecurityGroupsPort(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
 
-	th.AssertNoErr(t, err)
+	network, err := networking.CreateNetwork(t, client)
+	if err != nil {
+		t.Fatalf("Unable to create network: %v", err)
+	}
+	defer networking.DeleteNetwork(t, client, network.ID)
 
-	t.Logf("Created security group rule %s", r.ID)
+	subnet, err := networking.CreateSubnet(t, client, network.ID)
+	if err != nil {
+		t.Fatalf("Unable to create subnet: %v", err)
+	}
+	defer networking.DeleteSubnet(t, client, subnet.ID)
 
-	return r.ID
-}
+	group, err := CreateSecurityGroup(t, client)
+	if err != nil {
+		t.Fatalf("Unable to create security group: %v", err)
+	}
+	defer DeleteSecurityGroup(t, client, group.ID)
 
-func listSecRules(t *testing.T) {
-	err := rules.List(base.Client, rules.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
-		list, err := rules.ExtractRules(page)
-		if err != nil {
-			t.Errorf("Failed to extract sec rules: %v", err)
-			return false, err
-		}
+	rule, err := CreateSecurityGroupRule(t, client, group.ID)
+	if err != nil {
+		t.Fatalf("Unable to create security group rule: %v", err)
+	}
+	defer DeleteSecurityGroupRule(t, client, rule.ID)
 
-		for _, r := range list {
-			t.Logf("Listing security rule: ID [%s]", r.ID)
-		}
+	port, err := CreatePortWithSecurityGroup(t, client, network.ID, subnet.ID, group.ID)
+	if err != nil {
+		t.Fatalf("Unable to create port: %v", err)
+	}
+	defer networking.DeletePort(t, client, port.ID)
 
-		return true, nil
-	})
-
-	th.AssertNoErr(t, err)
-}
-
-func getSecRule(t *testing.T, id string) {
-	r, err := rules.Get(base.Client, id).Extract()
-	th.AssertNoErr(t, err)
-	t.Logf("Getting security rule: ID [%s] Direction [%s] EtherType [%s] Protocol [%s]",
-		r.ID, r.Direction, r.EtherType, r.Protocol)
-}
-
-func deleteSecRule(t *testing.T, id string) {
-	res := rules.Delete(base.Client, id)
-	th.AssertNoErr(t, res.Err)
-	t.Logf("Deleted security rule %s", id)
+	networking.PrintPort(t, port)
 }
diff --git a/acceptance/openstack/networking/v2/network_test.go b/acceptance/openstack/networking/v2/network_test.go
deleted file mode 100644
index 1926999..0000000
--- a/acceptance/openstack/networking/v2/network_test.go
+++ /dev/null
@@ -1,68 +0,0 @@
-// +build acceptance networking
-
-package v2
-
-import (
-	"strconv"
-	"testing"
-
-	"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
-	"github.com/gophercloud/gophercloud/pagination"
-	th "github.com/gophercloud/gophercloud/testhelper"
-)
-
-func TestNetworkCRUDOperations(t *testing.T) {
-	Setup(t)
-	defer Teardown()
-
-	// Create a network
-	n, err := networks.Create(Client, networks.CreateOpts{Name: "sample_network", AdminStateUp: networks.Up}).Extract()
-	th.AssertNoErr(t, err)
-	defer networks.Delete(Client, n.ID)
-	th.AssertEquals(t, n.Name, "sample_network")
-	th.AssertEquals(t, n.AdminStateUp, true)
-	networkID := n.ID
-
-	// List networks
-	pager := networks.List(Client, networks.ListOpts{Limit: 2})
-	err = pager.EachPage(func(page pagination.Page) (bool, error) {
-		t.Logf("--- Page ---")
-
-		networkList, err := networks.ExtractNetworks(page)
-		th.AssertNoErr(t, err)
-
-		for _, n := range networkList {
-			t.Logf("Network: ID [%s] Name [%s] Status [%s] Is shared? [%s]",
-				n.ID, n.Name, n.Status, strconv.FormatBool(n.Shared))
-		}
-
-		return true, nil
-	})
-	th.CheckNoErr(t, err)
-
-	// Get a network
-	if networkID == "" {
-		t.Fatalf("In order to retrieve a network, the NetworkID must be set")
-	}
-	n, err = networks.Get(Client, networkID).Extract()
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, n.Status, "ACTIVE")
-	th.AssertDeepEquals(t, n.Subnets, []string{})
-	th.AssertEquals(t, n.Name, "sample_network")
-	th.AssertEquals(t, n.AdminStateUp, true)
-	th.AssertEquals(t, n.Shared, false)
-	th.AssertEquals(t, n.ID, networkID)
-
-	// Update network
-	n, err = networks.Update(Client, networkID, networks.UpdateOpts{Name: "new_network_name"}).Extract()
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, n.Name, "new_network_name")
-
-	// Delete network
-	res := networks.Delete(Client, networkID)
-	th.AssertNoErr(t, res.Err)
-}
-
-func TestCreateMultipleNetworks(t *testing.T) {
-	//networks.CreateMany()
-}
diff --git a/acceptance/openstack/networking/v2/networking.go b/acceptance/openstack/networking/v2/networking.go
new file mode 100644
index 0000000..86c3545
--- /dev/null
+++ b/acceptance/openstack/networking/v2/networking.go
@@ -0,0 +1,182 @@
+package v2
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/acceptance/tools"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/apiversions"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
+)
+
+// CreateNetwork will create basic network. An error will be returned if the
+// network could not be created.
+func CreateNetwork(t *testing.T, client *gophercloud.ServiceClient) (*networks.Network, error) {
+	networkName := tools.RandomString("TESTACC-", 8)
+	createOpts := networks.CreateOpts{
+		Name:         networkName,
+		AdminStateUp: gophercloud.Enabled,
+	}
+
+	t.Logf("Attempting to create network: %s", networkName)
+
+	network, err := networks.Create(client, createOpts).Extract()
+	if err != nil {
+		return network, err
+	}
+
+	t.Logf("Successfully created network.")
+	return network, nil
+}
+
+// CreatePort will create a port on the specified subnet. An error will be
+// returned if the port could not be created.
+func CreatePort(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID string) (*ports.Port, error) {
+	portName := tools.RandomString("TESTACC-", 8)
+
+	t.Logf("Attempting to create port: %s", portName)
+
+	createOpts := ports.CreateOpts{
+		NetworkID:    networkID,
+		Name:         portName,
+		AdminStateUp: gophercloud.Disabled,
+		FixedIPs:     []ports.IP{ports.IP{SubnetID: subnetID}},
+	}
+
+	port, err := ports.Create(client, createOpts).Extract()
+	if err != nil {
+		return port, err
+	}
+
+	t.Logf("Successfully created port: %s", portName)
+
+	return port, nil
+}
+
+// CreateSubnet will create a subnet on the specified Network ID. An error
+// will be returned if the subnet could not be created.
+func CreateSubnet(t *testing.T, client *gophercloud.ServiceClient, networkID string) (*subnets.Subnet, error) {
+	subnetName := tools.RandomString("TESTACC-", 8)
+	subnetOctet := tools.RandomInt(1, 250)
+	subnetCIDR := fmt.Sprintf("192.168.%d.0/24", subnetOctet)
+	subnetGateway := fmt.Sprintf("192.168.%d.1", subnetOctet)
+	iFalse := false
+	createOpts := subnets.CreateOpts{
+		NetworkID:  networkID,
+		CIDR:       subnetCIDR,
+		IPVersion:  4,
+		Name:       subnetName,
+		EnableDHCP: &iFalse,
+		GatewayIP:  &subnetGateway,
+	}
+
+	t.Logf("Attempting to create subnet: %s", subnetName)
+
+	subnet, err := subnets.Create(client, createOpts).Extract()
+	if err != nil {
+		return subnet, err
+	}
+
+	t.Logf("Successfully created subnet.")
+	return subnet, nil
+}
+
+// DeleteNetwork will delete a network with a specified ID. A fatal error will
+// occur if the delete was not successful. This works best when used as a
+// deferred function.
+func DeleteNetwork(t *testing.T, client *gophercloud.ServiceClient, networkID string) {
+	t.Logf("Attempting to delete network: %s", networkID)
+
+	err := networks.Delete(client, networkID).ExtractErr()
+	if err != nil {
+		t.Fatalf("Unable to delete network %s: %v", networkID, err)
+	}
+
+	t.Logf("Deleted network: %s", networkID)
+}
+
+// DeletePort will delete a port with a specified ID. A fatal error will
+// occur if the delete was not successful. This works best when used as a
+// deferred function.
+func DeletePort(t *testing.T, client *gophercloud.ServiceClient, portID string) {
+	t.Logf("Attempting to delete port: %s", portID)
+
+	err := ports.Delete(client, portID).ExtractErr()
+	if err != nil {
+		t.Fatalf("Unable to delete port %s: %v", portID, err)
+	}
+
+	t.Logf("Deleted port: %s", portID)
+}
+
+// DeleteSubnet will delete a subnet with a specified ID. A fatal error will
+// occur if the delete was not successful. This works best when used as a
+// deferred function.
+func DeleteSubnet(t *testing.T, client *gophercloud.ServiceClient, subnetID string) {
+	t.Logf("Attempting to delete subnet: %s", subnetID)
+
+	err := subnets.Delete(client, subnetID).ExtractErr()
+	if err != nil {
+		t.Fatalf("Unable to delete subnet %s: %v", subnetID, err)
+	}
+
+	t.Logf("Deleted subnet: %s", subnetID)
+}
+
+// PrintAPIVersion will print an API version and all of its attributes.
+func PrintAPIVersion(t *testing.T, apiVersion *apiversions.APIVersion) {
+	t.Logf("ID: %s", apiVersion.ID)
+	t.Logf("Status: %s", apiVersion.Status)
+}
+
+// PrintNetwork will print a network and all of its attributes.
+func PrintNetwork(t *testing.T, network *networks.Network) {
+	t.Logf("ID: %s", network.ID)
+	t.Logf("Name: %s", network.Name)
+	t.Logf("AdminStateUp: %t", network.AdminStateUp)
+	t.Logf("Status: %s", network.Status)
+	t.Logf("TenantID: %s", network.TenantID)
+	t.Logf("Shared: %t", network.Shared)
+	t.Logf("Subnets: %s", network.Subnets)
+}
+
+// PrintPort will print a port and all of its attributes.
+func PrintPort(t *testing.T, port *ports.Port) {
+	t.Logf("ID: %s", port.ID)
+	t.Logf("NetworkID: %s", port.NetworkID)
+	t.Logf("Name: %s", port.Name)
+	t.Logf("AdminStateUp: %t", port.AdminStateUp)
+	t.Logf("Status: %s", port.Status)
+	t.Logf("MACAddress: %s", port.MACAddress)
+	t.Logf("FixedIPs: %s", port.FixedIPs)
+	t.Logf("TenantID: %s", port.TenantID)
+	t.Logf("DeviceOwner: %s", port.DeviceOwner)
+	t.Logf("SecurityGroups: %s", port.SecurityGroups)
+	t.Logf("DeviceID: %s", port.DeviceID)
+	t.Logf("DeviceOwner: %s", port.DeviceOwner)
+	t.Logf("AllowedAddressPairs: %s", port.AllowedAddressPairs)
+}
+
+// PrintSubnet will print a subnet and all of its attributes.
+func PrintSubnet(t *testing.T, subnet *subnets.Subnet) {
+	t.Logf("ID: %s", subnet.ID)
+	t.Logf("NetworkID: %s", subnet.NetworkID)
+	t.Logf("Name: %s", subnet.Name)
+	t.Logf("IPVersion: %d", subnet.IPVersion)
+	t.Logf("CIDR: %s", subnet.CIDR)
+	t.Logf("GatewayIP: %s", subnet.GatewayIP)
+	t.Logf("DNSNameservers: %s", subnet.DNSNameservers)
+	t.Logf("AllocationPools: %s", subnet.AllocationPools)
+	t.Logf("HostRoutes: %s", subnet.HostRoutes)
+	t.Logf("EnableDHCP: %t", subnet.EnableDHCP)
+	t.Logf("TenantID: %s", subnet.TenantID)
+}
+
+// PrintVersionResource will print an API version resource and all of its attributes.
+func PrintVersionResource(t *testing.T, versionResource *apiversions.APIVersionResource) {
+	t.Logf("Name: %s", versionResource.Name)
+	t.Logf("Collection: %s", versionResource.Collection)
+}
diff --git a/acceptance/openstack/networking/v2/networks_test.go b/acceptance/openstack/networking/v2/networks_test.go
new file mode 100644
index 0000000..a095079
--- /dev/null
+++ b/acceptance/openstack/networking/v2/networks_test.go
@@ -0,0 +1,65 @@
+// +build acceptance networking
+
+package v2
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/acceptance/clients"
+	"github.com/gophercloud/gophercloud/acceptance/tools"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
+)
+
+func TestNetworksList(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
+
+	allPages, err := networks.List(client, nil).AllPages()
+	if err != nil {
+		t.Fatalf("Unable to list networks: %v", err)
+	}
+
+	allNetworks, err := networks.ExtractNetworks(allPages)
+	if err != nil {
+		t.Fatalf("Unable to extract networks: %v", err)
+	}
+
+	for _, network := range allNetworks {
+		PrintNetwork(t, &network)
+	}
+}
+
+func TestNetworksCRUD(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
+
+	// Create a network
+	network, err := CreateNetwork(t, client)
+	if err != nil {
+		t.Fatalf("Unable to create network: %v", err)
+	}
+	defer DeleteNetwork(t, client, network.ID)
+
+	PrintNetwork(t, network)
+
+	newName := tools.RandomString("TESTACC-", 8)
+	updateOpts := &networks.UpdateOpts{
+		Name: newName,
+	}
+
+	_, err = networks.Update(client, network.ID, updateOpts).Extract()
+	if err != nil {
+		t.Fatalf("Unable to update network: %v", err)
+	}
+
+	newNetwork, err := networks.Get(client, network.ID).Extract()
+	if err != nil {
+		t.Fatalf("Unable to retrieve network: %v", err)
+	}
+
+	PrintNetwork(t, newNetwork)
+}
diff --git a/acceptance/openstack/networking/v2/port_test.go b/acceptance/openstack/networking/v2/port_test.go
deleted file mode 100644
index 2ef3408..0000000
--- a/acceptance/openstack/networking/v2/port_test.go
+++ /dev/null
@@ -1,130 +0,0 @@
-// +build acceptance networking
-
-package v2
-
-import (
-	"testing"
-
-	"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
-	"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
-	"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
-	"github.com/gophercloud/gophercloud/pagination"
-	th "github.com/gophercloud/gophercloud/testhelper"
-)
-
-func TestPortCRUD(t *testing.T) {
-	Setup(t)
-	defer Teardown()
-
-	// Setup network
-	t.Log("Setting up network")
-	networkID, err := createNetwork()
-	th.AssertNoErr(t, err)
-	defer networks.Delete(Client, networkID)
-
-	// Setup subnet
-	t.Logf("Setting up subnet on network %s", networkID)
-	subnetID, err := createSubnet(networkID)
-	th.AssertNoErr(t, err)
-	defer subnets.Delete(Client, subnetID)
-
-	// Create port
-	t.Logf("Create port based on subnet %s", subnetID)
-	portID := createPort(t, networkID, subnetID)
-
-	// List ports
-	t.Logf("Listing all ports")
-	listPorts(t)
-
-	// Get port
-	if portID == "" {
-		t.Fatalf("In order to retrieve a port, the portID must be set")
-	}
-	p, err := ports.Get(Client, portID).Extract()
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, p.ID, portID)
-
-	// Update port
-	updateOpts := ports.UpdateOpts{
-		Name: "new_port_name",
-		AllowedAddressPairs: []ports.AddressPair{
-			ports.AddressPair{IPAddress: "192.168.199.201"},
-		},
-	}
-	p, err = ports.Update(Client, portID, updateOpts).Extract()
-
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, p.Name, "new_port_name")
-
-	updatedPort, err := ports.Get(Client, portID).Extract()
-	th.AssertEquals(t, updatedPort.AllowedAddressPairs[0].IPAddress, "192.168.199.201")
-
-	// Delete port
-	res := ports.Delete(Client, portID)
-	th.AssertNoErr(t, res.Err)
-}
-
-func createPort(t *testing.T, networkID, subnetID string) string {
-	enable := false
-	opts := ports.CreateOpts{
-		NetworkID:    networkID,
-		Name:         "my_port",
-		AdminStateUp: &enable,
-		FixedIPs:     []ports.IP{ports.IP{SubnetID: subnetID}},
-	}
-	p, err := ports.Create(Client, opts).Extract()
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, p.NetworkID, networkID)
-	th.AssertEquals(t, p.Name, "my_port")
-	th.AssertEquals(t, p.AdminStateUp, false)
-
-	return p.ID
-}
-
-func listPorts(t *testing.T) {
-	count := 0
-	pager := ports.List(Client, ports.ListOpts{})
-	err := pager.EachPage(func(page pagination.Page) (bool, error) {
-		count++
-		t.Logf("--- Page ---")
-
-		portList, err := ports.ExtractPorts(page)
-		th.AssertNoErr(t, err)
-
-		for _, p := range portList {
-			t.Logf("Port: ID [%s] Name [%s] Status [%s] MAC addr [%s] Fixed IPs [%#v] Security groups [%#v] Allowed Address Pairs [%#v]",
-				p.ID, p.Name, p.Status, p.MACAddress, p.FixedIPs, p.SecurityGroups, p.AllowedAddressPairs)
-		}
-
-		return true, nil
-	})
-
-	th.CheckNoErr(t, err)
-
-	if count == 0 {
-		t.Logf("No pages were iterated over when listing ports")
-	}
-}
-
-func createNetwork() (string, error) {
-	res, err := networks.Create(Client, networks.CreateOpts{Name: "tmp_network", AdminStateUp: networks.Up}).Extract()
-	return res.ID, err
-}
-
-func createSubnet(networkID string) (string, error) {
-	s, err := subnets.Create(Client, subnets.CreateOpts{
-		NetworkID:  networkID,
-		CIDR:       "192.168.199.0/24",
-		IPVersion:  subnets.IPv4,
-		Name:       "my_subnet",
-		EnableDHCP: subnets.Down,
-		AllocationPools: []subnets.AllocationPool{
-			subnets.AllocationPool{Start: "192.168.199.2", End: "192.168.199.200"},
-		},
-	}).Extract()
-	return s.ID, err
-}
-
-func TestPortBatchCreate(t *testing.T) {
-	// todo
-}
diff --git a/acceptance/openstack/networking/v2/ports_test.go b/acceptance/openstack/networking/v2/ports_test.go
new file mode 100644
index 0000000..b3b9ecb
--- /dev/null
+++ b/acceptance/openstack/networking/v2/ports_test.go
@@ -0,0 +1,74 @@
+// +build acceptance networking
+
+package v2
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/acceptance/clients"
+	"github.com/gophercloud/gophercloud/acceptance/tools"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
+)
+
+func TestPortsList(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
+
+	allPages, err := ports.List(client, nil).AllPages()
+	if err != nil {
+		t.Fatalf("Unable to list ports: %v", err)
+	}
+
+	allPorts, err := ports.ExtractPorts(allPages)
+	if err != nil {
+		t.Fatalf("Unable to extract ports: %v", err)
+	}
+
+	for _, port := range allPorts {
+		PrintPort(t, &port)
+	}
+}
+
+func TestPortsCRUD(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
+
+	// Create Network
+	network, err := CreateNetwork(t, client)
+	if err != nil {
+		t.Fatalf("Unable to create network: %v", err)
+	}
+	defer DeleteNetwork(t, client, network.ID)
+
+	// Create Subnet
+	subnet, err := CreateSubnet(t, client, network.ID)
+	if err != nil {
+		t.Fatalf("Unable to create subnet: %v", err)
+	}
+	defer DeleteSubnet(t, client, subnet.ID)
+
+	// Create port
+	port, err := CreatePort(t, client, network.ID, subnet.ID)
+	if err != nil {
+		t.Fatalf("Unable to create port: %v", err)
+	}
+	defer DeletePort(t, client, port.ID)
+
+	PrintPort(t, port)
+
+	// Update port
+	newPortName := tools.RandomString("TESTACC-", 8)
+	updateOpts := ports.UpdateOpts{
+		Name: newPortName,
+	}
+	newPort, err := ports.Update(client, port.ID, updateOpts).Extract()
+	if err != nil {
+		t.Fatalf("Could not update port: %v", err)
+	}
+
+	PrintPort(t, newPort)
+}
diff --git a/acceptance/openstack/networking/v2/subnet_test.go b/acceptance/openstack/networking/v2/subnet_test.go
deleted file mode 100644
index c9efd5c..0000000
--- a/acceptance/openstack/networking/v2/subnet_test.go
+++ /dev/null
@@ -1,141 +0,0 @@
-// +build acceptance networking
-
-package v2
-
-import (
-	"testing"
-
-	"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
-	"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
-	"github.com/gophercloud/gophercloud/pagination"
-	th "github.com/gophercloud/gophercloud/testhelper"
-)
-
-func TestSubnetList(t *testing.T) {
-	Setup(t)
-	defer Teardown()
-
-	pager := subnets.List(Client, subnets.ListOpts{Limit: 2})
-	err := pager.EachPage(func(page pagination.Page) (bool, error) {
-		t.Logf("--- Page ---")
-
-		subnetList, err := subnets.ExtractSubnets(page)
-		th.AssertNoErr(t, err)
-
-		for _, s := range subnetList {
-			t.Logf("Subnet: ID [%s] Name [%s] IP Version [%d] CIDR [%s] GatewayIP [%s]",
-				s.ID, s.Name, s.IPVersion, s.CIDR, s.GatewayIP)
-		}
-
-		return true, nil
-	})
-	th.CheckNoErr(t, err)
-}
-
-func TestSubnetCRUD(t *testing.T) {
-	Setup(t)
-	defer Teardown()
-
-	// Setup network
-	t.Log("Setting up network")
-	n, err := networks.Create(Client, networks.CreateOpts{Name: "tmp_network", AdminStateUp: networks.Up}).Extract()
-	th.AssertNoErr(t, err)
-	networkID := n.ID
-	defer networks.Delete(Client, networkID)
-
-	// Create subnet
-	t.Log("Create subnet")
-	enable := false
-	opts := subnets.CreateOpts{
-		NetworkID:  networkID,
-		CIDR:       "192.168.199.0/24",
-		IPVersion:  subnets.IPv4,
-		Name:       "my_subnet",
-		EnableDHCP: &enable,
-	}
-	s, err := subnets.Create(Client, opts).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, s.NetworkID, networkID)
-	th.AssertEquals(t, s.CIDR, "192.168.199.0/24")
-	th.AssertEquals(t, s.IPVersion, 4)
-	th.AssertEquals(t, s.Name, "my_subnet")
-	th.AssertEquals(t, s.EnableDHCP, false)
-	th.AssertEquals(t, s.GatewayIP, "192.168.199.1")
-	subnetID := s.ID
-
-	// Get subnet
-	t.Log("Getting subnet")
-	s, err = subnets.Get(Client, subnetID).Extract()
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, s.ID, subnetID)
-
-	// Update subnet
-	t.Log("Update subnet")
-	s, err = subnets.Update(Client, subnetID, subnets.UpdateOpts{Name: "new_subnet_name"}).Extract()
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, s.Name, "new_subnet_name")
-
-	// Delete subnet
-	t.Log("Delete subnet")
-	res := subnets.Delete(Client, subnetID)
-	th.AssertNoErr(t, res.Err)
-
-	// Create subnet with no gateway
-	t.Log("Create subnet with no gateway")
-	opts = subnets.CreateOpts{
-		NetworkID:  networkID,
-		CIDR:       "192.168.199.0/24",
-		IPVersion:  subnets.IPv4,
-		Name:       "my_subnet",
-		EnableDHCP: &enable,
-		NoGateway:  true,
-	}
-	s, err = subnets.Create(Client, opts).Extract()
-	th.AssertNoErr(t, err)
-
-	th.AssertEquals(t, s.NetworkID, networkID)
-	th.AssertEquals(t, s.CIDR, "192.168.199.0/24")
-	th.AssertEquals(t, s.IPVersion, 4)
-	th.AssertEquals(t, s.Name, "my_subnet")
-	th.AssertEquals(t, s.EnableDHCP, false)
-	th.AssertEquals(t, s.GatewayIP, "")
-	subnetID = s.ID
-
-	// Get subnet
-	t.Log("Getting subnet with no gateway")
-	s, err = subnets.Get(Client, subnetID).Extract()
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, s.ID, subnetID)
-
-	// Update subnet
-	t.Log("Update subnet with no gateway")
-	s, err = subnets.Update(Client, subnetID, subnets.UpdateOpts{Name: "new_subnet_name"}).Extract()
-	th.AssertNoErr(t, err)
-	th.AssertEquals(t, s.Name, "new_subnet_name")
-
-	// Delete subnet
-	t.Log("Delete subnet with no gateway")
-	res = subnets.Delete(Client, subnetID)
-	th.AssertNoErr(t, res.Err)
-
-	// Create subnet with invalid gateway configuration
-	t.Log("Create subnet with invalid gateway configuration")
-	opts = subnets.CreateOpts{
-		NetworkID:  networkID,
-		CIDR:       "192.168.199.0/24",
-		IPVersion:  subnets.IPv4,
-		Name:       "my_subnet",
-		EnableDHCP: &enable,
-		NoGateway:  true,
-		GatewayIP:  "192.168.199.1",
-	}
-	_, err = subnets.Create(Client, opts).Extract()
-	if err == nil {
-		t.Fatalf("Expected an error, got none")
-	}
-}
-
-func TestBatchCreate(t *testing.T) {
-	// todo
-}
diff --git a/acceptance/openstack/networking/v2/subnets_test.go b/acceptance/openstack/networking/v2/subnets_test.go
new file mode 100644
index 0000000..49c970a
--- /dev/null
+++ b/acceptance/openstack/networking/v2/subnets_test.go
@@ -0,0 +1,73 @@
+// +build acceptance networking
+
+package v2
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/acceptance/clients"
+	"github.com/gophercloud/gophercloud/acceptance/tools"
+	"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
+)
+
+func TestSubnetsList(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
+
+	allPages, err := subnets.List(client, nil).AllPages()
+	if err != nil {
+		t.Fatalf("Unable to list subnets: %v", err)
+	}
+
+	allSubnets, err := subnets.ExtractSubnets(allPages)
+	if err != nil {
+		t.Fatalf("Unable to extract subnets: %v", err)
+	}
+
+	for _, subnet := range allSubnets {
+		PrintSubnet(t, &subnet)
+	}
+}
+
+func TestSubnetCRUD(t *testing.T) {
+	client, err := clients.NewNetworkV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create a network client: %v", err)
+	}
+
+	// Create Network
+	network, err := CreateNetwork(t, client)
+	if err != nil {
+		t.Fatalf("Unable to create network: %v", err)
+	}
+	defer DeleteNetwork(t, client, network.ID)
+
+	// Create Subnet
+	subnet, err := CreateSubnet(t, client, network.ID)
+	if err != nil {
+		t.Fatalf("Unable to create subnet: %v", err)
+	}
+	defer DeleteSubnet(t, client, subnet.ID)
+
+	PrintSubnet(t, subnet)
+
+	// Update Subnet
+	newSubnetName := tools.RandomString("TESTACC-", 8)
+	updateOpts := subnets.UpdateOpts{
+		Name: newSubnetName,
+	}
+	_, err = subnets.Update(client, subnet.ID, updateOpts).Extract()
+	if err != nil {
+		t.Fatalf("Unable to update subnet: %v", err)
+	}
+
+	// Get subnet
+	newSubnet, err := subnets.Get(client, subnet.ID).Extract()
+	if err != nil {
+		t.Fatalf("Unable to get subnet: %v", err)
+	}
+
+	PrintSubnet(t, newSubnet)
+}
diff --git a/script/acceptancetest_environments/keystonev3-lbaasv2.sh b/script/acceptancetest_environments/keystonev3-lbaasv2.sh
new file mode 100644
index 0000000..69efca9
--- /dev/null
+++ b/script/acceptancetest_environments/keystonev3-lbaasv2.sh
@@ -0,0 +1,204 @@
+#!/bin/bash
+#
+# This script is useful for creating a devstack environment to run gophercloud
+# acceptance tests on.
+#
+# To run, simply execute this script within a virtual machine.
+#
+# The following OpenStack versions are installed:
+# * OpenStack Mitaka
+# * Keystone v3
+# * Glance v1 and v2
+# * Nova v2 and v2.1
+# * Cinder v1 and v2
+# * Trove v1
+# * Swift v1
+# * Neutron v2
+# * Neutron LBaaS v2.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
+
+export PATH=$PATH:$HOME/terraform:$HOME/go/bin
+echo 'export PATH=$PATH:$HOME/terraform:$HOME/go/bin' >> .bashrc
+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_plugin octavia https://git.openstack.org/openstack/octavia
+enable_service q-lbaasv2
+enable_service octavia
+enable_service o-cw
+enable_service o-hm
+enable_service o-hk
+enable_service o-api
+
+# Octavia
+OCTAVIA_AUTH_VERSION=3
+
+# 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
+
+[[post-config|\$OCTAVIA_CONF]]
+[default]
+debug = true
+verbose = true
+[keystone_authtoken]
+auth_uri = http://localhost:5000/v3
+auth_url = http://localhost:35357/v3
+user_domain_id = default
+project_name = default
+auth_type = password
+[keystone_authtoken_v3]
+admin_user_domain = default
+admin_project_domain = default
+[[post-config|\$NEUTRON_CONF]]
+[service_auth]
+auth_version = 3
+auth_uri = http://localhost:5000/v3
+auth_url = http://localhost:35357/v3
+admin_user_domain = default
+admin_project_domain = default
+[[post-config|\$NEUTRON_LBAAS_CONF]]
+[service_auth]
+auth_version = 3
+auth_uri = http://localhost:5000/v3
+auth_url = http://localhost:35357/v3
+admin_user_domain = default
+admin_project_domain = default
+EOF
+./stack.sh
+
+# Patch openrc
+cat >> openrc <<EOF
+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