Test servers.List.
diff --git a/openstack/compute/v2/servers/data_test.go b/openstack/compute/v2/servers/data_test.go
new file mode 100644
index 0000000..16a5433
--- /dev/null
+++ b/openstack/compute/v2/servers/data_test.go
@@ -0,0 +1,328 @@
+package servers
+
+// Recorded responses for the server resource.
+
+const (
+	serverListBody = `
+      {
+        "servers": [
+          {
+            "status": "ACTIVE",
+            "updated": "2014-09-25T13:10:10Z",
+            "hostId": "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362",
+            "OS-EXT-SRV-ATTR:host": "devstack",
+            "addresses": {
+              "private": [
+                {
+                  "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:7c:1b:2b",
+                  "version": 4,
+                  "addr": "10.0.0.32",
+                  "OS-EXT-IPS:type": "fixed"
+                }
+              ]
+            },
+            "links": [
+              {
+                "href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/ef079b0c-e610-4dfb-b1aa-b49f07ac48e5",
+                "rel": "self"
+              },
+              {
+                "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/ef079b0c-e610-4dfb-b1aa-b49f07ac48e5",
+                "rel": "bookmark"
+              }
+            ],
+            "key_name": null,
+            "image": {
+              "id": "f90f6034-2570-4974-8351-6b49732ef2eb",
+              "links": [
+                {
+                  "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb",
+                  "rel": "bookmark"
+                }
+              ]
+            },
+            "OS-EXT-STS:task_state": null,
+            "OS-EXT-STS:vm_state": "active",
+            "OS-EXT-SRV-ATTR:instance_name": "instance-0000001e",
+            "OS-SRV-USG:launched_at": "2014-09-25T13:10:10.000000",
+            "OS-EXT-SRV-ATTR:hypervisor_hostname": "devstack",
+            "flavor": {
+              "id": "1",
+              "links": [
+                {
+                  "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1",
+                  "rel": "bookmark"
+                }
+              ]
+            },
+            "id": "ef079b0c-e610-4dfb-b1aa-b49f07ac48e5",
+            "security_groups": [
+              {
+                "name": "default"
+              }
+            ],
+            "OS-SRV-USG:terminated_at": null,
+            "OS-EXT-AZ:availability_zone": "nova",
+            "user_id": "9349aff8be7545ac9d2f1d00999a23cd",
+            "name": "herp",
+            "created": "2014-09-25T13:10:02Z",
+            "tenant_id": "fcad67a6189847c4aecfa3c81a05783b",
+            "OS-DCF:diskConfig": "MANUAL",
+            "os-extended-volumes:volumes_attached": [],
+            "accessIPv4": "",
+            "accessIPv6": "",
+            "progress": 0,
+            "OS-EXT-STS:power_state": 1,
+            "config_drive": "",
+            "metadata": {}
+          },
+          {
+            "status": "ACTIVE",
+            "updated": "2014-09-25T13:04:49Z",
+            "hostId": "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362",
+            "OS-EXT-SRV-ATTR:host": "devstack",
+            "addresses": {
+              "private": [
+                {
+                  "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:9e:89:be",
+                  "version": 4,
+                  "addr": "10.0.0.31",
+                  "OS-EXT-IPS:type": "fixed"
+                }
+              ]
+            },
+            "links": [
+              {
+                "href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba",
+                "rel": "self"
+              },
+              {
+                "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba",
+                "rel": "bookmark"
+              }
+            ],
+            "key_name": null,
+            "image": {
+              "id": "f90f6034-2570-4974-8351-6b49732ef2eb",
+              "links": [
+                {
+                  "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb",
+                  "rel": "bookmark"
+                }
+              ]
+            },
+            "OS-EXT-STS:task_state": null,
+            "OS-EXT-STS:vm_state": "active",
+            "OS-EXT-SRV-ATTR:instance_name": "instance-0000001d",
+            "OS-SRV-USG:launched_at": "2014-09-25T13:04:49.000000",
+            "OS-EXT-SRV-ATTR:hypervisor_hostname": "devstack",
+            "flavor": {
+              "id": "1",
+              "links": [
+                {
+                  "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1",
+                  "rel": "bookmark"
+                }
+              ]
+            },
+            "id": "9e5476bd-a4ec-4653-93d6-72c93aa682ba",
+            "security_groups": [
+              {
+                "name": "default"
+              }
+            ],
+            "OS-SRV-USG:terminated_at": null,
+            "OS-EXT-AZ:availability_zone": "nova",
+            "user_id": "9349aff8be7545ac9d2f1d00999a23cd",
+            "name": "derp",
+            "created": "2014-09-25T13:04:41Z",
+            "tenant_id": "fcad67a6189847c4aecfa3c81a05783b",
+            "OS-DCF:diskConfig": "MANUAL",
+            "os-extended-volumes:volumes_attached": [],
+            "accessIPv4": "",
+            "accessIPv6": "",
+            "progress": 0,
+            "OS-EXT-STS:power_state": 1,
+            "config_drive": "",
+            "metadata": {}
+          }
+        ]
+      }
+    `
+
+	serverGetBody = `
+    {
+      "server": {
+        "status": "ACTIVE",
+        "updated": "2014-09-25T13:04:49Z",
+        "hostId": "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362",
+        "OS-EXT-SRV-ATTR:host": "devstack",
+        "addresses": {
+          "private": [
+            {
+              "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:9e:89:be",
+              "version": 4,
+              "addr": "10.0.0.31",
+              "OS-EXT-IPS:type": "fixed"
+            }
+          ]
+        },
+        "links": [
+          {
+            "href": "http://104.130.131.164:8774/v2/e1a1494482b947f5a270acb6e5963aa3/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba",
+            "rel": "self"
+          },
+          {
+            "href": "http://104.130.131.164:8774/e1a1494482b947f5a270acb6e5963aa3/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba",
+            "rel": "bookmark"
+          }
+        ],
+        "key_name": null,
+        "image": {
+          "id": "f90f6034-2570-4974-8351-6b49732ef2eb",
+          "links": [
+            {
+              "href": "http://104.130.131.164:8774/e1a1494482b947f5a270acb6e5963aa3/images/f90f6034-2570-4974-8351-6b49732ef2eb",
+              "rel": "bookmark"
+            }
+          ]
+        },
+        "OS-EXT-STS:task_state": null,
+        "OS-EXT-STS:vm_state": "active",
+        "OS-EXT-SRV-ATTR:instance_name": "instance-0000001d",
+        "OS-SRV-USG:launched_at": "2014-09-25T13:04:49.000000",
+        "OS-EXT-SRV-ATTR:hypervisor_hostname": "devstack",
+        "flavor": {
+          "id": "1",
+          "links": [
+            {
+              "href": "http://104.130.131.164:8774/e1a1494482b947f5a270acb6e5963aa3/flavors/1",
+              "rel": "bookmark"
+            }
+          ]
+        },
+        "id": "9e5476bd-a4ec-4653-93d6-72c93aa682ba",
+        "security_groups": [
+          {
+            "name": "default"
+          }
+        ],
+        "OS-SRV-USG:terminated_at": null,
+        "OS-EXT-AZ:availability_zone": "nova",
+        "user_id": "9349aff8be7545ac9d2f1d00999a23cd",
+        "name": "derp",
+        "created": "2014-09-25T13:04:41Z",
+        "tenant_id": "fcad67a6189847c4aecfa3c81a05783b",
+        "OS-DCF:diskConfig": "MANUAL",
+        "os-extended-volumes:volumes_attached": [],
+        "accessIPv4": "",
+        "accessIPv6": "",
+        "progress": 0,
+        "OS-EXT-STS:power_state": 1,
+        "config_drive": "",
+        "metadata": {}
+      }
+    }
+    `
+)
+
+var (
+	serverHerp = Server{
+		Status:  "ACTIVE",
+		Updated: "2014-09-25T13:10:10Z",
+		HostID:  "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362",
+		Addresses: map[string]interface{}{
+			"private": []interface{}{
+				map[string]interface{}{
+					"OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:7c:1b:2b",
+					"version":                 float64(4),
+					"addr":                    "10.0.0.32",
+					"OS-EXT-IPS:type":         "fixed",
+				},
+			},
+		},
+		Links: []interface{}{
+			map[string]interface{}{
+				"href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/ef079b0c-e610-4dfb-b1aa-b49f07ac48e5",
+				"rel":  "self",
+			},
+			map[string]interface{}{
+				"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/ef079b0c-e610-4dfb-b1aa-b49f07ac48e5",
+				"rel":  "bookmark",
+			},
+		},
+		Image: map[string]interface{}{
+			"id": "f90f6034-2570-4974-8351-6b49732ef2eb",
+			"links": []interface{}{
+				map[string]interface{}{
+					"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb",
+					"rel":  "bookmark",
+				},
+			},
+		},
+		Flavor: map[string]interface{}{
+			"id": "1",
+			"links": []interface{}{
+				map[string]interface{}{
+					"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1",
+					"rel":  "bookmark",
+				},
+			},
+		},
+		ID:       "ef079b0c-e610-4dfb-b1aa-b49f07ac48e5",
+		UserID:   "9349aff8be7545ac9d2f1d00999a23cd",
+		Name:     "herp",
+		Created:  "2014-09-25T13:10:02Z",
+		TenantID: "fcad67a6189847c4aecfa3c81a05783b",
+		Metadata: map[string]interface{}{},
+	}
+	serverDerp = Server{
+		Status:  "ACTIVE",
+		Updated: "2014-09-25T13:04:49Z",
+		HostID:  "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362",
+		Addresses: map[string]interface{}{
+			"private": []interface{}{
+				map[string]interface{}{
+					"OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:9e:89:be",
+					"version":                 float64(4),
+					"addr":                    "10.0.0.31",
+					"OS-EXT-IPS:type":         "fixed",
+				},
+			},
+		},
+		Links: []interface{}{
+			map[string]interface{}{
+				"href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba",
+				"rel":  "self",
+			},
+			map[string]interface{}{
+				"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba",
+				"rel":  "bookmark",
+			},
+		},
+		Image: map[string]interface{}{
+			"id": "f90f6034-2570-4974-8351-6b49732ef2eb",
+			"links": []interface{}{
+				map[string]interface{}{
+					"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb",
+					"rel":  "bookmark",
+				},
+			},
+		},
+		Flavor: map[string]interface{}{
+			"id": "1",
+			"links": []interface{}{
+				map[string]interface{}{
+					"href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1",
+					"rel":  "bookmark",
+				},
+			},
+		},
+		ID:       "9e5476bd-a4ec-4653-93d6-72c93aa682ba",
+		UserID:   "9349aff8be7545ac9d2f1d00999a23cd",
+		Name:     "derp",
+		Created:  "2014-09-25T13:04:41Z",
+		TenantID: "fcad67a6189847c4aecfa3c81a05783b",
+		Metadata: map[string]interface{}{},
+	}
+)
diff --git a/openstack/compute/v2/servers/requests_test.go b/openstack/compute/v2/servers/requests_test.go
new file mode 100644
index 0000000..4499660
--- /dev/null
+++ b/openstack/compute/v2/servers/requests_test.go
@@ -0,0 +1,128 @@
+package servers
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/rackspace/gophercloud"
+	"github.com/rackspace/gophercloud/pagination"
+	"github.com/rackspace/gophercloud/testhelper"
+)
+
+const tokenID = "bzbzbzbzbz"
+
+func serviceClient() *gophercloud.ServiceClient {
+	return &gophercloud.ServiceClient{
+		Provider: &gophercloud.ProviderClient{TokenID: tokenID},
+		Endpoint: testhelper.Endpoint(),
+	}
+}
+
+func TestListServers(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+
+	testhelper.Mux.HandleFunc("/servers/detail", func(w http.ResponseWriter, r *http.Request) {
+		testhelper.TestMethod(t, r, "GET")
+		testhelper.TestHeader(t, r, "X-Auth-Token", tokenID)
+
+		w.Header().Add("Content-Type", "application/json")
+		r.ParseForm()
+		marker := r.Form.Get("marker")
+		switch marker {
+		case "":
+			fmt.Fprintf(w, serverListBody)
+		case "9e5476bd-a4ec-4653-93d6-72c93aa682ba":
+			fmt.Fprintf(w, `{ "servers": [] }`)
+		default:
+			t.Fatalf("/servers/detail invoked with unexpected marker=[%s]", marker)
+		}
+	})
+
+	client := serviceClient()
+	pages := 0
+	err := List(client).EachPage(func(page pagination.Page) (bool, error) {
+		pages++
+
+		actual, err := ExtractServers(page)
+		if err != nil {
+			return false, err
+		}
+
+		if len(actual) != 2 {
+			t.Fatalf("Expected 2 servers, got %d", len(actual))
+		}
+		equalServers(t, serverHerp, actual[0])
+		equalServers(t, serverDerp, actual[1])
+
+		return true, nil
+	})
+
+	if err != nil {
+		t.Fatalf("Unexpected error from EachPage: %v", err)
+	}
+	if pages != 1 {
+		t.Errorf("Expected 1 page, saw %d", pages)
+	}
+}
+
+func TestCreateServer(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+	t.Error("Pending")
+}
+
+func TestDeleteServer(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+	t.Error("Pending")
+}
+
+func TestGetServer(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+	t.Error("Pending")
+}
+
+func TestUpdateServer(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+	t.Error("Pending")
+}
+
+func TestChangeServerAdminPassword(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+	t.Error("Pending")
+}
+
+func TestRebootServer(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+	t.Error("Pending")
+}
+
+func TestRebuildServer(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+	t.Error("Pending")
+}
+
+func TestResizeServer(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+	t.Error("Pending")
+}
+
+func TestConfirmResize(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+	t.Error("Pending")
+}
+
+func TestRevertResize(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+	t.Error("Pending")
+}
diff --git a/openstack/compute/v2/servers/servers.go b/openstack/compute/v2/servers/servers.go
index fd11d4c..fbf50c3 100644
--- a/openstack/compute/v2/servers/servers.go
+++ b/openstack/compute/v2/servers/servers.go
@@ -48,7 +48,6 @@
 	Flavor map[string]interface{}
 
 	// Addresses includes a list of all IP addresses assigned to the server, keyed by pool.
-
 	Addresses map[string]interface{}
 
 	// Metadata includes a list of all user-specified key-value pairs attached to the server.
@@ -57,6 +56,9 @@
 	// Links includes HTTP references to the itself, useful for passing along to other APIs that might want a server reference.
 	Links []interface{}
 
+	// KeyName indicates which public key was injected into the server on launch.
+	KeyName string `mapstructure:"keyname"`
+
 	// AdminPass will generally be empty ("").  However, it will contain the administrative password chosen when provisioning a new server without a set AdminPass setting in the first place.
 	// Note that this is the ONLY time this field will be valid.
 	AdminPass string `mapstructure:"adminPass"`
diff --git a/openstack/compute/v2/servers/servers_test.go b/openstack/compute/v2/servers/servers_test.go
index 106d9b5..9127991 100644
--- a/openstack/compute/v2/servers/servers_test.go
+++ b/openstack/compute/v2/servers/servers_test.go
@@ -2,6 +2,7 @@
 
 import (
 	"encoding/json"
+	"reflect"
 	"testing"
 )
 
@@ -185,3 +186,62 @@
 		t.Fatalf("Expected 2 servers; got %d", len(svrs))
 	}
 }
+
+// This provides more fine-grained failures when Servers differ, because Server structs are too damn big to compare by eye.
+// FIXME I should absolutely refactor this into a general-purpose thing in testhelper.
+func equalServers(t *testing.T, expected Server, actual Server) {
+	if expected.ID != actual.ID {
+		t.Errorf("ID differs. expected=[%s], actual=[%s]", expected.ID, actual.ID)
+	}
+	if expected.TenantID != actual.TenantID {
+		t.Errorf("TenantID differs. expected=[%s], actual=[%s]", expected.TenantID, actual.TenantID)
+	}
+	if expected.UserID != actual.UserID {
+		t.Errorf("UserID differs. expected=[%s], actual=[%s]", expected.UserID, actual.UserID)
+	}
+	if expected.Name != actual.Name {
+		t.Errorf("Name differs. expected=[%s], actual=[%s]", expected.Name, actual.Name)
+	}
+	if expected.Updated != actual.Updated {
+		t.Errorf("Updated differs. expected=[%s], actual=[%s]", expected.Updated, actual.Updated)
+	}
+	if expected.Created != actual.Created {
+		t.Errorf("Created differs. expected=[%s], actual=[%s]", expected.Created, actual.Created)
+	}
+	if expected.HostID != actual.HostID {
+		t.Errorf("HostID differs. expected=[%s], actual=[%s]", expected.HostID, actual.HostID)
+	}
+	if expected.Status != actual.Status {
+		t.Errorf("Status differs. expected=[%s], actual=[%s]", expected.Status, actual.Status)
+	}
+	if expected.Progress != actual.Progress {
+		t.Errorf("Progress differs. expected=[%s], actual=[%s]", expected.Progress, actual.Progress)
+	}
+	if expected.AccessIPv4 != actual.AccessIPv4 {
+		t.Errorf("AccessIPv4 differs. expected=[%s], actual=[%s]", expected.AccessIPv4, actual.AccessIPv4)
+	}
+	if expected.AccessIPv6 != actual.AccessIPv6 {
+		t.Errorf("AccessIPv6 differs. expected=[%s], actual=[%s]", expected.AccessIPv6, actual.AccessIPv6)
+	}
+	if !reflect.DeepEqual(expected.Image, actual.Image) {
+		t.Errorf("Image differs. expected=[%s], actual=[%s]", expected.Image, actual.Image)
+	}
+	if !reflect.DeepEqual(expected.Flavor, actual.Flavor) {
+		t.Errorf("Flavor differs. expected=[%s], actual=[%s]", expected.Flavor, actual.Flavor)
+	}
+	if !reflect.DeepEqual(expected.Addresses, actual.Addresses) {
+		t.Errorf("Addresses differ. expected=[%s], actual=[%s]", expected.Addresses, actual.Addresses)
+	}
+	if !reflect.DeepEqual(expected.Metadata, actual.Metadata) {
+		t.Errorf("Metadata differs. expected=[%s], actual=[%s]", expected.Metadata, actual.Metadata)
+	}
+	if !reflect.DeepEqual(expected.Links, actual.Links) {
+		t.Errorf("Links differs. expected=[%s], actual=[%s]", expected.Links, actual.Links)
+	}
+	if expected.KeyName != actual.KeyName {
+		t.Errorf("KeyName differs. expected=[%s], actual=[%s]", expected.KeyName, actual.KeyName)
+	}
+	if expected.AdminPass != actual.AdminPass {
+		t.Errorf("AdminPass differs. expected=[%s], actual=[%s]", expected.AdminPass, actual.AdminPass)
+	}
+}