Adding more documentation and tweaking operations
diff --git a/openstack/networking/v2/extensions/layer3/doc.go b/openstack/networking/v2/extensions/layer3/doc.go
new file mode 100644
index 0000000..d533458
--- /dev/null
+++ b/openstack/networking/v2/extensions/layer3/doc.go
@@ -0,0 +1,5 @@
+// Package layer3 provides access to the Layer-3 networking extension for the
+// OpenStack Neutron service. This extension allows API users to route packets
+// between subnets, forward packets from internal networks to external ones,
+// and access instances from external networks through floating IPs.
+package layer3
diff --git a/openstack/networking/v2/extensions/layer3/floatingips/doc.go b/openstack/networking/v2/extensions/layer3/floatingips/doc.go
deleted file mode 100644
index 6648963..0000000
--- a/openstack/networking/v2/extensions/layer3/floatingips/doc.go
+++ /dev/null
@@ -1 +0,0 @@
-package floatingips
diff --git a/openstack/networking/v2/extensions/layer3/floatingips/requests.go b/openstack/networking/v2/extensions/layer3/floatingips/requests.go
index 151591a..3ec3899 100644
--- a/openstack/networking/v2/extensions/layer3/floatingips/requests.go
+++ b/openstack/networking/v2/extensions/layer3/floatingips/requests.go
@@ -2,13 +2,77 @@
 
 import (
 	"fmt"
+	"strconv"
 
 	"github.com/racker/perigee"
 	"github.com/rackspace/gophercloud"
+	"github.com/rackspace/gophercloud/openstack/utils"
+	"github.com/rackspace/gophercloud/pagination"
 )
 
+// ListOpts allows the filtering and sorting of paginated collections through
+// the API. Filtering is achieved by passing in struct field values that map to
+// the floating IP attributes you want to see returned. SortKey allows you to
+// sort by a particular network attribute. SortDir sets the direction, and is
+// either `asc' or `desc'. Marker and Limit are used for pagination.
+type ListOpts struct {
+	ID                string
+	FloatingNetworkID string
+	PortID            string
+	FixedIP           string
+	FloatingIP        string
+	TenantID          string
+	Limit             int
+	Marker            string
+	SortKey           string
+	SortDir           string
+}
+
+// List returns a Pager which allows you to iterate over a collection of
+// floating IP resources. It accepts a ListOpts struct, which allows you to
+// filter and sort the returned collection for greater efficiency.
+func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager {
+	q := make(map[string]string)
+	if opts.ID != "" {
+		q["id"] = opts.ID
+	}
+	if opts.FloatingNetworkID != "" {
+		q["floating_network_id"] = opts.FloatingNetworkID
+	}
+	if opts.FixedIP != "" {
+		q["fixed_ip_address"] = opts.FixedIP
+	}
+	if opts.FloatingIP != "" {
+		q["floating_ip_address"] = opts.FloatingIP
+	}
+	if opts.TenantID != "" {
+		q["tenant_id"] = opts.TenantID
+	}
+	if opts.Marker != "" {
+		q["marker"] = opts.Marker
+	}
+	if opts.Limit != 0 {
+		q["limit"] = strconv.Itoa(opts.Limit)
+	}
+	if opts.SortKey != "" {
+		q["sort_key"] = opts.SortKey
+	}
+	if opts.SortDir != "" {
+		q["sort_dir"] = opts.SortDir
+	}
+
+	u := rootURL(c) + utils.BuildQuery(q)
+	return pagination.NewPager(c, u, func(r pagination.LastHTTPResponse) pagination.Page {
+		return FloatingIPPage{pagination.LinkedPageBase(r)}
+	})
+}
+
+// CreateOpts contains all the values needed to create a new floating IP
+// resource. The only required fields are FloatingNetworkID and PortID which
+// refer to the external network and internal port respectively.
 type CreateOpts struct {
 	FloatingNetworkID string
+	FloatingIP        string
 	PortID            string
 	FixedIP           string
 	TenantID          string
@@ -19,6 +83,30 @@
 	errPortIDRequired            = fmt.Errorf("A PortID is required")
 )
 
+// Create accepts a CreateOpts struct and uses the values provided to create a
+// new floating IP resource. You can create floating IPs on external networks
+// only. If you provide a FloatingNetworkID which refers to a network that is
+// not external (i.e. its `router:external' attribute is False), the operation
+// will fail and return a 400 error.
+//
+// If you do not specify a FloatingIP address value, the operation will
+// automatically allocate an available address for the new resource. If you do
+// choose to specify one, it must fall within the subnet range for the external
+// network - otherwise the operation returns a 400 error. If the FloatingIP
+// address is already in use, the operation returns a 409 error code.
+//
+// You can associate the new resource with an internal port by using the PortID
+// field. If you specify a PortID that is not valid, the operation will fail and
+// return 404 error code.
+//
+// You must also configure an IP address for the port associated with the PortID
+// you have provided - this is what the FixedIP refers to: an IP fixed to a port.
+// Because a port might be associated with multiple IP addresses, you can use
+// the FixedIP field to associate a particular IP address rather than have the
+// API assume for you. If you specify an IP address that is not valid, the
+// operation will fail and return a 400 error code. If the PortID and FixedIP
+// are already associated with another resource, the operation will fail and
+// returns a 409 error code.
 func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
 	var res CreateResult
 
@@ -35,6 +123,7 @@
 	// Define structures
 	type floatingIP struct {
 		FloatingNetworkID string `json:"floating_network_id"`
+		FloatingIP        string `json:"floating_ip_address,omitempty"`
 		PortID            string `json:"port_id"`
 		FixedIP           string `json:"fixed_ip_address,omitempty"`
 		TenantID          string `json:"tenant_id,omitempty"`
@@ -64,6 +153,7 @@
 	return res
 }
 
+// Get retrieves a particular floating IP resource based on its unique ID.
 func Get(c *gophercloud.ServiceClient, id string) GetResult {
 	var res GetResult
 	_, err := perigee.Request("GET", resourceURL(c, id), perigee.Options{
@@ -75,10 +165,18 @@
 	return res
 }
 
+// UpdateOpts contains the values used when updating a floating IP resource. The
+// only value that can be updated is which internal port the floating IP is
+// linked to. To associate the floating IP with a new internal port, provide its
+// ID. To disassociate the floating IP from all ports, provide an empty string.
 type UpdateOpts struct {
 	PortID string
 }
 
+// Update allows floating IP resources to be updated. Currently, the only way to
+// "update" a floating IP is to associate it with a new internal port, or
+// disassociated it from all ports. See UpdateOpts for instructions of how to
+// do this.
 func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResult {
 	type floatingIP struct {
 		PortID *string `json:"port_id"`
@@ -109,6 +207,9 @@
 	return res
 }
 
+// Delete will permanently delete a particular floating IP resource. Please
+// ensure this is what you want - you can also disassociate the IP from existing
+// internal ports.
 func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
 	var res DeleteResult
 	_, err := perigee.Request("DELETE", resourceURL(c, id), perigee.Options{
diff --git a/openstack/networking/v2/extensions/layer3/floatingips/requests_test.go b/openstack/networking/v2/extensions/layer3/floatingips/requests_test.go
index f03d5ea..a9739ba 100644
--- a/openstack/networking/v2/extensions/layer3/floatingips/requests_test.go
+++ b/openstack/networking/v2/extensions/layer3/floatingips/requests_test.go
@@ -6,6 +6,7 @@
 	"testing"
 
 	"github.com/rackspace/gophercloud"
+	"github.com/rackspace/gophercloud/pagination"
 	th "github.com/rackspace/gophercloud/testhelper"
 )
 
@@ -18,6 +19,86 @@
 	}
 }
 
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/floatingips", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", tokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "floatingips": [
+        {
+            "floating_network_id": "6d67c30a-ddb4-49a1-bec3-a65b286b4170",
+            "router_id": null,
+            "fixed_ip_address": null,
+            "floating_ip_address": "192.0.0.4",
+            "tenant_id": "017d8de156df4177889f31a9bd6edc00",
+            "status": "DOWN",
+            "port_id": null,
+            "id": "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e"
+        },
+        {
+            "floating_network_id": "90f742b1-6d17-487b-ba95-71881dbc0b64",
+            "router_id": "0a24cb83-faf5-4d7f-b723-3144ed8a2167",
+            "fixed_ip_address": "192.0.0.2",
+            "floating_ip_address": "10.0.0.3",
+            "tenant_id": "017d8de156df4177889f31a9bd6edc00",
+            "status": "DOWN",
+            "port_id": "74a342ce-8e07-4e91-880c-9f834b68fa25",
+            "id": "ada25a95-f321-4f59-b0e0-f3a970dd3d63"
+        }
+    ]
+}
+			`)
+	})
+
+	count := 0
+
+	List(serviceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := ExtractFloatingIPs(page)
+		if err != nil {
+			t.Errorf("Failed to extract floating IPs: %v", err)
+			return false, err
+		}
+
+		expected := []FloatingIP{
+			FloatingIP{
+				FloatingNetworkID: "6d67c30a-ddb4-49a1-bec3-a65b286b4170",
+				FixedIP:           "",
+				FloatingIP:        "192.0.0.4",
+				TenantID:          "017d8de156df4177889f31a9bd6edc00",
+				Status:            "DOWN",
+				PortID:            "",
+				ID:                "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e",
+			},
+			FloatingIP{
+				FloatingNetworkID: "90f742b1-6d17-487b-ba95-71881dbc0b64",
+				FixedIP:           "192.0.0.2",
+				FloatingIP:        "10.0.0.3",
+				TenantID:          "017d8de156df4177889f31a9bd6edc00",
+				Status:            "DOWN",
+				PortID:            "74a342ce-8e07-4e91-880c-9f834b68fa25",
+				ID:                "ada25a95-f321-4f59-b0e0-f3a970dd3d63",
+			},
+		}
+
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
+
 func TestCreate(t *testing.T) {
 	th.SetupHTTP()
 	defer th.TeardownHTTP()
diff --git a/openstack/networking/v2/extensions/layer3/floatingips/results.go b/openstack/networking/v2/extensions/layer3/floatingips/results.go
index a09f1d4..f9c7a08 100644
--- a/openstack/networking/v2/extensions/layer3/floatingips/results.go
+++ b/openstack/networking/v2/extensions/layer3/floatingips/results.go
@@ -5,22 +5,45 @@
 
 	"github.com/mitchellh/mapstructure"
 	"github.com/rackspace/gophercloud"
+	"github.com/rackspace/gophercloud/pagination"
 )
 
+// FloatingIP represents a floating IP resource. A floating IP is an external
+// IP address that is mapped to an internal port and, optionally, a specific
+// IP address on a private network. In other words, it enables access to an
+// instance on a private network from an external network. For thsi reason,
+// floating IPs can only be defined on networks where the `router:external'
+// attribute (provided by the external network extension) is set to True.
 type FloatingIP struct {
-	ID                string `json:"id" mapstructure:"id"`
+	// Unique identifier for the floating IP instance.
+	ID string `json:"id" mapstructure:"id"`
+
+	// UUID of the external network where the floating IP is to be created.
 	FloatingNetworkID string `json:"floating_network_id" mapstructure:"floating_network_id"`
-	FloatingIP        string `json:"floating_ip_address" mapstructure:"floating_ip_address"`
-	PortID            string `json:"port_id" mapstructure:"port_id"`
-	FixedIP           string `json:"fixed_ip_address" mapstructure:"fixed_ip_address"`
-	TenantID          string `json:"tenant_id" mapstructure:"tenant_id"`
-	Status            string `json:"status" mapstructure:"status"`
+
+	// Address of the floating IP on the external network.
+	FloatingIP string `json:"floating_ip_address" mapstructure:"floating_ip_address"`
+
+	// UUID of the port on an internal network that is associated with the floating IP.
+	PortID string `json:"port_id" mapstructure:"port_id"`
+
+	// The specific IP address of the internal port which should be associated
+	// with the floating IP.
+	FixedIP string `json:"fixed_ip_address" mapstructure:"fixed_ip_address"`
+
+	// Owner of the floating IP. Only admin users can specify a tenant identifier
+	// other than its own.
+	TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
+
+	// The condition of the API resource.
+	Status string `json:"status" mapstructure:"status"`
 }
 
 type commonResult struct {
 	gophercloud.CommonResult
 }
 
+// Extract a result and extracts a FloatingIP resource.
 func (r commonResult) Extract() (*FloatingIP, error) {
 	if r.Err != nil {
 		return nil, r.Err
@@ -53,4 +76,58 @@
 	commonResult
 }
 
+// DeleteResult represents the result of an update operation.
 type DeleteResult commonResult
+
+type FloatingIPPage struct {
+	pagination.LinkedPageBase
+}
+
+func (p FloatingIPPage) NextPageURL() (string, error) {
+	type link struct {
+		Href string `mapstructure:"href"`
+		Rel  string `mapstructure:"rel"`
+	}
+	type resp struct {
+		Links []link `mapstructure:"floatingips_links"`
+	}
+
+	var r resp
+	err := mapstructure.Decode(p.Body, &r)
+	if err != nil {
+		return "", err
+	}
+
+	var url string
+	for _, l := range r.Links {
+		if l.Rel == "next" {
+			url = l.Href
+		}
+	}
+	if url == "" {
+		return "", nil
+	}
+
+	return url, nil
+}
+
+func (p FloatingIPPage) IsEmpty() (bool, error) {
+	is, err := ExtractFloatingIPs(p)
+	if err != nil {
+		return true, nil
+	}
+	return len(is) == 0, nil
+}
+
+func ExtractFloatingIPs(page pagination.Page) ([]FloatingIP, error) {
+	var resp struct {
+		FloatingIPs []FloatingIP `mapstructure:"floatingips" json:"floatingips"`
+	}
+
+	err := mapstructure.Decode(page.(FloatingIPPage).Body, &resp)
+	if err != nil {
+		return nil, err
+	}
+
+	return resp.FloatingIPs, nil
+}
diff --git a/openstack/networking/v2/extensions/layer3/routers/doc.go b/openstack/networking/v2/extensions/layer3/routers/doc.go
deleted file mode 100755
index 159906f..0000000
--- a/openstack/networking/v2/extensions/layer3/routers/doc.go
+++ /dev/null
@@ -1 +0,0 @@
-package routers
diff --git a/openstack/networking/v2/extensions/provider/results.go b/openstack/networking/v2/extensions/provider/results.go
index 09fdff6..11b0604 100755
--- a/openstack/networking/v2/extensions/provider/results.go
+++ b/openstack/networking/v2/extensions/provider/results.go
@@ -51,6 +51,8 @@
 	SegmentationID string `json:"provider:segmentation_id" mapstructure:"provider:segmentation_id"`
 }
 
+// ExtractGet decorates a GetResult struct returned from a networks.Get()
+// function with extended attributes.
 func ExtractGet(r networks.GetResult) (*NetworkExtAttrs, error) {
 	if r.Err != nil {
 		return nil, r.Err
@@ -65,6 +67,8 @@
 	return res.Network, nil
 }
 
+// ExtractGet decorates a CreateResult struct returned from a networks.Create()
+// function with extended attributes.
 func ExtractCreate(r networks.CreateResult) (*NetworkExtAttrs, error) {
 	if r.Err != nil {
 		return nil, r.Err
@@ -79,6 +83,8 @@
 	return res.Network, nil
 }
 
+// ExtractUpdate decorates a UpdateResult struct returned from a
+// networks.Update() function with extended attributes.
 func ExtractUpdate(r networks.UpdateResult) (*NetworkExtAttrs, error) {
 	if r.Err != nil {
 		return nil, r.Err