dsl struct tags; wip
diff --git a/openstack/identity/v3/services/requests.go b/openstack/identity/v3/services/requests.go
index 484afab..34bf979 100644
--- a/openstack/identity/v3/services/requests.go
+++ b/openstack/identity/v3/services/requests.go
@@ -5,21 +5,16 @@
 	"github.com/gophercloud/gophercloud/pagination"
 )
 
-type response struct {
-	Service Service `json:"service"`
-}
-
 // Create adds a new service of the requested type to the catalog.
 func Create(client *gophercloud.ServiceClient, serviceType string) CreateResult {
-	type request struct {
-		Type string `json:"type"`
-	}
+	var r CreateResult
+	b := map[string]string{"type": serviceType}
+	_, r.Err = client.Post(listURL(client), b, &r.Body, nil)
+	return r
+}
 
-	req := request{Type: serviceType}
-
-	var result CreateResult
-	_, result.Err = client.Post(listURL(client), req, &result.Body, nil)
-	return result
+type ListOptsBuilder interface {
+	ToServiceListMap() (string, error)
 }
 
 // ListOpts allows you to query the List method.
@@ -29,49 +24,45 @@
 	Page        int    `q:"page"`
 }
 
-// List enumerates the services available to a specific user.
-func List(client *gophercloud.ServiceClient, opts ListOpts) pagination.Pager {
-	u := listURL(client)
+func (opts ListOpts) ToServiceListMap() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return pagination.Pager{Err: err}
-	}
-	u += q.String()
-	createPage := func(r pagination.PageResult) pagination.Page {
-		return ServicePage{pagination.LinkedPageBase{PageResult: r}}
-	}
+	return q.String(), err
+}
 
-	return pagination.NewPager(client, u, createPage)
+// List enumerates the services available to a specific user.
+func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
+	u := listURL(client)
+	if opts != nil {
+		q, err := opts.ToServiceListMap()
+		if err != nil {
+			return pagination.Pager{Err: err}
+		}
+		u += q
+	}
+	return pagination.NewPager(client, u, func(r pagination.PageResult) pagination.Page {
+		return ServicePage{pagination.LinkedPageBase{PageResult: r}}
+	})
 }
 
 // Get returns additional information about a service, given its ID.
 func Get(client *gophercloud.ServiceClient, serviceID string) GetResult {
-	var result GetResult
-	_, result.Err = client.Get(serviceURL(client, serviceID), &result.Body, nil)
-	return result
+	var r GetResult
+	_, r.Err = client.Get(serviceURL(client, serviceID), &r.Body, nil)
+	return r
 }
 
 // Update changes the service type of an existing service.
 func Update(client *gophercloud.ServiceClient, serviceID string, serviceType string) UpdateResult {
-	type request struct {
-		Type string `json:"type"`
-	}
-
-	req := request{Type: serviceType}
-
-	var result UpdateResult
-	_, result.Err = client.Request("PATCH", serviceURL(client, serviceID), &gophercloud.RequestOpts{
-		JSONBody:     &req,
-		JSONResponse: &result.Body,
-		OkCodes:      []int{200},
-	})
-	return result
+	var r UpdateResult
+	b := map[string]string{"type": serviceType}
+	_, r.Err = client.Patch(serviceURL(client, serviceID), &b, &r.Body, nil)
+	return r
 }
 
 // Delete removes an existing service.
 // It either deletes all associated endpoints, or fails until all endpoints are deleted.
 func Delete(client *gophercloud.ServiceClient, serviceID string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = client.Delete(serviceURL(client, serviceID), nil)
-	return res
+	var r DeleteResult
+	_, r.Err = client.Delete(serviceURL(client, serviceID), nil)
+	return r
 }
diff --git a/openstack/identity/v3/services/requests_test.go b/openstack/identity/v3/services/requests_test.go
index ebe9e0f..aa19bcc 100644
--- a/openstack/identity/v3/services/requests_test.go
+++ b/openstack/identity/v3/services/requests_test.go
@@ -3,22 +3,21 @@
 import (
 	"fmt"
 	"net/http"
-	"reflect"
 	"testing"
 
 	"github.com/gophercloud/gophercloud/pagination"
-	"github.com/gophercloud/gophercloud/testhelper"
+	th "github.com/gophercloud/gophercloud/testhelper"
 	"github.com/gophercloud/gophercloud/testhelper/client"
 )
 
 func TestCreateSuccessful(t *testing.T) {
-	testhelper.SetupHTTP()
-	defer testhelper.TeardownHTTP()
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
 
-	testhelper.Mux.HandleFunc("/services", func(w http.ResponseWriter, r *http.Request) {
-		testhelper.TestMethod(t, r, "POST")
-		testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		testhelper.TestJSONRequest(t, r, `{ "type": "compute" }`)
+	th.Mux.HandleFunc("/services", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `{ "type": "compute" }`)
 
 		w.Header().Add("Content-Type", "application/json")
 		w.WriteHeader(http.StatusCreated)
@@ -32,32 +31,27 @@
     }`)
 	})
 
-	result, err := Create(client.ServiceClient(), "compute").Extract()
+	expected := &Service{
+		Description: "Here's your service",
+		ID:          "1234",
+		Name:        "InscrutableOpenStackProjectName",
+		Type:        "compute",
+	}
+
+	actual, err := Create(client.ServiceClient(), "compute").Extract()
 	if err != nil {
 		t.Fatalf("Unexpected error from Create: %v", err)
 	}
-
-	if result.Description == nil || *result.Description != "Here's your service" {
-		t.Errorf("Service description was unexpected [%s]", *result.Description)
-	}
-	if result.ID != "1234" {
-		t.Errorf("Service ID was unexpected [%s]", result.ID)
-	}
-	if result.Name != "InscrutableOpenStackProjectName" {
-		t.Errorf("Service name was unexpected [%s]", result.Name)
-	}
-	if result.Type != "compute" {
-		t.Errorf("Service type was unexpected [%s]", result.Type)
-	}
+	th.AssertDeepEquals(t, expected, actual)
 }
 
 func TestListSinglePage(t *testing.T) {
-	testhelper.SetupHTTP()
-	defer testhelper.TeardownHTTP()
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
 
-	testhelper.Mux.HandleFunc("/services", func(w http.ResponseWriter, r *http.Request) {
-		testhelper.TestMethod(t, r, "GET")
-		testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+	th.Mux.HandleFunc("/services", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
 
 		w.Header().Add("Content-Type", "application/json")
 		fmt.Fprintf(w, `
@@ -92,44 +86,34 @@
 			return false, err
 		}
 
-		desc0 := "Service One"
-		desc1 := "Service Two"
 		expected := []Service{
 			Service{
-				Description: &desc0,
+				Description: "Service One",
 				ID:          "1234",
 				Name:        "service-one",
 				Type:        "identity",
 			},
 			Service{
-				Description: &desc1,
+				Description: "Service Two",
 				ID:          "9876",
 				Name:        "service-two",
 				Type:        "compute",
 			},
 		}
-
-		if !reflect.DeepEqual(expected, actual) {
-			t.Errorf("Expected %#v, got %#v", expected, actual)
-		}
-
+		th.AssertDeepEquals(t, expected, actual)
 		return true, nil
 	})
-	if err != nil {
-		t.Errorf("Unexpected error while paging: %v", err)
-	}
-	if count != 1 {
-		t.Errorf("Expected 1 page, got %d", count)
-	}
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, 1, count)
 }
 
 func TestGetSuccessful(t *testing.T) {
-	testhelper.SetupHTTP()
-	defer testhelper.TeardownHTTP()
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
 
-	testhelper.Mux.HandleFunc("/services/12345", func(w http.ResponseWriter, r *http.Request) {
-		testhelper.TestMethod(t, r, "GET")
-		testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+	th.Mux.HandleFunc("/services/12345", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
 
 		w.Header().Add("Content-Type", "application/json")
 		fmt.Fprintf(w, `
@@ -144,33 +128,27 @@
 		`)
 	})
 
-	result, err := Get(client.ServiceClient(), "12345").Extract()
-	if err != nil {
-		t.Fatalf("Error fetching service information: %v", err)
+	actual, err := Get(client.ServiceClient(), "12345").Extract()
+	th.AssertNoErr(t, err)
+
+	expected := &Service{
+		ID:          "12345",
+		Description: "Service One",
+		Name:        "service-one",
+		Type:        "identity",
 	}
 
-	if result.ID != "12345" {
-		t.Errorf("Unexpected service ID: %s", result.ID)
-	}
-	if *result.Description != "Service One" {
-		t.Errorf("Unexpected service description: [%s]", *result.Description)
-	}
-	if result.Name != "service-one" {
-		t.Errorf("Unexpected service name: [%s]", result.Name)
-	}
-	if result.Type != "identity" {
-		t.Errorf("Unexpected service type: [%s]", result.Type)
-	}
+	th.AssertDeepEquals(t, expected, actual)
 }
 
 func TestUpdateSuccessful(t *testing.T) {
-	testhelper.SetupHTTP()
-	defer testhelper.TeardownHTTP()
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
 
-	testhelper.Mux.HandleFunc("/services/12345", func(w http.ResponseWriter, r *http.Request) {
-		testhelper.TestMethod(t, r, "PATCH")
-		testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-		testhelper.TestJSONRequest(t, r, `{ "type": "lasermagic" }`)
+	th.Mux.HandleFunc("/services/12345", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PATCH")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `{ "type": "lasermagic" }`)
 
 		w.Header().Add("Content-Type", "application/json")
 		fmt.Fprintf(w, `
@@ -183,27 +161,26 @@
 		`)
 	})
 
-	result, err := Update(client.ServiceClient(), "12345", "lasermagic").Extract()
-	if err != nil {
-		t.Fatalf("Unable to update service: %v", err)
+	expected := &Service{
+		ID:   "12345",
+		Type: "lasermagic",
 	}
 
-	if result.ID != "12345" {
-		t.Fatalf("Expected ID 12345, was %s", result.ID)
-	}
+	actual, err := Update(client.ServiceClient(), "12345", "lasermagic").Extract()
+	th.AssertNoErr(t, err)
+	th.AssertDeepEquals(t, expected, actual)
 }
 
 func TestDeleteSuccessful(t *testing.T) {
-	testhelper.SetupHTTP()
-	defer testhelper.TeardownHTTP()
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
 
-	testhelper.Mux.HandleFunc("/services/12345", func(w http.ResponseWriter, r *http.Request) {
-		testhelper.TestMethod(t, r, "DELETE")
-		testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID)
-
+	th.Mux.HandleFunc("/services/12345", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
 		w.WriteHeader(http.StatusNoContent)
 	})
 
 	res := Delete(client.ServiceClient(), "12345")
-	testhelper.AssertNoErr(t, res.Err)
+	th.AssertNoErr(t, res.Err)
 }
diff --git a/openstack/identity/v3/services/results.go b/openstack/identity/v3/services/results.go
index b0d36fb..9ebcc20 100644
--- a/openstack/identity/v3/services/results.go
+++ b/openstack/identity/v3/services/results.go
@@ -41,10 +41,10 @@
 
 // Service is the result of a list or information query.
 type Service struct {
-	Description *string `json:"description,omitempty"`
-	ID          string  `json:"id"`
-	Name        string  `json:"name"`
-	Type        string  `json:"type"`
+	Description string `json:"description`
+	ID          string `json:"id"`
+	Name        string `json:"name"`
+	Type        string `json:"type"`
 }
 
 // ServicePage is a single page of Service results.