Finalizing provider ext
diff --git a/openstack/networking/v2/extensions/networkattrs/doc.go b/openstack/networking/v2/extensions/networkattrs/doc.go
deleted file mode 100755
index b54ae27..0000000
--- a/openstack/networking/v2/extensions/networkattrs/doc.go
+++ /dev/null
@@ -1 +0,0 @@
-package networkattrs
diff --git a/openstack/networking/v2/extensions/networkattrs/errors.go b/openstack/networking/v2/extensions/networkattrs/errors.go
deleted file mode 100755
index b54ae27..0000000
--- a/openstack/networking/v2/extensions/networkattrs/errors.go
+++ /dev/null
@@ -1 +0,0 @@
-package networkattrs
diff --git a/openstack/networking/v2/extensions/networkattrs/requests.go b/openstack/networking/v2/extensions/networkattrs/requests.go
deleted file mode 100755
index b54ae27..0000000
--- a/openstack/networking/v2/extensions/networkattrs/requests.go
+++ /dev/null
@@ -1 +0,0 @@
-package networkattrs
diff --git a/openstack/networking/v2/extensions/networkattrs/requests_test.go b/openstack/networking/v2/extensions/networkattrs/requests_test.go
deleted file mode 100755
index b54ae27..0000000
--- a/openstack/networking/v2/extensions/networkattrs/requests_test.go
+++ /dev/null
@@ -1 +0,0 @@
-package networkattrs
diff --git a/openstack/networking/v2/extensions/networkattrs/results.go b/openstack/networking/v2/extensions/networkattrs/results.go
deleted file mode 100755
index b54ae27..0000000
--- a/openstack/networking/v2/extensions/networkattrs/results.go
+++ /dev/null
@@ -1 +0,0 @@
-package networkattrs
diff --git a/openstack/networking/v2/extensions/networkattrs/urls.go b/openstack/networking/v2/extensions/networkattrs/urls.go
deleted file mode 100755
index b54ae27..0000000
--- a/openstack/networking/v2/extensions/networkattrs/urls.go
+++ /dev/null
@@ -1 +0,0 @@
-package networkattrs
diff --git a/openstack/networking/v2/extensions/networkattrs/urls_tests.go b/openstack/networking/v2/extensions/networkattrs/urls_tests.go
deleted file mode 100755
index b54ae27..0000000
--- a/openstack/networking/v2/extensions/networkattrs/urls_tests.go
+++ /dev/null
@@ -1 +0,0 @@
-package networkattrs
diff --git a/openstack/networking/v2/extensions/provider/doc.go b/openstack/networking/v2/extensions/provider/doc.go
new file mode 100755
index 0000000..612d26e
--- /dev/null
+++ b/openstack/networking/v2/extensions/provider/doc.go
@@ -0,0 +1,21 @@
+// Package networkattrs gives access to the provider Neutron plugin, allowing
+// network extended attributes. The provider extended attributes for networks
+// enable administrative users to specify how network objects map to the
+// underlying networking infrastructure. These extended attributes also appear
+// when administrative users query networks.
+//
+// For more information about extended attributes, see the NetworkExtAttrs
+// struct. The actual semantics of these attributes depend on the technology
+// back end of the particular plug-in. See the plug-in documentation and the
+// OpenStack Cloud Administrator Guide to understand which values should be
+// specific for each of these attributes when OpenStack Networking is deployed
+// with a particular plug-in. The examples shown in this chapter refer to the
+// Open vSwitch plug-in.
+//
+// The default policy settings enable only users with administrative rights to
+// specify these parameters in requests and to see their values in responses. By
+// default, the provider network extension attributes are completely hidden from
+// regular tenants. As a rule of thumb, if these attributes are not visible in a
+// GET /networks/<network-id> operation, this implies the user submitting the
+// request is not authorized to view or manipulate provider network attributes.
+package provider
diff --git a/openstack/networking/v2/extensions/provider/results.go b/openstack/networking/v2/extensions/provider/results.go
new file mode 100755
index 0000000..09fdff6
--- /dev/null
+++ b/openstack/networking/v2/extensions/provider/results.go
@@ -0,0 +1,110 @@
+package provider
+
+import (
+	"fmt"
+
+	"github.com/mitchellh/mapstructure"
+	"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
+	"github.com/rackspace/gophercloud/pagination"
+)
+
+// NetworkExtAttrs represents an extended form of a Network with additional fields.
+type NetworkExtAttrs struct {
+	// UUID for the network
+	ID string `mapstructure:"id" json:"id"`
+
+	// Human-readable name for the network. Might not be unique.
+	Name string `mapstructure:"name" json:"name"`
+
+	// The administrative state of network. If false (down), the network does not forward packets.
+	AdminStateUp bool `mapstructure:"admin_state_up" json:"admin_state_up"`
+
+	// Indicates whether network is currently operational. Possible values include
+	// `ACTIVE', `DOWN', `BUILD', or `ERROR'. Plug-ins might define additional values.
+	Status string `mapstructure:"status" json:"status"`
+
+	// Subnets associated with this network.
+	Subnets []string `mapstructure:"subnets" json:"subnets"`
+
+	// Owner of network. Only admin users can specify a tenant_id other than its own.
+	TenantID string `mapstructure:"tenant_id" json:"tenant_id"`
+
+	// Specifies whether the network resource can be accessed by any tenant or not.
+	Shared bool `mapstructure:"shared" json:"shared"`
+
+	// Specifies the nature of the physical network mapped to this network
+	// resource. Examples are flat, vlan, or gre.
+	NetworkType string `json:"provider:network_type" mapstructure:"provider:network_type"`
+
+	// Identifies the physical network on top of which this network object is
+	// being implemented. The OpenStack Networking API does not expose any facility
+	// for retrieving the list of available physical networks. As an example, in
+	// the Open vSwitch plug-in this is a symbolic name which is then mapped to
+	// specific bridges on each compute host through the Open vSwitch plug-in
+	// configuration file.
+	PhysicalNetwork string `json:"provider:physical_network" mapstructure:"provider:physical_network"`
+
+	// Identifies an isolated segment on the physical network; the nature of the
+	// segment depends on the segmentation model defined by network_type. For
+	// instance, if network_type is vlan, then this is a vlan identifier;
+	// otherwise, if network_type is gre, then this will be a gre key.
+	SegmentationID string `json:"provider:segmentation_id" mapstructure:"provider:segmentation_id"`
+}
+
+func ExtractGet(r networks.GetResult) (*NetworkExtAttrs, error) {
+	if r.Err != nil {
+		return nil, r.Err
+	}
+	var res struct {
+		Network *NetworkExtAttrs `json:"network"`
+	}
+	err := mapstructure.Decode(r.Resp, &res)
+	if err != nil {
+		return nil, fmt.Errorf("Error decoding Neutron network: %v", err)
+	}
+	return res.Network, nil
+}
+
+func ExtractCreate(r networks.CreateResult) (*NetworkExtAttrs, error) {
+	if r.Err != nil {
+		return nil, r.Err
+	}
+	var res struct {
+		Network *NetworkExtAttrs `json:"network"`
+	}
+	err := mapstructure.Decode(r.Resp, &res)
+	if err != nil {
+		return nil, fmt.Errorf("Error decoding Neutron network: %v", err)
+	}
+	return res.Network, nil
+}
+
+func ExtractUpdate(r networks.UpdateResult) (*NetworkExtAttrs, error) {
+	if r.Err != nil {
+		return nil, r.Err
+	}
+	var res struct {
+		Network *NetworkExtAttrs `json:"network"`
+	}
+	err := mapstructure.Decode(r.Resp, &res)
+	if err != nil {
+		return nil, fmt.Errorf("Error decoding Neutron network: %v", err)
+	}
+	return res.Network, nil
+}
+
+// ExtractList accepts a Page struct, specifically a NetworkPage struct, and
+// extracts the elements into a slice of NetworkExtAttrs structs. In other
+// words, a generic collection is mapped into a relevant slice.
+func ExtractList(page pagination.Page) ([]NetworkExtAttrs, error) {
+	var resp struct {
+		Networks []NetworkExtAttrs `mapstructure:"networks" json:"networks"`
+	}
+
+	err := mapstructure.Decode(page.(networks.NetworkPage).Body, &resp)
+	if err != nil {
+		return nil, err
+	}
+
+	return resp.Networks, nil
+}
diff --git a/openstack/networking/v2/extensions/provider/results_test.go b/openstack/networking/v2/extensions/provider/results_test.go
new file mode 100644
index 0000000..f575197
--- /dev/null
+++ b/openstack/networking/v2/extensions/provider/results_test.go
@@ -0,0 +1,262 @@
+package provider
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/rackspace/gophercloud"
+	"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
+	"github.com/rackspace/gophercloud/pagination"
+	th "github.com/rackspace/gophercloud/testhelper"
+)
+
+const tokenID = "123"
+
+func serviceClient() *gophercloud.ServiceClient {
+	return &gophercloud.ServiceClient{
+		Provider: &gophercloud.ProviderClient{TokenID: tokenID},
+		Endpoint: th.Endpoint(),
+	}
+}
+
+func TestList(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/networks", 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, `
+{
+    "networks": [
+        {
+            "status": "ACTIVE",
+            "subnets": [
+                "54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
+            ],
+            "name": "private-network",
+            "admin_state_up": true,
+            "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
+            "shared": true,
+            "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
+            "provider:segmentation_id": null,
+            "provider:physical_network": null,
+            "provider:network_type": "local"
+        },
+        {
+            "status": "ACTIVE",
+            "subnets": [
+                "08eae331-0402-425a-923c-34f7cfe39c1b"
+            ],
+            "name": "private",
+            "admin_state_up": true,
+            "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e",
+            "shared": true,
+            "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324",
+            "provider:segmentation_id": null,
+            "provider:physical_network": null,
+            "provider:network_type": "local"
+        }
+    ]
+}
+			`)
+	})
+
+	count := 0
+
+	networks.List(serviceClient(), networks.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := ExtractList(page)
+		if err != nil {
+			t.Errorf("Failed to extract networks: %v", err)
+			return false, err
+		}
+
+		expected := []NetworkExtAttrs{
+			NetworkExtAttrs{
+				Status:          "ACTIVE",
+				Subnets:         []string{"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"},
+				Name:            "private-network",
+				AdminStateUp:    true,
+				TenantID:        "4fd44f30292945e481c7b8a0c8908869",
+				Shared:          true,
+				ID:              "d32019d3-bc6e-4319-9c1d-6722fc136a22",
+				NetworkType:     "local",
+				PhysicalNetwork: "",
+				SegmentationID:  "",
+			},
+			NetworkExtAttrs{
+				Status:          "ACTIVE",
+				Subnets:         []string{"08eae331-0402-425a-923c-34f7cfe39c1b"},
+				Name:            "private",
+				AdminStateUp:    true,
+				TenantID:        "26a7980765d0414dbc1fc1f88cdb7e6e",
+				Shared:          true,
+				ID:              "db193ab3-96e3-4cb3-8fc5-05f4296d0324",
+				NetworkType:     "local",
+				PhysicalNetwork: "",
+				SegmentationID:  "",
+			},
+		}
+
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
+
+func TestGet(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/networks/d32019d3-bc6e-4319-9c1d-6722fc136a22", 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, `
+{
+    "network": {
+        "status": "ACTIVE",
+        "subnets": [
+            "54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
+        ],
+        "name": "private-network",
+        "provider:physical_network": null,
+        "admin_state_up": true,
+        "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
+        "provider:network_type": "local",
+        "shared": true,
+        "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
+        "provider:segmentation_id": null
+    }
+}
+			`)
+	})
+
+	res := networks.Get(serviceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22")
+	n, err := ExtractGet(res)
+
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, "", n.PhysicalNetwork)
+	th.AssertEquals(t, "local", n.NetworkType)
+	th.AssertEquals(t, "", n.SegmentationID)
+}
+
+func TestCreate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/networks", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", tokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+    "network": {
+        "name": "sample_network",
+        "admin_state_up": true
+    }
+}
+			`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusCreated)
+
+		fmt.Fprintf(w, `
+{
+    "network": {
+        "status": "ACTIVE",
+        "subnets": [
+            "54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
+        ],
+        "name": "private-network",
+        "provider:physical_network": null,
+        "admin_state_up": true,
+        "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
+        "provider:network_type": "local",
+        "shared": true,
+        "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
+        "provider:segmentation_id": null
+    }
+}
+		`)
+	})
+
+	options := networks.CreateOpts{Name: "sample_network", AdminStateUp: true}
+	res := networks.Create(serviceClient(), options)
+	n, err := ExtractCreate(res)
+
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, "", n.PhysicalNetwork)
+	th.AssertEquals(t, "local", n.NetworkType)
+	th.AssertEquals(t, "", n.SegmentationID)
+}
+
+func TestUpdate(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/v2.0/networks/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", tokenID)
+		th.TestHeader(t, r, "Content-Type", "application/json")
+		th.TestHeader(t, r, "Accept", "application/json")
+		th.TestJSONRequest(t, r, `
+{
+		"network": {
+				"name": "new_network_name",
+				"admin_state_up": false,
+				"shared": true
+		}
+}
+			`)
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+
+		fmt.Fprintf(w, `
+{
+    "network": {
+        "status": "ACTIVE",
+        "subnets": [
+            "54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
+        ],
+        "name": "private-network",
+        "provider:physical_network": null,
+        "admin_state_up": true,
+        "tenant_id": "4fd44f30292945e481c7b8a0c8908869",
+        "provider:network_type": "local",
+        "shared": true,
+        "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
+        "provider:segmentation_id": null
+    }
+}
+		`)
+	})
+
+	shared := true
+	options := networks.UpdateOpts{Name: "new_network_name", AdminStateUp: false, Shared: &shared}
+	res := networks.Update(serviceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options)
+	n, err := ExtractUpdate(res)
+
+	th.AssertNoErr(t, err)
+
+	th.AssertEquals(t, "", n.PhysicalNetwork)
+	th.AssertEquals(t, "local", n.NetworkType)
+	th.AssertEquals(t, "", n.SegmentationID)
+}