Use clients and pagination for Servers, too.

Whew.
diff --git a/openstack/compute/v2/servers/client.go b/openstack/compute/v2/servers/client.go
deleted file mode 100644
index 8c79f94..0000000
--- a/openstack/compute/v2/servers/client.go
+++ /dev/null
@@ -1,93 +0,0 @@
-package servers
-
-import (
-	"fmt"
-
-	"github.com/rackspace/gophercloud"
-	identity "github.com/rackspace/gophercloud/openstack/identity/v2"
-)
-
-// Client abstracts the connection information needed to make API requests for OpenStack compute endpoints.
-type Client struct {
-	endpoint  string
-	authority identity.AuthResults
-	options   gophercloud.AuthOptions
-	token     *identity.Token
-}
-
-// NewClient creates a new Client structure to use when issuing requests to the server.
-func NewClient(e string, a identity.AuthResults, o gophercloud.AuthOptions) *Client {
-	return &Client{
-		endpoint:  e,
-		authority: a,
-		options:   o,
-	}
-}
-
-func (c *Client) getListUrl() string {
-	return fmt.Sprintf("%s/servers/detail", c.endpoint)
-}
-
-func (c *Client) getCreateUrl() string {
-	return fmt.Sprintf("%s/servers", c.endpoint)
-}
-
-func (c *Client) getDeleteUrl(id string) string {
-	return fmt.Sprintf("%s/servers/%s", c.endpoint, id)
-}
-
-func (c *Client) getDetailUrl(id string) string {
-	return c.getDeleteUrl(id)
-}
-
-func (c *Client) getUpdateUrl(id string) string {
-	return c.getDeleteUrl(id)
-}
-
-func (c *Client) getActionUrl(id string) string {
-	return fmt.Sprintf("%s/servers/%s/action", c.endpoint, id)
-}
-
-func (c *Client) getListHeaders() (map[string]string, error) {
-	t, err := c.getAuthToken()
-	if err != nil {
-		return map[string]string{}, err
-	}
-
-	return map[string]string{
-		"X-Auth-Token": t,
-	}, nil
-}
-
-func (c *Client) getCreateHeaders() (map[string]string, error) {
-	return c.getListHeaders()
-}
-
-func (c *Client) getDeleteHeaders() (map[string]string, error) {
-	return c.getListHeaders()
-}
-
-func (c *Client) getDetailHeaders() (map[string]string, error) {
-	return c.getListHeaders()
-}
-
-func (c *Client) getUpdateHeaders() (map[string]string, error) {
-	return c.getListHeaders()
-}
-
-func (c *Client) getActionHeaders() (map[string]string, error) {
-	return c.getListHeaders()
-}
-
-func (c *Client) getAuthToken() (string, error) {
-	var err error
-
-	if c.token == nil {
-		c.token, err = identity.GetToken(c.authority)
-		if err != nil {
-			return "", err
-		}
-	}
-
-	return c.token.ID, err
-}
diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go
index 95818da..e2875e3 100644
--- a/openstack/compute/v2/servers/requests.go
+++ b/openstack/compute/v2/servers/requests.go
@@ -2,123 +2,111 @@
 
 import (
 	"fmt"
+
 	"github.com/racker/perigee"
+	"github.com/rackspace/gophercloud"
+	"github.com/rackspace/gophercloud/pagination"
 )
 
-// ListResult abstracts the raw results of making a List() request against the
-// API.  As OpenStack extensions may freely alter the response bodies of
-// structures returned to the client, you may only safely access the data
-// provided through separate, type-safe accessors or methods.
-type ListResult map[string]interface{}
+// ListResult abstracts the raw results of making a List() request against the API.
+// As OpenStack extensions may freely alter the response bodies of structures returned to the client, you may only safely access the
+// data provided through the ExtractServers call.
+type ListResult struct {
+	pagination.MarkerPageBase
+}
 
-// ServerResult abstracts a single server description,
-// as returned by the OpenStack provider.
-// As OpenStack extensions may freely alter the response bodies of the
-// structures returned to the client,
-// you may only safely access the data provided through
-// separate, type-safe accessors or methods.
+// IsEmpty returns true if a page contains no Server results.
+func (page ListResult) IsEmpty() (bool, error) {
+	servers, err := ExtractServers(page)
+	if err != nil {
+		return true, err
+	}
+	return len(servers) == 0, nil
+}
+
+// LastMarker returns the ID of the final server on the current page.
+func (page ListResult) LastMarker() (string, error) {
+	servers, err := ExtractServers(page)
+	if err != nil {
+		return "", err
+	}
+	if len(servers) == 0 {
+		return "", nil
+	}
+	return servers[len(servers)-1].ID, nil
+}
+
+// ServerResult abstracts a single server description, as returned by the OpenStack provider.
+// As OpenStack extensions may freely alter the response bodies of the structures returned to the client,
+// you may only safely access the data provided through separate, type-safe accessors or methods.
 type ServerResult map[string]interface{}
 
 // List makes a request against the API to list servers accessible to you.
-func List(c *Client) (ListResult, error) {
-	var lr ListResult
-
-	h, err := c.getListHeaders()
-	if err != nil {
-		return nil, err
+func List(client *gophercloud.ServiceClient) pagination.Pager {
+	createPage := func(r pagination.LastHTTPResponse) pagination.Page {
+		p := ListResult{pagination.MarkerPageBase{LastHTTPResponse: r}}
+		p.MarkerPageBase.Owner = p
+		return p
 	}
 
-	err = perigee.Get(c.getListUrl(), perigee.Options{
-		Results:     &lr,
-		MoreHeaders: h,
-	})
-	return lr, err
+	return pagination.NewPager(client, getListURL(client), createPage)
 }
 
 // Create requests a server to be provisioned to the user in the current tenant.
-func Create(c *Client, opts map[string]interface{}) (ServerResult, error) {
+func Create(client *gophercloud.ServiceClient, opts map[string]interface{}) (ServerResult, error) {
 	var sr ServerResult
-
-	h, err := c.getCreateHeaders()
-	if err != nil {
-		return nil, err
-	}
-
-	err = perigee.Post(c.getCreateUrl(), perigee.Options{
+	_, err := perigee.Request("POST", getListURL(client), perigee.Options{
 		Results: &sr,
 		ReqBody: map[string]interface{}{
 			"server": opts,
 		},
-		MoreHeaders: h,
+		MoreHeaders: client.Provider.AuthenticatedHeaders(),
 		OkCodes:     []int{202},
 	})
 	return sr, err
 }
 
 // Delete requests that a server previously provisioned be removed from your account.
-func Delete(c *Client, id string) error {
-	h, err := c.getDeleteHeaders()
-	if err != nil {
-		return err
-	}
-
-	err = perigee.Delete(c.getDeleteUrl(id), perigee.Options{
-		MoreHeaders: h,
+func Delete(client *gophercloud.ServiceClient, id string) error {
+	_, err := perigee.Request("DELETE", getServerURL(client, id), perigee.Options{
+		MoreHeaders: client.Provider.AuthenticatedHeaders(),
 		OkCodes:     []int{204},
 	})
 	return err
 }
 
 // GetDetail requests details on a single server, by ID.
-func GetDetail(c *Client, id string) (ServerResult, error) {
+func GetDetail(client *gophercloud.ServiceClient, id string) (ServerResult, error) {
 	var sr ServerResult
-
-	h, err := c.getDetailHeaders()
-	if err != nil {
-		return nil, err
-	}
-
-	err = perigee.Get(c.getDetailUrl(id), perigee.Options{
+	_, err := perigee.Request("GET", getServerURL(client, id), perigee.Options{
 		Results:     &sr,
-		MoreHeaders: h,
+		MoreHeaders: client.Provider.AuthenticatedHeaders(),
 	})
 	return sr, err
 }
 
 // Update requests that various attributes of the indicated server be changed.
-func Update(c *Client, id string, opts map[string]interface{}) (ServerResult, error) {
+func Update(client *gophercloud.ServiceClient, id string, opts map[string]interface{}) (ServerResult, error) {
 	var sr ServerResult
-
-	h, err := c.getUpdateHeaders()
-	if err != nil {
-		return nil, err
-	}
-
-	err = perigee.Put(c.getUpdateUrl(id), perigee.Options{
+	_, err := perigee.Request("PUT", getServerURL(client, id), perigee.Options{
 		Results: &sr,
 		ReqBody: map[string]interface{}{
 			"server": opts,
 		},
-		MoreHeaders: h,
+		MoreHeaders: client.Provider.AuthenticatedHeaders(),
 	})
 	return sr, err
 }
 
-// ChangeAdminPassword alters the administrator or root password for a specified
-// server.
-func ChangeAdminPassword(c *Client, id, newPassword string) error {
-	h, err := c.getActionHeaders()
-	if err != nil {
-		return err
-	}
-
-	err = perigee.Post(c.getActionUrl(id), perigee.Options{
+// ChangeAdminPassword alters the administrator or root password for a specified server.
+func ChangeAdminPassword(client *gophercloud.ServiceClient, id, newPassword string) error {
+	_, err := perigee.Request("POST", getActionURL(client, id), perigee.Options{
 		ReqBody: struct {
 			C map[string]string `json:"changePassword"`
 		}{
 			map[string]string{"adminPass": newPassword},
 		},
-		MoreHeaders: h,
+		MoreHeaders: client.Provider.AuthenticatedHeaders(),
 		OkCodes:     []int{202},
 	})
 	return err
@@ -150,28 +138,29 @@
 	return e.Error()
 }
 
+// RebootMethod describes the mechanisms by which a server reboot can be requested.
+type RebootMethod string
+
 // These constants determine how a server should be rebooted.
 // See the Reboot() function for further details.
 const (
-	SoftReboot = "SOFT"
-	HardReboot = "HARD"
-	OSReboot   = SoftReboot
-	PowerCycle = HardReboot
+	SoftReboot RebootMethod = "SOFT"
+	HardReboot RebootMethod = "HARD"
+	OSReboot                = SoftReboot
+	PowerCycle              = HardReboot
 )
 
 // Reboot requests that a given server reboot.
 // Two methods exist for rebooting a server:
 //
-// HardReboot (aka PowerCycle) -- restarts the server instance by physically
-// cutting power to the machine, or if a VM, terminating it at the hypervisor
-// level.  It's done.  Caput.  Full stop.  Then, after a brief while, power is
-// restored or the VM instance restarted.
+// HardReboot (aka PowerCycle) restarts the server instance by physically cutting power to the machine, or if a VM,
+// terminating it at the hypervisor level.
+// It's done. Caput. Full stop.
+// Then, after a brief while, power is restored or the VM instance restarted.
 //
-// SoftReboot (aka OSReboot).  This approach simply tells the OS to restart
-// under its own procedures.  E.g., in Linux, asking it to enter runlevel 6,
-// or executing "sudo shutdown -r now", or by wasking Windows to restart the
-// machine.
-func Reboot(c *Client, id, how string) error {
+// SoftReboot (aka OSReboot) simply tells the OS to restart under its own procedures.
+// E.g., in Linux, asking it to enter runlevel 6, or executing "sudo shutdown -r now", or by asking Windows to restart the machine.
+func Reboot(client *gophercloud.ServiceClient, id string, how RebootMethod) error {
 	if (how != SoftReboot) && (how != HardReboot) {
 		return &ErrArgument{
 			Function: "Reboot",
@@ -180,36 +169,28 @@
 		}
 	}
 
-	h, err := c.getActionHeaders()
-	if err != nil {
-		return err
-	}
-
-	err = perigee.Post(c.getActionUrl(id), perigee.Options{
+	_, err := perigee.Request("POST", getActionURL(client, id), perigee.Options{
 		ReqBody: struct {
 			C map[string]string `json:"reboot"`
 		}{
-			map[string]string{"type": how},
+			map[string]string{"type": string(how)},
 		},
-		MoreHeaders: h,
+		MoreHeaders: client.Provider.AuthenticatedHeaders(),
 		OkCodes:     []int{202},
 	})
 	return err
 }
 
-// Rebuild requests that the Openstack provider reprovision the
-// server.  The rebuild will need to know the server's name and
-// new image reference or ID.  In addition, and unlike building
-// a server with Create(), you must provide an administrator
-// password.
+// Rebuild requests that the Openstack provider reprovision the server.
+// The rebuild will need to know the server's name and new image reference or ID.
+// In addition, and unlike building a server with Create(), you must provide an administrator password.
 //
 // Additional options may be specified with the additional map.
 // This function treats a nil map the same as an empty map.
 //
-// Rebuild returns a server result as though you had called
-// GetDetail() on the server's ID.  The information, however,
-// refers to the new server, not the old.
-func Rebuild(c *Client, id, name, password, imageRef string, additional map[string]interface{}) (ServerResult, error) {
+// Rebuild returns a server result as though you had called GetDetail() on the server's ID.
+// The information, however, refers to the new server, not the old.
+func Rebuild(client *gophercloud.ServiceClient, id, name, password, imageRef string, additional map[string]interface{}) (ServerResult, error) {
 	var sr ServerResult
 
 	if id == "" {
@@ -252,43 +233,34 @@
 	additional["imageRef"] = imageRef
 	additional["adminPass"] = password
 
-	h, err := c.getActionHeaders()
-	if err != nil {
-		return sr, err
-	}
-
-	err = perigee.Post(c.getActionUrl(id), perigee.Options{
+	_, err := perigee.Request("POST", getActionURL(client, id), perigee.Options{
 		ReqBody: struct {
 			R map[string]interface{} `json:"rebuild"`
 		}{
 			additional,
 		},
 		Results:     &sr,
-		MoreHeaders: h,
+		MoreHeaders: client.Provider.AuthenticatedHeaders(),
 		OkCodes:     []int{202},
 	})
 	return sr, err
 }
 
 // Resize instructs the provider to change the flavor of the server.
-// Note that this implies rebuilding it.  Unfortunately, one cannot pass rebuild parameters to the resize function.
+// Note that this implies rebuilding it.
+// Unfortunately, one cannot pass rebuild parameters to the resize function.
 // When the resize completes, the server will be in RESIZE_VERIFY state.
 // While in this state, you can explore the use of the new server's configuration.
 // If you like it, call ConfirmResize() to commit the resize permanently.
 // Otherwise, call RevertResize() to restore the old configuration.
-func Resize(c *Client, id, flavorRef string) error {
-	h, err := c.getActionHeaders()
-	if err != nil {
-		return err
-	}
-
-	err = perigee.Post(c.getActionUrl(id), perigee.Options{
+func Resize(client *gophercloud.ServiceClient, id, flavorRef string) error {
+	_, err := perigee.Request("POST", getActionURL(client, id), perigee.Options{
 		ReqBody: struct {
 			R map[string]interface{} `json:"resize"`
 		}{
 			map[string]interface{}{"flavorRef": flavorRef},
 		},
-		MoreHeaders: h,
+		MoreHeaders: client.Provider.AuthenticatedHeaders(),
 		OkCodes:     []int{202},
 	})
 	return err
@@ -296,15 +268,10 @@
 
 // ConfirmResize confirms a previous resize operation on a server.
 // See Resize() for more details.
-func ConfirmResize(c *Client, id string) error {
-	h, err := c.getActionHeaders()
-	if err != nil {
-		return err
-	}
-
-	err = perigee.Post(c.getActionUrl(id), perigee.Options{
+func ConfirmResize(client *gophercloud.ServiceClient, id string) error {
+	_, err := perigee.Request("POST", getActionURL(client, id), perigee.Options{
 		ReqBody:     map[string]interface{}{"confirmResize": nil},
-		MoreHeaders: h,
+		MoreHeaders: client.Provider.AuthenticatedHeaders(),
 		OkCodes:     []int{204},
 	})
 	return err
@@ -312,15 +279,10 @@
 
 // RevertResize cancels a previous resize operation on a server.
 // See Resize() for more details.
-func RevertResize(c *Client, id string) error {
-	h, err := c.getActionHeaders()
-	if err != nil {
-		return err
-	}
-
-	err = perigee.Post(c.getActionUrl(id), perigee.Options{
+func RevertResize(client *gophercloud.ServiceClient, id string) error {
+	_, err := perigee.Request("POST", getActionURL(client, id), perigee.Options{
 		ReqBody:     map[string]interface{}{"revertResize": nil},
-		MoreHeaders: h,
+		MoreHeaders: client.Provider.AuthenticatedHeaders(),
 		OkCodes:     []int{202},
 	})
 	return err
diff --git a/openstack/compute/v2/servers/servers.go b/openstack/compute/v2/servers/servers.go
index 28d66d0..865c7d1 100644
--- a/openstack/compute/v2/servers/servers.go
+++ b/openstack/compute/v2/servers/servers.go
@@ -1,89 +1,80 @@
 package servers
 
 import (
-	"fmt"
+	"errors"
+
 	"github.com/mitchellh/mapstructure"
+	"github.com/rackspace/gophercloud/pagination"
 )
 
-// ErrNotImplemented indicates a failure to discover a feature of the response from the API.
-// E.g., a missing server field, a missing extension, etc.
-var ErrNotImplemented = fmt.Errorf("Compute Servers feature not implemented.")
+// ErrCannotInterpret is returned by an Extract call if the response body doesn't have the expected structure.
+var ErrCannotInterpet = errors.New("Unable to interpret a response body.")
 
 // Server exposes only the standard OpenStack fields corresponding to a given server on the user's account.
-//
-// Id uniquely identifies this server amongst all other servers, including those not accessible to the current tenant.
-//
-// TenantId identifies the tenant owning this server resource.
-//
-// UserId uniquely identifies the user account owning the tenant.
-//
-// Name contains the human-readable name for the server.
-//
-// Updated and Created contain ISO-8601 timestamps of when the state of the server last changed, and when it was created.
-//
-// Status contains the current operational status of the server, such as IN_PROGRESS or ACTIVE.
-//
-// Progress ranges from 0..100.  A request made against the server completes only once Progress reaches 100.
-//
-// AccessIPv4 and AccessIPv6 contain the IP addresses of the server, suitable for remote access for administration.
-//
-// Image refers to a JSON object, which itself indicates the OS image used to deploy the server.
-//
-// Flavor refers to a JSON object, which itself indicates the hardware configuration of the deployed server.
-//
-// Addresses includes a list of all IP addresses assigned to the server, keyed by pool.
-//
-// Metadata includes a list of all user-specified key-value pairs attached to the server.
-//
-// Links includes HTTP references to the itself, useful for passing along to other APIs that might want a server reference.
-//
-// 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.
 type Server struct {
-	Id         string
-	TenantId   string `mapstructure:tenant_id`
-	UserId     string `mapstructure:user_id`
-	Name       string
-	Updated    string
-	Created    string
-	HostId     string
-	Status     string
-	Progress   int
+	// ID uniquely identifies this server amongst all other servers, including those not accessible to the current tenant.
+	ID string
+
+	// TenantID identifies the tenant owning this server resource.
+	TenantID string `mapstructure:"tenant_id"`
+
+	// UserID uniquely identifies the user account owning the tenant.
+	UserID string `mapstructure:"user_id"`
+
+	// Name contains the human-readable name for the server.
+	Name string
+
+	// Updated and Created contain ISO-8601 timestamps of when the state of the server last changed, and when it was created.
+	Updated string
+	Created string
+
+	HostID string
+
+	// Status contains the current operational status of the server, such as IN_PROGRESS or ACTIVE.
+	Status string
+
+	// Progress ranges from 0..100.
+	// A request made against the server completes only once Progress reaches 100.
+	Progress int
+
+	// AccessIPv4 and AccessIPv6 contain the IP addresses of the server, suitable for remote access for administration.
 	AccessIPv4 string
 	AccessIPv6 string
-	Image      map[string]interface{}
-	Flavor     map[string]interface{}
-	Addresses  map[string]interface{}
-	Metadata   map[string]interface{}
-	Links      []interface{}
-	AdminPass  string `mapstructure:adminPass`
+
+	// Image refers to a JSON object, which itself indicates the OS image used to deploy the server.
+	Image map[string]interface{}
+
+	// Flavor refers to a JSON object, which itself indicates the hardware configuration of the deployed server.
+	Flavor map[string]interface{}
+
+	// Addresses includes a list of all IP addresses assigned to the server, keyed by pool.
+
+	Addresses map[string]interface{}
+
+	// Metadata includes a list of all user-specified key-value pairs attached to the server.
+	Metadata map[string]interface{}
+
+	// Links includes HTTP references to the itself, useful for passing along to other APIs that might want a server reference.
+	Links []interface{}
+
+	// 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"`
 }
 
-// GetServers interprets the result of a List() call, producing a slice of Server entities.
-func GetServers(lr ListResult) ([]Server, error) {
-	sa, ok := lr["servers"]
-	if !ok {
-		return nil, ErrNotImplemented
-	}
-	serversArray := sa.([]interface{})
-
-	servers := make([]Server, len(serversArray))
-	for i, so := range serversArray {
-		serverObj := so.(map[string]interface{})
-		err := mapstructure.Decode(serverObj, &servers[i])
-		if err != nil {
-			return servers, err
-		}
-	}
-
-	return servers, nil
+// ExtractServers interprets the results of a single page from a List() call, producing a slice of Server entities.
+func ExtractServers(page pagination.Page) ([]Server, error) {
+	casted := page.(ListResult).Body
+	var servers []Server
+	err := mapstructure.Decode(servers, casted)
+	return servers, err
 }
 
-// GetServer interprets the result of a call expected to return data on a single server.
-func GetServer(sr ServerResult) (*Server, error) {
+// ExtractServer interprets the result of a call expected to return data on a single server.
+func ExtractServer(sr ServerResult) (*Server, error) {
 	so, ok := sr["server"]
 	if !ok {
-		return nil, ErrNotImplemented
+		return nil, ErrCannotInterpet
 	}
 	serverObj := so.(map[string]interface{})
 	s := new(Server)
diff --git a/openstack/compute/v2/servers/urls.go b/openstack/compute/v2/servers/urls.go
new file mode 100644
index 0000000..3440de1
--- /dev/null
+++ b/openstack/compute/v2/servers/urls.go
@@ -0,0 +1,19 @@
+package servers
+
+import "github.com/rackspace/gophercloud"
+
+func getListURL(client *gophercloud.ServiceClient) string {
+	return client.ServiceURL("servers", "detail")
+}
+
+func getCreateURL(client *gophercloud.ServiceClient) string {
+	return client.ServiceURL("servers")
+}
+
+func getServerURL(client *gophercloud.ServiceClient, id string) string {
+	return client.ServiceURL("servers", id)
+}
+
+func getActionURL(client *gophercloud.ServiceClient, id string) string {
+	return client.ServiceURL("servers", id, "action")
+}