dsl struct tags; wip
diff --git a/openstack/compute/v2/extensions/bootfromvolume/requests.go b/openstack/compute/v2/extensions/bootfromvolume/requests.go
index 7171afa..086cc0a 100644
--- a/openstack/compute/v2/extensions/bootfromvolume/requests.go
+++ b/openstack/compute/v2/extensions/bootfromvolume/requests.go
@@ -1,9 +1,6 @@
 package bootfromvolume
 
 import (
-	"fmt"
-	"strconv"
-
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
 )
@@ -12,37 +9,35 @@
 type SourceType string
 
 const (
-	Volume   SourceType = "volume"
+	// Volume SourceType
+	Volume SourceType = "volume"
+	// Snapshot SourceType
 	Snapshot SourceType = "snapshot"
-	Image    SourceType = "image"
-	Blank    SourceType = "blank"
+	// Image SourceType
+	Image SourceType = "image"
+	// Blank SourceType
+	Blank SourceType = "blank"
 )
 
 // BlockDevice is a structure with options for booting a server instance
 // from a volume. The volume may be created from an image, snapshot, or another
 // volume.
 type BlockDevice struct {
-	// BootIndex [optional] is the boot index. It defaults to 0.
+	// SourceType must be one of: "volume", "snapshot", "image".
+	SourceType SourceType `json:"source_type" required:"true"`
+	// UUID is the unique identifier for the volume, snapshot, or image (see above)
+	UUID string `json:"uuid,omitempty"`
+	// BootIndex is the boot index. It defaults to 0.
 	BootIndex int `json:"boot_index"`
-
-	// DeleteOnTermination [optional] specifies whether or not to delete the attached volume
+	// DeleteOnTermination specifies whether or not to delete the attached volume
 	// when the server is deleted. Defaults to `false`.
 	DeleteOnTermination bool `json:"delete_on_termination"`
-
-	// DestinationType [optional] is the type that gets created. Possible values are "volume"
+	// DestinationType is the type that gets created. Possible values are "volume"
 	// and "local".
-	DestinationType string `json:"destination_type"`
-
-	// GuestFormat [optional] specifies the format of the block device.
-	GuestFormat string `json:"guest_format"`
-
-	// SourceType [required] must be one of: "volume", "snapshot", "image".
-	SourceType SourceType `json:"source_type"`
-
-	// UUID [required] is the unique identifier for the volume, snapshot, or image (see above)
-	UUID string `json:"uuid"`
-
-	// VolumeSize [optional] is the size of the volume to create (in gigabytes).
+	DestinationType string `json:"destination_type,omitempty"`
+	// GuestFormat specifies the format of the block device.
+	GuestFormat string `json:"guest_format,omitempty"`
+	// VolumeSize is the size of the volume to create (in gigabytes).
 	VolumeSize int `json:"volume_size"`
 }
 
@@ -63,7 +58,6 @@
 
 	if len(opts.BlockDevice) == 0 {
 		err := gophercloud.ErrMissingInput{}
-		err.Function = "bootfromvolume.ToServerCreateMap"
 		err.Argument = "bootfromvolume.CreateOptsExt.BlockDevice"
 		return nil, err
 	}
@@ -73,29 +67,11 @@
 	blockDevice := make([]map[string]interface{}, len(opts.BlockDevice))
 
 	for i, bd := range opts.BlockDevice {
-		if string(bd.SourceType) == "" {
-			err := gophercloud.ErrMissingInput{}
-			err.Function = "bootfromvolume.ToServerCreateMap"
-			err.Argument = fmt.Sprintf("bootfromvolume.CreateOptsExt.BlockDevice[%d].SourceType", i)
+		b, err := gophercloud.BuildRequestBody(bd, "")
+		if err != nil {
 			return nil, err
 		}
-
-		blockDevice[i] = make(map[string]interface{})
-
-		blockDevice[i]["source_type"] = bd.SourceType
-		blockDevice[i]["boot_index"] = strconv.Itoa(bd.BootIndex)
-		blockDevice[i]["delete_on_termination"] = strconv.FormatBool(bd.DeleteOnTermination)
-		blockDevice[i]["volume_size"] = strconv.Itoa(bd.VolumeSize)
-		if bd.UUID != "" {
-			blockDevice[i]["uuid"] = bd.UUID
-		}
-		if bd.DestinationType != "" {
-			blockDevice[i]["destination_type"] = bd.DestinationType
-		}
-		if bd.GuestFormat != "" {
-			blockDevice[i]["guest_format"] = bd.GuestFormat
-		}
-
+		blockDevice[i] = b
 	}
 	serverMap["block_device_mapping_v2"] = blockDevice
 
@@ -104,21 +80,14 @@
 
 // Create requests the creation of a server from the given block device mapping.
 func Create(client *gophercloud.ServiceClient, opts servers.CreateOptsBuilder) servers.CreateResult {
-	var res servers.CreateResult
-
-	reqBody, err := opts.ToServerCreateMap()
+	var r servers.CreateResult
+	b, err := opts.ToServerCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return r
 	}
-
-	// Delete imageName and flavorName that come from ToServerCreateMap().
-	// As of Liberty, Boot From Volume is failing if they are passed.
-	delete(reqBody["server"].(map[string]interface{}), "imageName")
-	delete(reqBody["server"].(map[string]interface{}), "flavorName")
-
-	_, res.Err = client.Post(createURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 202},
 	})
-	return res
+	return r
 }
diff --git a/openstack/compute/v2/extensions/bootfromvolume/requests_test.go b/openstack/compute/v2/extensions/bootfromvolume/requests_test.go
index 2359a77..d85070d 100644
--- a/openstack/compute/v2/extensions/bootfromvolume/requests_test.go
+++ b/openstack/compute/v2/extensions/bootfromvolume/requests_test.go
@@ -18,10 +18,11 @@
 		CreateOptsBuilder: base,
 		BlockDevice: []BlockDevice{
 			BlockDevice{
-				UUID:            "123456",
-				SourceType:      Image,
-				DestinationType: "volume",
-				VolumeSize:      10,
+				UUID:                "123456",
+				SourceType:          Image,
+				DestinationType:     "volume",
+				VolumeSize:          10,
+				DeleteOnTermination: false,
 			},
 		},
 	}
@@ -37,9 +38,9 @@
             "uuid":"123456",
             "source_type":"image",
             "destination_type":"volume",
-            "boot_index": "0",
-            "delete_on_termination": "false",
-            "volume_size": "10"
+            "boot_index": 0,
+            "delete_on_termination": false,
+            "volume_size": 10
           }
         ]
       }
@@ -94,28 +95,28 @@
         "flavorRef": "performance1-1",
         "block_device_mapping_v2":[
           {
-            "boot_index": "0",
-            "delete_on_termination": "true",
+            "boot_index": 0,
+            "delete_on_termination": true,
             "destination_type":"local",
             "source_type":"image",
             "uuid":"123456",
-            "volume_size": "0"
+            "volume_size": 0
           },
           {
-            "boot_index": "-1",
-            "delete_on_termination": "true",
+            "boot_index": -1,
+            "delete_on_termination": true,
             "destination_type":"local",
             "guest_format":"ext4",
             "source_type":"blank",
-            "volume_size": "1"
+            "volume_size": 1
           },
           {
-            "boot_index": "-1",
-            "delete_on_termination": "true",
+            "boot_index": -1,
+            "delete_on_termination": true,
             "destination_type":"local",
             "guest_format":"ext4",
             "source_type":"blank",
-            "volume_size": "1"
+            "volume_size": 1
           }
         ]
       }
diff --git a/openstack/compute/v2/extensions/defsecrules/requests.go b/openstack/compute/v2/extensions/defsecrules/requests.go
index 5e2d686..62c112e 100644
--- a/openstack/compute/v2/extensions/defsecrules/requests.go
+++ b/openstack/compute/v2/extensions/defsecrules/requests.go
@@ -7,24 +7,19 @@
 
 // List will return a collection of default rules.
 func List(client *gophercloud.ServiceClient) pagination.Pager {
-	createPage := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, rootURL(client), func(r pagination.PageResult) pagination.Page {
 		return DefaultRulePage{pagination.SinglePageBase(r)}
-	}
-
-	return pagination.NewPager(client, rootURL(client), createPage)
+	})
 }
 
 // CreateOpts represents the configuration for adding a new default rule.
 type CreateOpts struct {
-	// Required - the lower bound of the port range that will be opened.
-	FromPort int `json:"from_port"`
-
-	// Required - the upper bound of the port range that will be opened.
-	ToPort int `json:"to_port"`
-
-	// Required - the protocol type that will be allowed, e.g. TCP.
-	IPProtocol string `json:"ip_protocol"`
-
+	// The lower bound of the port range that will be opened.
+	FromPort int `json:"from_port" required:"true"`
+	// The upper bound of the port range that will be opened.
+	ToPort int `json:"to_port" required:"true"`
+	// The protocol type that will be allowed, e.g. TCP.
+	IPProtocol string `json:"ip_protocol" required:"true"`
 	// ONLY required if FromGroupID is blank. This represents the IP range that
 	// will be the source of network traffic to your security group. Use
 	// 0.0.0.0/0 to allow all IP addresses.
@@ -38,68 +33,33 @@
 
 // ToRuleCreateMap builds the create rule options into a serializable format.
 func (opts CreateOpts) ToRuleCreateMap() (map[string]interface{}, error) {
-	if opts.FromPort == 0 {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "defsecrules.ToRuleCreateMap"
-		err.Argument = "defsecrules.CreateOpts.FromPort"
-		return nil, err
-	}
-	if opts.ToPort == 0 {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "defsecrules.ToRuleCreateMap"
-		err.Argument = "defsecrules.CreateOpts.ToPort"
-		return nil, err
-	}
-	if opts.IPProtocol == "" {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "defsecrules.ToRuleCreateMap"
-		err.Argument = "defsecrules.CreateOpts.IPProtocol"
-		return nil, err
-	}
-	if opts.CIDR == "" {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "defsecrules.ToRuleCreateMap"
-		err.Argument = "defsecrules.CreateOpts.CIDR"
-		return nil, err
-	}
-
-	rule := make(map[string]interface{})
-
-	rule["from_port"] = opts.FromPort
-	rule["to_port"] = opts.ToPort
-	rule["ip_protocol"] = opts.IPProtocol
-	rule["cidr"] = opts.CIDR
-
-	return map[string]interface{}{"security_group_default_rule": rule}, nil
+	return gophercloud.BuildRequestBody(opts, "security_group_default_rule")
 }
 
 // Create is the operation responsible for creating a new default rule.
 func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var result CreateResult
-
-	reqBody, err := opts.ToRuleCreateMap()
+	var r CreateResult
+	b, err := opts.ToRuleCreateMap()
 	if err != nil {
-		result.Err = err
-		return result
+		r.Err = err
+		return r
 	}
-
-	_, result.Err = client.Post(rootURL(client), reqBody, &result.Body, &gophercloud.RequestOpts{
+	_, r.Err = client.Post(rootURL(client), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-
-	return result
+	return r
 }
 
 // Get will return details for a particular default rule.
 func Get(client *gophercloud.ServiceClient, id string) GetResult {
-	var result GetResult
-	_, result.Err = client.Get(resourceURL(client, id), &result.Body, nil)
-	return result
+	var r GetResult
+	_, r.Err = client.Get(resourceURL(client, id), &r.Body, nil)
+	return r
 }
 
 // Delete will permanently delete a default rule from the project.
 func Delete(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult {
-	var result gophercloud.ErrResult
-	_, result.Err = client.Delete(resourceURL(client, id), nil)
-	return result
+	var r gophercloud.ErrResult
+	_, r.Err = client.Delete(resourceURL(client, id), nil)
+	return r
 }
diff --git a/openstack/compute/v2/extensions/diskconfig/requests.go b/openstack/compute/v2/extensions/diskconfig/requests.go
index 19798d7..41d04b9 100644
--- a/openstack/compute/v2/extensions/diskconfig/requests.go
+++ b/openstack/compute/v2/extensions/diskconfig/requests.go
@@ -50,16 +50,14 @@
 // RebuildOptsExt adds a DiskConfig option to the base RebuildOpts.
 type RebuildOptsExt struct {
 	servers.RebuildOptsBuilder
-
-	// DiskConfig [optional] controls how the rebuilt server's disk is partitioned.
-	DiskConfig DiskConfig
+	// DiskConfig controls how the rebuilt server's disk is partitioned.
+	DiskConfig DiskConfig `json:"OS-DCF:diskConfig,omitempty"`
 }
 
 // ToServerRebuildMap adds the diskconfig option to the base server rebuild options.
 func (opts RebuildOptsExt) ToServerRebuildMap() (map[string]interface{}, error) {
 	if opts.DiskConfig != Auto && opts.DiskConfig != Manual {
 		err := gophercloud.ErrInvalidInput{}
-		err.Function = "diskconfig.ToServerRebuildMap"
 		err.Argument = "diskconfig.RebuildOptsExt.DiskConfig"
 		err.Info = "Must be either diskconfig.Auto or diskconfig.Manual"
 		return nil, err
@@ -88,7 +86,6 @@
 func (opts ResizeOptsExt) ToServerResizeMap() (map[string]interface{}, error) {
 	if opts.DiskConfig != Auto && opts.DiskConfig != Manual {
 		err := gophercloud.ErrInvalidInput{}
-		err.Function = "diskconfig.ToServerResizeMap"
 		err.Argument = "diskconfig.ResizeOptsExt.DiskConfig"
 		err.Info = "Must be either diskconfig.Auto or diskconfig.Manual"
 		return nil, err
diff --git a/openstack/compute/v2/extensions/floatingips/requests.go b/openstack/compute/v2/extensions/floatingips/requests.go
index 00b2520..8137d76 100644
--- a/openstack/compute/v2/extensions/floatingips/requests.go
+++ b/openstack/compute/v2/extensions/floatingips/requests.go
@@ -21,158 +21,97 @@
 // CreateOpts specifies a Floating IP allocation request
 type CreateOpts struct {
 	// Pool is the pool of floating IPs to allocate one from
-	Pool string
-}
-
-// AssociateOpts specifies the required information to associate or disassociate a floating IP to an instance
-type AssociateOpts struct {
-	// ServerID is the UUID of the server
-	ServerID string
-
-	// FixedIP is an optional fixed IP address of the server
-	FixedIP string
-
-	// FloatingIP is the floating IP to associate with an instance
-	FloatingIP string
+	Pool string `json:"pool" required:"true"`
 }
 
 // ToFloatingIPCreateMap constructs a request body from CreateOpts.
 func (opts CreateOpts) ToFloatingIPCreateMap() (map[string]interface{}, error) {
-	if opts.Pool == "" {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "floatingips.ToFloatingIPCreateMap"
-		err.Argument = "floatingips.CreateOpts.Pool"
-		return nil, err
-	}
-
-	return map[string]interface{}{"pool": opts.Pool}, nil
-}
-
-// ToAssociateMap constructs a request body from AssociateOpts.
-func (opts AssociateOpts) ToAssociateMap() (map[string]interface{}, error) {
-	if opts.ServerID == "" {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "floatingips.ToAssociateMap"
-		err.Argument = "floatingips.AssociateOpts.ServerID"
-		return nil, err
-	}
-
-	if opts.FloatingIP == "" {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "floatingips.ToAssociateMap"
-		err.Argument = "floatingips.AssociateOpts.FloatingIP"
-		return nil, err
-	}
-
-	associateInfo := map[string]interface{}{
-		"serverId":   opts.ServerID,
-		"floatingIp": opts.FloatingIP,
-		"fixedIp":    opts.FixedIP,
-	}
-
-	return associateInfo, nil
-
+	return gophercloud.BuildRequestBody(opts, "")
 }
 
 // Create requests the creation of a new floating IP
 func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToFloatingIPCreateMap()
+	var r CreateResult
+	b, err := opts.ToFloatingIPCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return r
 	}
-
-	_, res.Err = client.Post(createURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-	return res
+	return r
 }
 
 // Get returns data about a previously created FloatingIP.
 func Get(client *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = client.Get(getURL(client, id), &res.Body, nil)
-	return res
+	var r GetResult
+	_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
+	return r
 }
 
 // Delete requests the deletion of a previous allocated FloatingIP.
 func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = client.Delete(deleteURL(client, id), nil)
-	return res
+	var r DeleteResult
+	_, r.Err = client.Delete(deleteURL(client, id), nil)
+	return r
 }
 
-// association / disassociation
+// AssociateOptsBuilder is the interface types must satfisfy to be used as
+// Associate options
+type AssociateOptsBuilder interface {
+	ToFloatingIPAssociateMap() (map[string]interface{}, error)
+}
 
-// Associate pairs an allocated floating IP with an instance
-// Deprecated. Use AssociateInstance.
-func Associate(client *gophercloud.ServiceClient, serverId, fip string) AssociateResult {
-	var res AssociateResult
+// AssociateOpts specifies the required information to associate a floating IP with an instance
+type AssociateOpts struct {
+	// FloatingIP is the floating IP to associate with an instance
+	FloatingIP string `json:"address" required:"true"`
+	// FixedIP is an optional fixed IP address of the server
+	FixedIP string `json:"fixed_address,omitempty"`
+}
 
-	addFloatingIp := make(map[string]interface{})
-	addFloatingIp["address"] = fip
-	reqBody := map[string]interface{}{"addFloatingIp": addFloatingIp}
-
-	_, res.Err = client.Post(associateURL(client, serverId), reqBody, nil, nil)
-	return res
+// ToFloatingIPAssociateMap constructs a request body from AssociateOpts.
+func (opts AssociateOpts) ToFloatingIPAssociateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "addFloatingIp")
 }
 
 // AssociateInstance pairs an allocated floating IP with an instance.
-func AssociateInstance(client *gophercloud.ServiceClient, opts AssociateOpts) AssociateResult {
-	var res AssociateResult
-
-	associateInfo, err := opts.ToAssociateMap()
+func AssociateInstance(client *gophercloud.ServiceClient, serverID string, opts AssociateOptsBuilder) AssociateResult {
+	var r AssociateResult
+	b, err := opts.ToFloatingIPAssociateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return r
 	}
-
-	addFloatingIp := make(map[string]interface{})
-	addFloatingIp["address"] = associateInfo["floatingIp"].(string)
-
-	// fixedIp is not required
-	if associateInfo["fixedIp"] != "" {
-		addFloatingIp["fixed_address"] = associateInfo["fixedIp"].(string)
-	}
-
-	serverId := associateInfo["serverId"].(string)
-
-	reqBody := map[string]interface{}{"addFloatingIp": addFloatingIp}
-	_, res.Err = client.Post(associateURL(client, serverId), reqBody, nil, nil)
-	return res
+	_, r.Err = client.Post(associateURL(client, serverID), b, nil, nil)
+	return r
 }
 
-// Disassociate decouples an allocated floating IP from an instance
-// Deprecated. Use DisassociateInstance.
-func Disassociate(client *gophercloud.ServiceClient, serverId, fip string) DisassociateResult {
-	var res DisassociateResult
+// DisassociateOptsBuilder is the interface types must satfisfy to be used as
+// Disassociate options
+type DisassociateOptsBuilder interface {
+	ToFloatingIPDisassociateMap() (map[string]interface{}, error)
+}
 
-	removeFloatingIp := make(map[string]interface{})
-	removeFloatingIp["address"] = fip
-	reqBody := map[string]interface{}{"removeFloatingIp": removeFloatingIp}
+// DisassociateOpts specifies the required information to disassociate a floating IP with an instance
+type DisassociateOpts struct {
+	FloatingIP string `json:"address" required:"true"`
+}
 
-	_, res.Err = client.Post(disassociateURL(client, serverId), reqBody, nil, nil)
-	return res
+// ToFloatingIPDisassociateMap constructs a request body from AssociateOpts.
+func (opts DisassociateOpts) ToFloatingIPDisassociateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "removeFloatingIp")
 }
 
 // DisassociateInstance decouples an allocated floating IP from an instance
-func DisassociateInstance(client *gophercloud.ServiceClient, opts AssociateOpts) DisassociateResult {
-	var res DisassociateResult
-
-	associateInfo, err := opts.ToAssociateMap()
+func DisassociateInstance(client *gophercloud.ServiceClient, serverID string, opts DisassociateOptsBuilder) DisassociateResult {
+	var r DisassociateResult
+	b, err := opts.ToFloatingIPDisassociateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return r
 	}
-
-	removeFloatingIp := make(map[string]interface{})
-	removeFloatingIp["address"] = associateInfo["floatingIp"].(string)
-	reqBody := map[string]interface{}{"removeFloatingIp": removeFloatingIp}
-
-	serverId := associateInfo["serverId"].(string)
-
-	_, res.Err = client.Post(disassociateURL(client, serverId), reqBody, nil, nil)
-	return res
+	_, r.Err = client.Post(disassociateURL(client, serverID), b, nil, nil)
+	return r
 }
diff --git a/openstack/compute/v2/extensions/floatingips/requests_test.go b/openstack/compute/v2/extensions/floatingips/requests_test.go
index 705c92f..f0e9560 100644
--- a/openstack/compute/v2/extensions/floatingips/requests_test.go
+++ b/openstack/compute/v2/extensions/floatingips/requests_test.go
@@ -57,28 +57,16 @@
 	th.AssertNoErr(t, err)
 }
 
-func TestAssociateDeprecated(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleAssociateSuccessfully(t)
-	serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
-	fip := "10.10.10.2"
-
-	err := Associate(client.ServiceClient(), serverId, fip).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
 func TestAssociate(t *testing.T) {
 	th.SetupHTTP()
 	defer th.TeardownHTTP()
 	HandleAssociateSuccessfully(t)
 
 	associateOpts := AssociateOpts{
-		ServerID:   "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
 		FloatingIP: "10.10.10.2",
 	}
 
-	err := AssociateInstance(client.ServiceClient(), associateOpts).ExtractErr()
+	err := AssociateInstance(client.ServiceClient(), "4d8c3732-a248-40ed-bebc-539a6ffd25c0", associateOpts).ExtractErr()
 	th.AssertNoErr(t, err)
 }
 
@@ -88,23 +76,11 @@
 	HandleAssociateFixedSuccessfully(t)
 
 	associateOpts := AssociateOpts{
-		ServerID:   "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
 		FloatingIP: "10.10.10.2",
 		FixedIP:    "166.78.185.201",
 	}
 
-	err := AssociateInstance(client.ServiceClient(), associateOpts).ExtractErr()
-	th.AssertNoErr(t, err)
-}
-
-func TestDisassociateDeprecated(t *testing.T) {
-	th.SetupHTTP()
-	defer th.TeardownHTTP()
-	HandleDisassociateSuccessfully(t)
-	serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
-	fip := "10.10.10.2"
-
-	err := Disassociate(client.ServiceClient(), serverId, fip).ExtractErr()
+	err := AssociateInstance(client.ServiceClient(), "4d8c3732-a248-40ed-bebc-539a6ffd25c0", associateOpts).ExtractErr()
 	th.AssertNoErr(t, err)
 }
 
@@ -113,11 +89,10 @@
 	defer th.TeardownHTTP()
 	HandleDisassociateSuccessfully(t)
 
-	associateOpts := AssociateOpts{
-		ServerID:   "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
+	disassociateOpts := DisassociateOpts{
 		FloatingIP: "10.10.10.2",
 	}
 
-	err := DisassociateInstance(client.ServiceClient(), associateOpts).ExtractErr()
+	err := DisassociateInstance(client.ServiceClient(), "4d8c3732-a248-40ed-bebc-539a6ffd25c0", disassociateOpts).ExtractErr()
 	th.AssertNoErr(t, err)
 }
diff --git a/openstack/compute/v2/extensions/keypairs/requests.go b/openstack/compute/v2/extensions/keypairs/requests.go
index b9b32ee..2a01084 100644
--- a/openstack/compute/v2/extensions/keypairs/requests.go
+++ b/openstack/compute/v2/extensions/keypairs/requests.go
@@ -45,59 +45,43 @@
 
 // CreateOpts specifies keypair creation or import parameters.
 type CreateOpts struct {
-	// Name [required] is a friendly name to refer to this KeyPair in other services.
-	Name string
-
+	// Name is a friendly name to refer to this KeyPair in other services.
+	Name string `json:"name" required:"true"`
 	// PublicKey [optional] is a pregenerated OpenSSH-formatted public key. If provided, this key
 	// will be imported and no new key will be created.
-	PublicKey string
+	PublicKey string `json:"public_key,omitempty"`
 }
 
 // ToKeyPairCreateMap constructs a request body from CreateOpts.
 func (opts CreateOpts) ToKeyPairCreateMap() (map[string]interface{}, error) {
-	if opts.Name == "" {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "keypairs.ToKeyPairCreateMap"
-		err.Argument = "keypairs.CreateOpts.Name"
-		return nil, err
-	}
-
-	keypair := make(map[string]interface{})
-	keypair["name"] = opts.Name
-	if opts.PublicKey != "" {
-		keypair["public_key"] = opts.PublicKey
-	}
-
-	return map[string]interface{}{"keypair": keypair}, nil
+	return gophercloud.BuildRequestBody(opts, "keypair")
 }
 
 // Create requests the creation of a new keypair on the server, or to import a pre-existing
 // keypair.
 func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToKeyPairCreateMap()
+	var r CreateResult
+	b, err := opts.ToKeyPairCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return r
 	}
-
-	_, res.Err = client.Post(createURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-	return res
+	return r
 }
 
 // Get returns public data about a previously uploaded KeyPair.
 func Get(client *gophercloud.ServiceClient, name string) GetResult {
-	var res GetResult
-	_, res.Err = client.Get(getURL(client, name), &res.Body, nil)
-	return res
+	var r GetResult
+	_, r.Err = client.Get(getURL(client, name), &r.Body, nil)
+	return r
 }
 
 // Delete requests the deletion of a previous stored KeyPair from the server.
 func Delete(client *gophercloud.ServiceClient, name string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = client.Delete(deleteURL(client, name), nil)
-	return res
+	var r DeleteResult
+	_, r.Err = client.Delete(deleteURL(client, name), nil)
+	return r
 }
diff --git a/openstack/compute/v2/extensions/networks/requests.go b/openstack/compute/v2/extensions/networks/requests.go
index a505733..6f0f182 100644
--- a/openstack/compute/v2/extensions/networks/requests.go
+++ b/openstack/compute/v2/extensions/networks/requests.go
@@ -7,16 +7,14 @@
 
 // List returns a Pager that allows you to iterate over a collection of Network.
 func List(client *gophercloud.ServiceClient) pagination.Pager {
-	url := listURL(client)
-	createPage := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page {
 		return NetworkPage{pagination.SinglePageBase(r)}
-	}
-	return pagination.NewPager(client, url, createPage)
+	})
 }
 
 // Get returns data about a previously created Network.
 func Get(client *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = client.Get(getURL(client, id), &res.Body, nil)
-	return res
+	var r GetResult
+	_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
+	return r
 }
diff --git a/openstack/compute/v2/extensions/schedulerhints/requests.go b/openstack/compute/v2/extensions/schedulerhints/requests.go
index 5713e72..1e06b46 100644
--- a/openstack/compute/v2/extensions/schedulerhints/requests.go
+++ b/openstack/compute/v2/extensions/schedulerhints/requests.go
@@ -14,33 +14,28 @@
 type SchedulerHints struct {
 	// Group specifies a Server Group to place the instance in.
 	Group string
-
 	// DifferentHost will place the instance on a compute node that does not
 	// host the given instances.
 	DifferentHost []string
-
 	// SameHost will place the instance on a compute node that hosts the given
 	// instances.
 	SameHost []string
-
 	// Query is a conditional statement that results in compute nodes able to
 	// host the instance.
 	Query []interface{}
-
 	// TargetCell specifies a cell name where the instance will be placed.
-	TargetCell string
-
+	TargetCell string `json:"target_cell,omitempty"`
 	// BuildNearHostIP specifies a subnet of compute nodes to host the instance.
 	BuildNearHostIP string
 }
 
-// SchedulerHintsBuilder builds the scheduler hints into a serializable format.
-type SchedulerHintsBuilder interface {
-	ToServerSchedulerHintsMap() (map[string]interface{}, error)
+// CreateOptsBuilder builds the scheduler hints into a serializable format.
+type CreateOptsBuilder interface {
+	ToServerSchedulerHintsCreateMap() (map[string]interface{}, error)
 }
 
 // ToServerSchedulerHintsMap builds the scheduler hints into a serializable format.
-func (opts SchedulerHints) ToServerSchedulerHintsMap() (map[string]interface{}, error) {
+func (opts SchedulerHints) ToServerSchedulerHintsCreateMap() (map[string]interface{}, error) {
 	sh := make(map[string]interface{})
 
 	uuidRegex, _ := regexp.Compile("^[a-z0-9]{8}-[a-z0-9]{4}-[1-5][a-z0-9]{3}-[a-z0-9]{4}-[a-z0-9]{12}$")
@@ -48,7 +43,6 @@
 	if opts.Group != "" {
 		if !uuidRegex.MatchString(opts.Group) {
 			err := gophercloud.ErrInvalidInput{}
-			err.Function = "schedulerhints.ToServerSchedulerHintsMap"
 			err.Argument = "schedulerhints.SchedulerHints.Group"
 			err.Value = opts.Group
 			err.Info = "Group must be a UUID"
@@ -61,7 +55,6 @@
 		for _, diffHost := range opts.DifferentHost {
 			if !uuidRegex.MatchString(diffHost) {
 				err := gophercloud.ErrInvalidInput{}
-				err.Function = "schedulerhints.ToServerSchedulerHintsMap"
 				err.Argument = "schedulerhints.SchedulerHints.DifferentHost"
 				err.Value = opts.DifferentHost
 				err.Info = "The hosts must be in UUID format."
@@ -75,7 +68,6 @@
 		for _, sameHost := range opts.SameHost {
 			if !uuidRegex.MatchString(sameHost) {
 				err := gophercloud.ErrInvalidInput{}
-				err.Function = "schedulerhints.ToServerSchedulerHintsMap"
 				err.Argument = "schedulerhints.SchedulerHints.SameHost"
 				err.Value = opts.SameHost
 				err.Info = "The hosts must be in UUID format."
@@ -99,7 +91,6 @@
 	if len(opts.Query) > 0 {
 		if len(opts.Query) < 3 {
 			err := gophercloud.ErrInvalidInput{}
-			err.Function = "schedulerhints.ToServerSchedulerHintsMap"
 			err.Argument = "schedulerhints.SchedulerHints.Query"
 			err.Value = opts.Query
 			err.Info = "Must be a conditional statement in the format of [op,variable,value]"
@@ -115,7 +106,6 @@
 	if opts.BuildNearHostIP != "" {
 		if _, _, err := net.ParseCIDR(opts.BuildNearHostIP); err != nil {
 			err := gophercloud.ErrInvalidInput{}
-			err.Function = "schedulerhints.ToServerSchedulerHintsMap"
 			err.Argument = "schedulerhints.SchedulerHints.BuildNearHostIP"
 			err.Value = opts.BuildNearHostIP
 			err.Info = "Must be a valid subnet in the form 192.168.1.1/24"
@@ -132,9 +122,8 @@
 // CreateOptsExt adds a SchedulerHints option to the base CreateOpts.
 type CreateOptsExt struct {
 	servers.CreateOptsBuilder
-
 	// SchedulerHints provides a set of hints to the scheduler.
-	SchedulerHints SchedulerHintsBuilder
+	SchedulerHints CreateOptsBuilder
 }
 
 // ToServerCreateMap adds the SchedulerHints option to the base server creation options.
@@ -144,7 +133,7 @@
 		return nil, err
 	}
 
-	schedulerHints, err := opts.SchedulerHints.ToServerSchedulerHintsMap()
+	schedulerHints, err := opts.SchedulerHints.ToServerSchedulerHintsCreateMap()
 	if err != nil {
 		return nil, err
 	}
diff --git a/openstack/compute/v2/extensions/secgroups/requests.go b/openstack/compute/v2/extensions/secgroups/requests.go
index a0bb025..48ff698 100644
--- a/openstack/compute/v2/extensions/secgroups/requests.go
+++ b/openstack/compute/v2/extensions/secgroups/requests.go
@@ -6,11 +6,9 @@
 )
 
 func commonList(client *gophercloud.ServiceClient, url string) pagination.Pager {
-	createPage := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
 		return SecurityGroupPage{pagination.SinglePageBase(r)}
-	}
-
-	return pagination.NewPager(client, url, createPage)
+	})
 }
 
 // List will return a collection of all the security groups for a particular
@@ -29,11 +27,10 @@
 // security groups. It therefore represents the mutable attributes of a
 // security group.
 type GroupOpts struct {
-	// Required - the name of your security group.
-	Name string `json:"name"`
-
-	// Required - the description of your security group.
-	Description string `json:"description"`
+	// the name of your security group.
+	Name string `json:"name" required:"true"`
+	// the description of your security group.
+	Description string `json:"description" required:"true"`
 }
 
 // CreateOpts is the struct responsible for creating a security group.
@@ -46,42 +43,21 @@
 
 // ToSecGroupCreateMap builds the create options into a serializable format.
 func (opts CreateOpts) ToSecGroupCreateMap() (map[string]interface{}, error) {
-	sg := make(map[string]interface{})
-
-	if opts.Name == "" {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "secgroups.ToSecGroupCreateMap"
-		err.Argument = "secgroups.CreateOpts.Name"
-		return nil, err
-	}
-	if opts.Description == "" {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "secgroups.ToSecGroupCreateMap"
-		err.Argument = "secgroups.CreateOpts.Description"
-		return nil, err
-	}
-
-	sg["name"] = opts.Name
-	sg["description"] = opts.Description
-
-	return map[string]interface{}{"security_group": sg}, nil
+	return gophercloud.BuildRequestBody(opts, "security_group")
 }
 
 // Create will create a new security group.
 func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var result CreateResult
-
-	reqBody, err := opts.ToSecGroupCreateMap()
+	var r CreateResult
+	b, err := opts.ToSecGroupCreateMap()
 	if err != nil {
-		result.Err = err
-		return result
+		r.Err = err
+		return r
 	}
-
-	_, result.Err = client.Post(rootURL(client), reqBody, &result.Body, &gophercloud.RequestOpts{
+	_, r.Err = client.Post(rootURL(client), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-
-	return result
+	return r
 }
 
 // UpdateOpts is the struct responsible for updating an existing security group.
@@ -94,79 +70,53 @@
 
 // ToSecGroupUpdateMap builds the update options into a serializable format.
 func (opts UpdateOpts) ToSecGroupUpdateMap() (map[string]interface{}, error) {
-	sg := make(map[string]interface{})
-
-	if opts.Name == "" {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "secgroups.ToSecGroupUpdateMap"
-		err.Argument = "secgroups.UpdateOpts.Name"
-		return nil, err
-	}
-	if opts.Description == "" {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "secgroups.ToSecGroupUpdateMap"
-		err.Argument = "secgroups.UpdateOpts.Description"
-		return nil, err
-	}
-
-	sg["name"] = opts.Name
-	sg["description"] = opts.Description
-
-	return map[string]interface{}{"security_group": sg}, nil
+	return gophercloud.BuildRequestBody(opts, "security_group")
 }
 
 // Update will modify the mutable properties of a security group, notably its
 // name and description.
 func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
-	var result UpdateResult
-
-	reqBody, err := opts.ToSecGroupUpdateMap()
+	var r UpdateResult
+	b, err := opts.ToSecGroupUpdateMap()
 	if err != nil {
-		result.Err = err
-		return result
+		r.Err = err
+		return r
 	}
-
-	_, result.Err = client.Put(resourceURL(client, id), reqBody, &result.Body, &gophercloud.RequestOpts{
+	_, r.Err = client.Put(resourceURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-
-	return result
+	return r
 }
 
 // Get will return details for a particular security group.
 func Get(client *gophercloud.ServiceClient, id string) GetResult {
-	var result GetResult
-	_, result.Err = client.Get(resourceURL(client, id), &result.Body, nil)
-	return result
+	var r GetResult
+	_, r.Err = client.Get(resourceURL(client, id), &r.Body, nil)
+	return r
 }
 
 // Delete will permanently delete a security group from the project.
 func Delete(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult {
-	var result gophercloud.ErrResult
-	_, result.Err = client.Delete(resourceURL(client, id), nil)
-	return result
+	var r gophercloud.ErrResult
+	_, r.Err = client.Delete(resourceURL(client, id), nil)
+	return r
 }
 
 // CreateRuleOpts represents the configuration for adding a new rule to an
 // existing security group.
 type CreateRuleOpts struct {
 	// Required - the ID of the group that this rule will be added to.
-	ParentGroupID string `json:"parent_group_id"`
-
+	ParentGroupID string `json:"parent_group_id" required:"true"`
 	// Required - the lower bound of the port range that will be opened.
-	FromPort int `json:"from_port"`
-
+	FromPort int `json:"from_port" required:"true"`
 	// Required - the upper bound of the port range that will be opened.
-	ToPort int `json:"to_port"`
-
+	ToPort int `json:"to_port" required:"true"`
 	// Required - the protocol type that will be allowed, e.g. TCP.
-	IPProtocol string `json:"ip_protocol"`
-
+	IPProtocol string `json:"ip_protocol" required:"true"`
 	// ONLY required if FromGroupID is blank. This represents the IP range that
 	// will be the source of network traffic to your security group. Use
 	// 0.0.0.0/0 to allow all IP addresses.
 	CIDR string `json:"cidr,omitempty"`
-
 	// ONLY required if CIDR is blank. This value represents the ID of a group
 	// that forwards traffic to the parent group. So, instead of accepting
 	// network traffic from an entire IP range, you can instead refine the
@@ -181,78 +131,30 @@
 
 // ToRuleCreateMap builds the create rule options into a serializable format.
 func (opts CreateRuleOpts) ToRuleCreateMap() (map[string]interface{}, error) {
-	if opts.ParentGroupID == "" {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "secgroups.ToRuleCreateMap"
-		err.Argument = "secgroups.CreateRuleOpts.ParentGroupID"
-		return nil, err
-	}
-	if opts.FromPort == 0 {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "secgroups.ToRuleCreateMap"
-		err.Argument = "secgroups.CreateRuleOpts.FromPort"
-		return nil, err
-	}
-	if opts.ToPort == 0 {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "secgroups.ToRuleCreateMap"
-		err.Argument = "secgroups.CreateRuleOpts.ToPort"
-		return nil, err
-	}
-	if opts.IPProtocol == "" {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "secgroups.ToRuleCreateMap"
-		err.Argument = "secgroups.CreateRuleOpts.IPProtocol"
-		return nil, err
-	}
-	if opts.CIDR == "" && opts.FromGroupID == "" {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "secgroups.ToRuleCreateMap"
-		err.Argument = "secgroups.CreateRuleOpts.CIDR/secgroups.CreateRuleOpts.FromGroupID"
-		return nil, err
-	}
-
-	rule := make(map[string]interface{})
-
-	rule["parent_group_id"] = opts.ParentGroupID
-	rule["from_port"] = opts.FromPort
-	rule["to_port"] = opts.ToPort
-	rule["ip_protocol"] = opts.IPProtocol
-
-	if opts.CIDR != "" {
-		rule["cidr"] = opts.CIDR
-	}
-	if opts.FromGroupID != "" {
-		rule["group_id"] = opts.FromGroupID
-	}
-
-	return map[string]interface{}{"security_group_rule": rule}, nil
+	return gophercloud.BuildRequestBody(opts, "security_group_rule")
 }
 
 // CreateRule will add a new rule to an existing security group (whose ID is
 // specified in CreateRuleOpts). You have the option of controlling inbound
 // traffic from either an IP range (CIDR) or from another security group.
 func CreateRule(client *gophercloud.ServiceClient, opts CreateRuleOptsBuilder) CreateRuleResult {
-	var result CreateRuleResult
-
-	reqBody, err := opts.ToRuleCreateMap()
+	var r CreateRuleResult
+	b, err := opts.ToRuleCreateMap()
 	if err != nil {
-		result.Err = err
-		return result
+		r.Err = err
+		return r
 	}
-
-	_, result.Err = client.Post(rootRuleURL(client), reqBody, &result.Body, &gophercloud.RequestOpts{
+	_, r.Err = client.Post(rootRuleURL(client), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-
-	return result
+	return r
 }
 
 // DeleteRule will permanently delete a rule from a security group.
 func DeleteRule(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult {
-	var result gophercloud.ErrResult
-	_, result.Err = client.Delete(resourceRuleURL(client, id), nil)
-	return result
+	var r gophercloud.ErrResult
+	_, r.Err = client.Delete(resourceRuleURL(client, id), nil)
+	return r
 }
 
 func actionMap(prefix, groupName string) map[string]map[string]string {
@@ -261,17 +163,17 @@
 	}
 }
 
-// AddServerToGroup will associate a server and a security group, enforcing the
+// AddServer will associate a server and a security group, enforcing the
 // rules of the group on the server.
-func AddServerToGroup(client *gophercloud.ServiceClient, serverID, groupName string) gophercloud.ErrResult {
-	var result gophercloud.ErrResult
-	_, result.Err = client.Post(serverActionURL(client, serverID), actionMap("add", groupName), &result.Body, nil)
-	return result
+func AddServer(client *gophercloud.ServiceClient, serverID, groupName string) gophercloud.ErrResult {
+	var r gophercloud.ErrResult
+	_, r.Err = client.Post(serverActionURL(client, serverID), actionMap("add", groupName), &r.Body, nil)
+	return r
 }
 
-// RemoveServerFromGroup will disassociate a server from a security group.
-func RemoveServerFromGroup(client *gophercloud.ServiceClient, serverID, groupName string) gophercloud.ErrResult {
-	var result gophercloud.ErrResult
-	_, result.Err = client.Post(serverActionURL(client, serverID), actionMap("remove", groupName), &result.Body, nil)
-	return result
+// RemoveServer will disassociate a server from a security group.
+func RemoveServer(client *gophercloud.ServiceClient, serverID, groupName string) gophercloud.ErrResult {
+	var r gophercloud.ErrResult
+	_, r.Err = client.Post(serverActionURL(client, serverID), actionMap("remove", groupName), &r.Body, nil)
+	return r
 }
diff --git a/openstack/compute/v2/extensions/secgroups/requests_test.go b/openstack/compute/v2/extensions/secgroups/requests_test.go
index 59a3898..9496d4a 100644
--- a/openstack/compute/v2/extensions/secgroups/requests_test.go
+++ b/openstack/compute/v2/extensions/secgroups/requests_test.go
@@ -233,7 +233,7 @@
 
 	mockAddServerToGroupResponse(t, serverID)
 
-	err := AddServerToGroup(client.ServiceClient(), serverID, "test").ExtractErr()
+	err := AddServer(client.ServiceClient(), serverID, "test").ExtractErr()
 	th.AssertNoErr(t, err)
 }
 
@@ -243,6 +243,6 @@
 
 	mockRemoveServerFromGroupResponse(t, serverID)
 
-	err := RemoveServerFromGroup(client.ServiceClient(), serverID, "test").ExtractErr()
+	err := RemoveServer(client.ServiceClient(), serverID, "test").ExtractErr()
 	th.AssertNoErr(t, err)
 }
diff --git a/openstack/compute/v2/extensions/servergroups/requests.go b/openstack/compute/v2/extensions/servergroups/requests.go
index 212edac..1348cfa 100644
--- a/openstack/compute/v2/extensions/servergroups/requests.go
+++ b/openstack/compute/v2/extensions/servergroups/requests.go
@@ -21,61 +21,40 @@
 // CreateOpts specifies a Server Group allocation request
 type CreateOpts struct {
 	// Name is the name of the server group
-	Name string
-
+	Name string `json:"name" required:"true"`
 	// Policies are the server group policies
-	Policies []string
+	Policies []string `json:"policies" required:"true"`
 }
 
 // ToServerGroupCreateMap constructs a request body from CreateOpts.
 func (opts CreateOpts) ToServerGroupCreateMap() (map[string]interface{}, error) {
-	if opts.Name == "" {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "servergroups.ToServerGroupCreateMap"
-		err.Argument = "servergroups.CreateOpts.Name"
-		return nil, err
-	}
-
-	if len(opts.Policies) < 1 {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "servergroups.ToServerGroupCreateMap"
-		err.Argument = "servergroups.CreateOpts.Policies"
-		return nil, err
-	}
-
-	serverGroup := make(map[string]interface{})
-	serverGroup["name"] = opts.Name
-	serverGroup["policies"] = opts.Policies
-
-	return map[string]interface{}{"server_group": serverGroup}, nil
+	return gophercloud.BuildRequestBody(opts, "server_group")
 }
 
 // Create requests the creation of a new Server Group
 func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToServerGroupCreateMap()
+	var r CreateResult
+	b, err := opts.ToServerGroupCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return r
 	}
-
-	_, res.Err = client.Post(createURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-	return res
+	return r
 }
 
 // Get returns data about a previously created ServerGroup.
 func Get(client *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = client.Get(getURL(client, id), &res.Body, nil)
-	return res
+	var r GetResult
+	_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
+	return r
 }
 
 // Delete requests the deletion of a previously allocated ServerGroup.
 func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = client.Delete(deleteURL(client, id), nil)
-	return res
+	var r DeleteResult
+	_, r.Err = client.Delete(deleteURL(client, id), nil)
+	return r
 }
diff --git a/openstack/compute/v2/extensions/startstop/requests.go b/openstack/compute/v2/extensions/startstop/requests.go
index 8364487..460223a 100644
--- a/openstack/compute/v2/extensions/startstop/requests.go
+++ b/openstack/compute/v2/extensions/startstop/requests.go
@@ -8,16 +8,14 @@
 
 // Start is the operation responsible for starting a Compute server.
 func Start(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult {
-	var res gophercloud.ErrResult
-	reqBody := map[string]interface{}{"os-start": nil}
-	_, res.Err = client.Post(actionURL(client, id), reqBody, nil, nil)
-	return res
+	var r gophercloud.ErrResult
+	_, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"os-start": nil}, nil, nil)
+	return r
 }
 
 // Stop is the operation responsible for stopping a Compute server.
 func Stop(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult {
-	var res gophercloud.ErrResult
-	reqBody := map[string]interface{}{"os-stop": nil}
-	_, res.Err = client.Post(actionURL(client, id), reqBody, nil, nil)
-	return res
+	var r gophercloud.ErrResult
+	_, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"os-stop": nil}, nil, nil)
+	return r
 }
diff --git a/openstack/compute/v2/extensions/tenantnetworks/requests.go b/openstack/compute/v2/extensions/tenantnetworks/requests.go
index 68159f2..ca33c37 100644
--- a/openstack/compute/v2/extensions/tenantnetworks/requests.go
+++ b/openstack/compute/v2/extensions/tenantnetworks/requests.go
@@ -7,16 +7,14 @@
 
 // List returns a Pager that allows you to iterate over a collection of Network.
 func List(client *gophercloud.ServiceClient) pagination.Pager {
-	url := listURL(client)
-	createPage := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page {
 		return NetworkPage{pagination.SinglePageBase(r)}
-	}
-	return pagination.NewPager(client, url, createPage)
+	})
 }
 
 // Get returns data about a previously created Network.
 func Get(client *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = client.Get(getURL(client, id), &res.Body, nil)
-	return res
+	var r GetResult
+	_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
+	return r
 }
diff --git a/openstack/compute/v2/extensions/volumeattach/requests.go b/openstack/compute/v2/extensions/volumeattach/requests.go
index c2bc2ee..c8ef930 100644
--- a/openstack/compute/v2/extensions/volumeattach/requests.go
+++ b/openstack/compute/v2/extensions/volumeattach/requests.go
@@ -6,8 +6,8 @@
 )
 
 // List returns a Pager that allows you to iterate over a collection of VolumeAttachments.
-func List(client *gophercloud.ServiceClient, serverId string) pagination.Pager {
-	return pagination.NewPager(client, listURL(client, serverId), func(r pagination.PageResult) pagination.Page {
+func List(client *gophercloud.ServiceClient, serverID string) pagination.Pager {
+	return pagination.NewPager(client, listURL(client, serverID), func(r pagination.PageResult) pagination.Page {
 		return VolumeAttachmentPage{pagination.SinglePageBase(r)}
 	})
 }
@@ -21,56 +21,40 @@
 // CreateOpts specifies volume attachment creation or import parameters.
 type CreateOpts struct {
 	// Device is the device that the volume will attach to the instance as. Omit for "auto"
-	Device string
-
+	Device string `json:"device,omitempty"`
 	// VolumeID is the ID of the volume to attach to the instance
-	VolumeID string
+	VolumeID string `json:"volumeId" required:"true"`
 }
 
 // ToVolumeAttachmentCreateMap constructs a request body from CreateOpts.
 func (opts CreateOpts) ToVolumeAttachmentCreateMap() (map[string]interface{}, error) {
-	if opts.VolumeID == "" {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "volumeattach.ToVolumeAttachmentCreateMap"
-		err.Argument = "volumeattach.CreateOpts.VolumeID"
-		return nil, err
-	}
-
-	volumeAttachment := make(map[string]interface{})
-	volumeAttachment["volumeId"] = opts.VolumeID
-	if opts.Device != "" {
-		volumeAttachment["device"] = opts.Device
-	}
-
-	return map[string]interface{}{"volumeAttachment": volumeAttachment}, nil
+	return gophercloud.BuildRequestBody(opts, "volumeAttachment")
 }
 
 // Create requests the creation of a new volume attachment on the server
-func Create(client *gophercloud.ServiceClient, serverId string, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToVolumeAttachmentCreateMap()
+func Create(client *gophercloud.ServiceClient, serverID string, opts CreateOptsBuilder) CreateResult {
+	var r CreateResult
+	b, err := opts.ToVolumeAttachmentCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return r
 	}
-
-	_, res.Err = client.Post(createURL(client, serverId), reqBody, &res.Body, &gophercloud.RequestOpts{
+	_, r.Err = client.Post(createURL(client, serverID), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-	return res
+	return r
 }
 
 // Get returns public data about a previously created VolumeAttachment.
-func Get(client *gophercloud.ServiceClient, serverId, aId string) GetResult {
-	var res GetResult
-	_, res.Err = client.Get(getURL(client, serverId, aId), &res.Body, nil)
-	return res
+func Get(client *gophercloud.ServiceClient, serverID, attachmentID string) GetResult {
+	var r GetResult
+	_, r.Err = client.Get(getURL(client, serverID, attachmentID), &r.Body, nil)
+	return r
 }
 
 // Delete requests the deletion of a previous stored VolumeAttachment from the server.
-func Delete(client *gophercloud.ServiceClient, serverId, aId string) DeleteResult {
-	var res DeleteResult
-	_, res.Err = client.Delete(deleteURL(client, serverId, aId), nil)
-	return res
+func Delete(client *gophercloud.ServiceClient, serverID, attachmentID string) DeleteResult {
+	var r DeleteResult
+	_, r.Err = client.Delete(deleteURL(client, serverID, attachmentID), nil)
+	return r
 }
diff --git a/openstack/compute/v2/flavors/requests.go b/openstack/compute/v2/flavors/requests.go
index 6e8f003..4f3fc46 100644
--- a/openstack/compute/v2/flavors/requests.go
+++ b/openstack/compute/v2/flavors/requests.go
@@ -34,10 +34,7 @@
 // ToFlavorListQuery formats a ListOpts into a query string.
 func (opts ListOpts) ToFlavorListQuery() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
+	return q.String(), err
 }
 
 // ListDetail instructs OpenStack to provide a list of flavors.
@@ -52,32 +49,23 @@
 		}
 		url += query
 	}
-	createPage := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
 		return FlavorPage{pagination.LinkedPageBase{PageResult: r}}
-	}
-
-	return pagination.NewPager(client, url, createPage)
+	})
 }
 
 // Get instructs OpenStack to provide details on a single flavor, identified by its ID.
 // Use ExtractFlavor to convert its result into a Flavor.
 func Get(client *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = client.Get(getURL(client, id), &res.Body, nil)
-	return res
+	var r GetResult
+	_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
+	return r
 }
 
 // IDFromName is a convienience function that returns a flavor's ID given its name.
 func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
 	count := 0
 	id := ""
-	if name == "" {
-		err := &gophercloud.ErrMissingInput{}
-		err.Function = "flavors.IDFromName"
-		err.Argument = "name"
-		return "", err
-	}
-
 	allPages, err := ListDetail(client, nil).AllPages()
 	if err != nil {
 		return "", err
@@ -98,7 +86,6 @@
 	switch count {
 	case 0:
 		err := &gophercloud.ErrResourceNotFound{}
-		err.Function = "flavors.IDFromName"
 		err.ResourceType = "flavor"
 		err.Name = name
 		return "", err
@@ -106,7 +93,6 @@
 		return id, nil
 	default:
 		err := &gophercloud.ErrMultipleResourcesFound{}
-		err.Function = "flavors.IDFromName"
 		err.ResourceType = "flavor"
 		err.Name = name
 		err.Count = count
diff --git a/openstack/compute/v2/images/requests.go b/openstack/compute/v2/images/requests.go
index 88db657..00461f8 100644
--- a/openstack/compute/v2/images/requests.go
+++ b/openstack/compute/v2/images/requests.go
@@ -32,10 +32,7 @@
 // ToImageListQuery formats a ListOpts into a query string.
 func (opts ListOpts) ToImageListQuery() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
+	return q.String(), err
 }
 
 // ListDetail enumerates the available images.
@@ -48,40 +45,30 @@
 		}
 		url += query
 	}
-
-	createPage := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
 		return ImagePage{pagination.LinkedPageBase{PageResult: r}}
-	}
-
-	return pagination.NewPager(client, url, createPage)
+	})
 }
 
 // Get acquires additional detail about a specific image by ID.
 // Use ExtractImage() to interpret the result as an openstack Image.
 func Get(client *gophercloud.ServiceClient, id string) GetResult {
-	var result GetResult
-	_, result.Err = client.Get(getURL(client, id), &result.Body, nil)
-	return result
+	var r GetResult
+	_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
+	return r
 }
 
 // Delete deletes the specified image ID.
 func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
-	var result DeleteResult
-	_, result.Err = client.Delete(deleteURL(client, id), nil)
-	return result
+	var r DeleteResult
+	_, r.Err = client.Delete(deleteURL(client, id), nil)
+	return r
 }
 
 // IDFromName is a convienience function that returns an image's ID given its name.
 func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
 	count := 0
 	id := ""
-	if name == "" {
-		err := &gophercloud.ErrMissingInput{}
-		err.Function = "images.IDFromName"
-		err.Argument = "name"
-		return "", err
-	}
-
 	allPages, err := ListDetail(client, nil).AllPages()
 	if err != nil {
 		return "", err
@@ -102,7 +89,6 @@
 	switch count {
 	case 0:
 		err := &gophercloud.ErrResourceNotFound{}
-		err.Function = "images.IDFromName"
 		err.ResourceType = "image"
 		err.Name = name
 		return "", err
@@ -110,7 +96,6 @@
 		return id, nil
 	default:
 		err := &gophercloud.ErrMultipleResourcesFound{}
-		err.Function = "images.IDFromName"
 		err.ResourceType = "image"
 		err.Name = name
 		err.Count = count
diff --git a/openstack/compute/v2/servers/fixtures.go b/openstack/compute/v2/servers/fixtures.go
index 2c22b45..224b996 100644
--- a/openstack/compute/v2/servers/fixtures.go
+++ b/openstack/compute/v2/servers/fixtures.go
@@ -7,6 +7,7 @@
 	"net/http"
 	"testing"
 
+	"github.com/gophercloud/gophercloud"
 	th "github.com/gophercloud/gophercloud/testhelper"
 	"github.com/gophercloud/gophercloud/testhelper/client"
 )
@@ -349,6 +350,15 @@
 	}
 )
 
+type CreateOptsWithCustomField struct {
+	CreateOpts
+	Foo string `json:"foo,omitempty"`
+}
+
+func (opts CreateOptsWithCustomField) ToServerCreateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "server")
+}
+
 // HandleServerCreationSuccessfully sets up the test server to respond to a server creation request
 // with a given response.
 func HandleServerCreationSuccessfully(t *testing.T, response string) {
@@ -369,6 +379,27 @@
 	})
 }
 
+// HandleServerCreationWithCustomFieldSuccessfully sets up the test server to respond to a server creation request
+// with a given response.
+func HandleServerCreationWithCustomFieldSuccessfully(t *testing.T, response string) {
+	th.Mux.HandleFunc("/servers", 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, `{
+			"server": {
+				"name": "derp",
+				"imageRef": "f90f6034-2570-4974-8351-6b49732ef2eb",
+				"flavorRef": "1",
+				"foo": "bar"
+			}
+		}`)
+
+		w.WriteHeader(http.StatusAccepted)
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, response)
+	})
+}
+
 // HandleServerListSuccessfully sets up the test server to respond to a server List request.
 func HandleServerListSuccessfully(t *testing.T) {
 	th.Mux.HandleFunc("/servers/detail", func(w http.ResponseWriter, r *http.Request) {
@@ -399,7 +430,7 @@
 	})
 }
 
-// HandleAdminPasswordChangeSuccessfully sets up the test server to respond to a server password
+// HandleServerForceDeletionSuccessfully sets up the test server to respond to a server password
 // change request.
 func HandleServerForceDeletionSuccessfully(t *testing.T) {
 	th.Mux.HandleFunc("/servers/asdfasdfasdf/action", func(w http.ResponseWriter, r *http.Request) {
@@ -673,4 +704,3 @@
 		w.WriteHeader(http.StatusAccepted)
 	})
 }
-
diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go
index 4129471..4257a4a 100644
--- a/openstack/compute/v2/servers/requests.go
+++ b/openstack/compute/v2/servers/requests.go
@@ -55,16 +55,12 @@
 // ToServerListQuery formats a ListOpts into a query string.
 func (opts ListOpts) ToServerListQuery() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
+	return q.String(), err
 }
 
 // List makes a request against the API to list servers accessible to you.
 func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
 	url := listDetailURL(client)
-
 	if opts != nil {
 		query, err := opts.ToServerListQuery()
 		if err != nil {
@@ -72,12 +68,9 @@
 		}
 		url += query
 	}
-
-	createPageFn := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
 		return ServerPage{pagination.LinkedPageBase{PageResult: r}}
-	}
-
-	return pagination.NewPager(client, url, createPageFn)
+	})
 }
 
 // CreateOptsBuilder describes struct types that can be accepted by the Create call.
@@ -189,7 +182,7 @@
 
 // ToServerCreateMap assembles a request body based on the contents of a CreateOpts.
 func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) {
-	b, err := gophercloud.BuildRequestBody(opts)
+	b, err := gophercloud.BuildRequestBody(opts, "")
 	if err != nil {
 		return nil, err
 	}
@@ -228,13 +221,11 @@
 	if opts.ImageRef == "" {
 		if opts.ImageName == "" {
 			err := ErrNeitherImageIDNorImageNameProvided{}
-			err.Function = "servers.CreateOpts.ToServerCreateMap"
 			err.Argument = "ImageRef/ImageName"
 			return nil, err
 		}
 		if opts.ServiceClient == nil {
 			err := ErrNoClientProvidedForIDByName{}
-			err.Function = "servers.CreateOpts.ToServerCreateMap"
 			err.Argument = "ServiceClient"
 			return nil, err
 		}
@@ -249,14 +240,12 @@
 	if opts.FlavorRef == "" {
 		if opts.FlavorName == "" {
 			err := ErrNeitherFlavorIDNorFlavorNameProvided{}
-			err.Function = "servers.CreateOpts.ToServerCreateMap"
 			err.Argument = "FlavorRef/FlavorName"
 			return nil, err
 		}
 		if opts.ServiceClient == nil {
 			err := ErrNoClientProvidedForIDByName{}
 			err.Argument = "ServiceClient"
-			err.Function = "servers.CreateOpts.ToServerCreateMap"
 			return nil, err
 		}
 		flavorID, err := flavors.IDFromName(opts.ServiceClient, opts.FlavorName)
@@ -272,13 +261,11 @@
 // Create requests a server to be provisioned to the user in the current tenant.
 func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
 	var r CreateResult
-
 	reqBody, err := opts.ToServerCreateMap()
 	if err != nil {
 		r.Err = err
 		return r
 	}
-
 	_, r.Err = client.Post(listURL(client), reqBody, &r.Body, nil)
 	return r
 }
@@ -308,42 +295,36 @@
 
 // UpdateOptsBuilder allows extensions to add additional attributes to the Update request.
 type UpdateOptsBuilder interface {
-	ToServerUpdateMap() map[string]interface{}
+	ToServerUpdateMap() (map[string]interface{}, error)
 }
 
 // UpdateOpts specifies the base attributes that may be updated on an existing server.
 type UpdateOpts struct {
-	// Name [optional] changes the displayed name of the server.
+	// Name changes the displayed name of the server.
 	// The server host name will *not* change.
 	// Server names are not constrained to be unique, even within the same tenant.
-	Name string
+	Name string `json:"name,omitempty"`
 
-	// AccessIPv4 [optional] provides a new IPv4 address for the instance.
-	AccessIPv4 string
+	// AccessIPv4 provides a new IPv4 address for the instance.
+	AccessIPv4 string `json:"accessIPv4,omitempty"`
 
-	// AccessIPv6 [optional] provides a new IPv6 address for the instance.
-	AccessIPv6 string
+	// AccessIPv6 provides a new IPv6 address for the instance.
+	AccessIPv6 string `json:"accessIPv6,omitempty"`
 }
 
 // ToServerUpdateMap formats an UpdateOpts structure into a request body.
-func (opts UpdateOpts) ToServerUpdateMap() map[string]interface{} {
-	server := make(map[string]string)
-	if opts.Name != "" {
-		server["name"] = opts.Name
-	}
-	if opts.AccessIPv4 != "" {
-		server["accessIPv4"] = opts.AccessIPv4
-	}
-	if opts.AccessIPv6 != "" {
-		server["accessIPv6"] = opts.AccessIPv6
-	}
-	return map[string]interface{}{"server": server}
+func (opts UpdateOpts) ToServerUpdateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "server")
 }
 
 // Update requests that various attributes of the indicated server be changed.
 func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
 	var r UpdateResult
-	b := opts.ToServerUpdateMap()
+	b, err := opts.ToServerUpdateMap()
+	if err != nil {
+		r.Err = err
+		return r
+	}
 	_, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
@@ -352,16 +333,13 @@
 
 // ChangeAdminPassword alters the administrator or root password for a specified server.
 func ChangeAdminPassword(client *gophercloud.ServiceClient, id, newPassword string) ActionResult {
-	var req struct {
-		ChangePassword struct {
-			AdminPass string `json:"adminPass"`
-		} `json:"changePassword"`
-	}
-
-	req.ChangePassword.AdminPass = newPassword
-
 	var r ActionResult
-	_, r.Err = client.Post(actionURL(client, id), req, nil, nil)
+	b := map[string]interface{}{
+		"changePassword": map[string]string{
+			"adminPass": newPassword,
+		},
+	}
+	_, r.Err = client.Post(actionURL(client, id), b, nil, nil)
 	return r
 }
 
@@ -377,36 +355,27 @@
 	PowerCycle              = HardReboot
 )
 
+// RebootOptsBuilder is an interface that options must satisfy in order to be
+// used when rebooting a server instance
 type RebootOptsBuilder interface {
 	ToServerRebootMap() (map[string]interface{}, error)
 }
 
+// RebootOpts satisfies the RebootOptsBuilder interface
 type RebootOpts struct {
-	Type RebootMethod
+	Type RebootMethod `json:"type" required:"true"`
 }
 
+// ToServerRebootMap allows RebootOpts to satisfiy the RebootOptsBuilder
+// interface
 func (opts *RebootOpts) ToServerRebootMap() (map[string]interface{}, error) {
-	if (opts.Type != SoftReboot) && (opts.Type != HardReboot) {
-		err := &gophercloud.ErrInvalidInput{}
-		err.Argument = "servers.RebootOpts.Type"
-		err.Value = opts.Type
-		err.Function = "servers.Reboot"
-		return nil, err
-	}
-
-	reqBody := map[string]interface{}{
-		"reboot": map[string]interface{}{
-			"type": string(opts.Type),
-		},
-	}
-
-	return reqBody, nil
+	return gophercloud.BuildRequestBody(opts, "reboot")
 }
 
 // Reboot requests that a given server reboot.
 // Two methods exist for rebooting a server:
 //
-// HardReboot (aka PowerCycle) rtarts the server instance by physically cutting power to the machine, or if a VM,
+// HardReboot (aka PowerCycle) starts 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 rtored or the VM instance rtarted.
@@ -415,14 +384,12 @@
 // E.g., in Linux, asking it to enter runlevel 6, or executing "sudo shutdown -r now", or by asking Windows to rtart the machine.
 func Reboot(client *gophercloud.ServiceClient, id string, opts RebootOptsBuilder) ActionResult {
 	var r ActionResult
-
-	reqBody, err := opts.ToServerRebootMap()
+	b, err := opts.ToServerRebootMap()
 	if err != nil {
 		r.Err = err
 		return r
 	}
-
-	_, r.Err = client.Post(actionURL(client, id), reqBody, nil, nil)
+	_, r.Err = client.Post(actionURL(client, id), b, nil, nil)
 	return r
 }
 
@@ -435,97 +402,64 @@
 // RebuildOpts represents the configuration options used in a server rebuild
 // operation
 type RebuildOpts struct {
-	// Required. The ID of the image you want your server to be provisioned on
-	ImageID string
-
+	// The server's admin password
+	AdminPass string `json:"adminPass" required:"true"`
+	// The ID of the image you want your server to be provisioned on
+	ImageID   string `json:"imageRef"`
+	ImageName string `json:"-"`
+	//ImageName string `json:"-"`
 	// Name to set the server to
-	Name string
-
-	// Required. The server's admin password
-	AdminPass string
-
+	Name string `json:"name,omitempty"`
 	// AccessIPv4 [optional] provides a new IPv4 address for the instance.
-	AccessIPv4 string
-
+	AccessIPv4 string `json:"accessIPv4,omitempty"`
 	// AccessIPv6 [optional] provides a new IPv6 address for the instance.
-	AccessIPv6 string
-
+	AccessIPv6 string `json:"accessIPv6,omitempty"`
 	// Metadata [optional] contains key-value pairs (up to 255 bytes each) to attach to the server.
-	Metadata map[string]string
-
+	Metadata map[string]string `json:"metadata,omitempty"`
 	// Personality [optional] includes files to inject into the server at launch.
 	// Rebuild will base64-encode file contents for you.
-	Personality Personality
+	Personality   Personality                `json:"personality,omitempty"`
+	ServiceClient *gophercloud.ServiceClient `json:"-"`
 }
 
 // ToServerRebuildMap formats a RebuildOpts struct into a map for use in JSON
 func (opts RebuildOpts) ToServerRebuildMap() (map[string]interface{}, error) {
-	var err error
-	server := make(map[string]interface{})
-
-	if opts.AdminPass == "" {
-		err := ErrNoAdminPassProvided{}
-		err.Function = "servers.ToServerRebuildMap"
-		err.Argument = "AdminPass"
-		return nil, err
-	}
-
-	if opts.ImageID == "" {
-		err := ErrNoImageIDProvided{}
-		err.Function = "servers.ToServerRebuildMap"
-		err.Argument = "ImageID"
-		return nil, err
-	}
-
+	b, err := gophercloud.BuildRequestBody(opts, "")
 	if err != nil {
-		return server, err
+		return nil, err
 	}
 
-	server["adminPass"] = opts.AdminPass
-	server["imageRef"] = opts.ImageID
-
-	if opts.Name != "" {
-		server["name"] = opts.Name
+	// If ImageRef isn't provided, use ImageName to ascertain the image ID.
+	if opts.ImageID == "" {
+		if opts.ImageName == "" {
+			err := ErrNeitherImageIDNorImageNameProvided{}
+			err.Argument = "ImageRef/ImageName"
+			return nil, err
+		}
+		if opts.ServiceClient == nil {
+			err := ErrNoClientProvidedForIDByName{}
+			err.Argument = "ServiceClient"
+			return nil, err
+		}
+		imageID, err := images.IDFromName(opts.ServiceClient, opts.ImageName)
+		if err != nil {
+			return nil, err
+		}
+		b["imageRef"] = imageID
 	}
 
-	if opts.AccessIPv4 != "" {
-		server["accessIPv4"] = opts.AccessIPv4
-	}
-
-	if opts.AccessIPv6 != "" {
-		server["accessIPv6"] = opts.AccessIPv6
-	}
-
-	if opts.Metadata != nil {
-		server["metadata"] = opts.Metadata
-	}
-
-	if len(opts.Personality) > 0 {
-		server["personality"] = opts.Personality
-	}
-
-	return map[string]interface{}{"rebuild": server}, nil
+	return map[string]interface{}{"rebuild": b}, nil
 }
 
 // Rebuild will reprovision the server according to the configuration options
 // provided in the RebuildOpts struct.
 func Rebuild(client *gophercloud.ServiceClient, id string, opts RebuildOptsBuilder) RebuildResult {
 	var r RebuildResult
-
-	if id == "" {
-		err := ErrNoIDProvided{}
-		err.Function = "servers.Rebuild"
-		err.Argument = "id"
-		r.Err = err
-		return r
-	}
-
 	b, err := opts.ToServerRebuildMap()
 	if err != nil {
 		r.Err = err
 		return r
 	}
-
 	_, r.Err = client.Post(actionURL(client, id), b, &r.Body, nil)
 	return r
 }
@@ -539,17 +473,13 @@
 // ResizeOpts represents the configuration options used to control a Resize operation.
 type ResizeOpts struct {
 	// FlavorRef is the ID of the flavor you wish your server to become.
-	FlavorRef string
+	FlavorRef string `json:"flavorRef" required:"true"`
 }
 
 // ToServerResizeMap formats a ResizeOpts as a map that can be used as a JSON request body for the
 // Resize request.
 func (opts ResizeOpts) ToServerResizeMap() (map[string]interface{}, error) {
-	resize := map[string]interface{}{
-		"flavorRef": opts.FlavorRef,
-	}
-
-	return map[string]interface{}{"resize": resize}, nil
+	return gophercloud.BuildRequestBody(opts, "resize")
 }
 
 // Resize instructs the provider to change the flavor of the server.
@@ -561,21 +491,11 @@
 // Otherwise, call RevertResize() to rtore the old configuration.
 func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOptsBuilder) ActionResult {
 	var r ActionResult
-
-	if id == "" {
-		err := ErrNoIDProvided{}
-		err.Function = "servers.Resize"
-		err.Argument = "id"
-		r.Err = err
-		return r
-	}
-
 	b, err := opts.ToServerResizeMap()
 	if err != nil {
 		r.Err = err
 		return r
 	}
-
 	_, r.Err = client.Post(actionURL(client, id), b, nil, nil)
 	return r
 }
@@ -584,17 +504,7 @@
 // See Resize() for more details.
 func ConfirmResize(client *gophercloud.ServiceClient, id string) ActionResult {
 	var r ActionResult
-
-	if id == "" {
-		err := ErrNoIDProvided{}
-		err.Function = "servers.ConfirmResize"
-		err.Argument = "id"
-		r.Err = err
-		return r
-	}
-
-	b := map[string]interface{}{"confirmResize": nil}
-	_, r.Err = client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{
+	_, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"confirmResize": nil}, nil, &gophercloud.RequestOpts{
 		OkCodes: []int{201, 202, 204},
 	})
 	return r
@@ -604,17 +514,7 @@
 // See Resize() for more details.
 func RevertResize(client *gophercloud.ServiceClient, id string) ActionResult {
 	var r ActionResult
-
-	if id == "" {
-		err := ErrNoIDProvided{}
-		err.Function = "servers.RevertResize"
-		err.Argument = "id"
-		r.Err = err
-		return r
-	}
-
-	b := map[string]interface{}{"revertResize": nil}
-	_, r.Err = client.Post(actionURL(client, id), b, nil, nil)
+	_, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"revertResize": nil}, nil, nil)
 	return r
 }
 
@@ -629,41 +529,26 @@
 type RescueOpts struct {
 	// AdminPass is the desired administrative password for the instance in
 	// RESCUE mode. If it's left blank, the server will generate a password.
-	AdminPass string
+	AdminPass string `json:"adminPass,omitempty"`
 }
 
 // ToServerRescueMap formats a RescueOpts as a map that can be used as a JSON
 // request body for the Rescue request.
 func (opts RescueOpts) ToServerRescueMap() (map[string]interface{}, error) {
-	server := make(map[string]interface{})
-	if opts.AdminPass != "" {
-		server["adminPass"] = opts.AdminPass
-	}
-	return map[string]interface{}{"rescue": server}, nil
+	return gophercloud.BuildRequestBody(opts, "rescue")
 }
 
 // Rescue instructs the provider to place the server into RESCUE mode.
 func Rescue(client *gophercloud.ServiceClient, id string, opts RescueOptsBuilder) RescueResult {
 	var r RescueResult
-
-	if id == "" {
-		err := ErrNoIDProvided{}
-		err.Function = "servers.Rebuild"
-		err.Argument = "id"
-		r.Err = err
-		return r
-	}
-
 	b, err := opts.ToServerRescueMap()
 	if err != nil {
 		r.Err = err
 		return r
 	}
-
 	_, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-
 	return r
 }
 
@@ -692,12 +577,12 @@
 // UpdateMetadatas or UpdateMetadata function.
 func ResetMetadata(client *gophercloud.ServiceClient, id string, opts ResetMetadataOptsBuilder) ResetMetadataResult {
 	var r ResetMetadataResult
-	metadata, err := opts.ToMetadataResetMap()
+	b, err := opts.ToMetadataResetMap()
 	if err != nil {
 		r.Err = err
 		return r
 	}
-	_, r.Err = client.Put(metadataURL(client, id), metadata, &r.Body, &gophercloud.RequestOpts{
+	_, r.Err = client.Put(metadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
 	return r
@@ -721,12 +606,12 @@
 // by opts.
 func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) UpdateMetadataResult {
 	var r UpdateMetadataResult
-	metadata, err := opts.ToMetadataUpdateMap()
+	b, err := opts.ToMetadataUpdateMap()
 	if err != nil {
 		r.Err = err
 		return r
 	}
-	_, r.Err = client.Post(metadataURL(client, id), metadata, &r.Body, &gophercloud.RequestOpts{
+	_, r.Err = client.Post(metadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
 	return r
@@ -760,13 +645,12 @@
 // CreateMetadatum will create or update the key-value pair with the given key for the given server ID.
 func CreateMetadatum(client *gophercloud.ServiceClient, id string, opts MetadatumOptsBuilder) CreateMetadatumResult {
 	var r CreateMetadatumResult
-	metadatum, key, err := opts.ToMetadatumCreateMap()
+	b, key, err := opts.ToMetadatumCreateMap()
 	if err != nil {
 		r.Err = err
 		return r
 	}
-
-	_, r.Err = client.Put(metadatumURL(client, id, key), metadatum, &r.Body, &gophercloud.RequestOpts{
+	_, r.Err = client.Put(metadatumURL(client, id, key), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
 	return r
@@ -775,9 +659,7 @@
 // Metadatum requests the key-value pair with the given key for the given server ID.
 func Metadatum(client *gophercloud.ServiceClient, id, key string) GetMetadatumResult {
 	var r GetMetadatumResult
-	_, r.Err = client.Request("GET", metadatumURL(client, id, key), &gophercloud.RequestOpts{
-		JSONResponse: &r.Body,
-	})
+	_, r.Err = client.Get(metadatumURL(client, id, key), &r.Body, nil)
 	return r
 }
 
@@ -790,60 +672,47 @@
 
 // ListAddresses makes a request against the API to list the servers IP addresses.
 func ListAddresses(client *gophercloud.ServiceClient, id string) pagination.Pager {
-	createPageFn := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, listAddressesURL(client, id), func(r pagination.PageResult) pagination.Page {
 		return AddressPage{pagination.SinglePageBase(r)}
-	}
-	return pagination.NewPager(client, listAddressesURL(client, id), createPageFn)
+	})
 }
 
 // ListAddressesByNetwork makes a request against the API to list the servers IP addresses
 // for the given network.
 func ListAddressesByNetwork(client *gophercloud.ServiceClient, id, network string) pagination.Pager {
-	createPageFn := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, listAddressesByNetworkURL(client, id, network), func(r pagination.PageResult) pagination.Page {
 		return NetworkAddressPage{pagination.SinglePageBase(r)}
-	}
-	return pagination.NewPager(client, listAddressesByNetworkURL(client, id, network), createPageFn)
+	})
 }
 
-type CreateImageOpts struct {
-	// Name [required] of the image/snapshot
-	Name string
-	// Metadata [optional] contains key-value pairs (up to 255 bytes each) to attach to the created image.
-	Metadata map[string]string
-}
-
+// CreateImageOptsBuilder is the interface types must satisfy in order to be
+// used as CreateImage options
 type CreateImageOptsBuilder interface {
 	ToServerCreateImageMap() (map[string]interface{}, error)
 }
 
+// CreateImageOpts satisfies the CreateImageOptsBuilder
+type CreateImageOpts struct {
+	// Name of the image/snapshot
+	Name string `json:"name" required:"true"`
+	// Metadata contains key-value pairs (up to 255 bytes each) to attach to the created image.
+	Metadata map[string]string `json:"metadata,omitempty"`
+}
+
 // ToServerCreateImageMap formats a CreateImageOpts structure into a request body.
 func (opts CreateImageOpts) ToServerCreateImageMap() (map[string]interface{}, error) {
-	var err error
-	img := make(map[string]interface{})
-	if opts.Name == "" {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "servers.CreateImageOpts.ToServerCreateImageMap"
-		err.Argument = "CreateImageOpts.Name"
-		return nil, err
-	}
-	img["name"] = opts.Name
-	if opts.Metadata != nil {
-		img["metadata"] = opts.Metadata
-	}
-	createImage := make(map[string]interface{})
-	createImage["createImage"] = img
-	return createImage, err
+	return gophercloud.BuildRequestBody(opts, "createImage")
 }
 
 // CreateImage makes a request against the nova API to schedule an image to be created of the server
-func CreateImage(client *gophercloud.ServiceClient, serverID string, opts CreateImageOptsBuilder) CreateImageResult {
+func CreateImage(client *gophercloud.ServiceClient, id string, opts CreateImageOptsBuilder) CreateImageResult {
 	var r CreateImageResult
 	b, err := opts.ToServerCreateImageMap()
 	if err != nil {
 		r.Err = err
 		return r
 	}
-	resp, err := client.Post(actionURL(client, serverID), b, nil, &gophercloud.RequestOpts{
+	resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{
 		OkCodes: []int{202},
 	})
 	r.Err = err
@@ -855,13 +724,6 @@
 func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
 	count := 0
 	id := ""
-	if name == "" {
-		err := &gophercloud.ErrMissingInput{}
-		err.Function = "servers.IDFromName"
-		err.Argument = "name"
-		return "", err
-	}
-
 	allPages, err := List(client, nil).AllPages()
 	if err != nil {
 		return "", err
@@ -881,19 +743,10 @@
 
 	switch count {
 	case 0:
-		err := &gophercloud.ErrResourceNotFound{}
-		err.Function = "servers.IDFromName"
-		err.ResourceType = "server"
-		err.Name = name
-		return "", err
+		return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "server"}
 	case 1:
 		return id, nil
 	default:
-		err := &gophercloud.ErrMultipleResourcesFound{}
-		err.Function = "servers.IDFromName"
-		err.ResourceType = "server"
-		err.Name = name
-		err.Count = count
-		return "", err
+		return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "server"}
 	}
 }
diff --git a/openstack/compute/v2/servers/requests_test.go b/openstack/compute/v2/servers/requests_test.go
index 73d1375..931ab36 100644
--- a/openstack/compute/v2/servers/requests_test.go
+++ b/openstack/compute/v2/servers/requests_test.go
@@ -69,6 +69,24 @@
 	th.CheckDeepEquals(t, ServerDerp, *actual)
 }
 
+func TestCreateServerWithCustomField(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleServerCreationWithCustomFieldSuccessfully(t, SingleServerBody)
+
+	actual, err := Create(client.ServiceClient(), CreateOptsWithCustomField{
+		CreateOpts: CreateOpts{
+			Name:      "derp",
+			ImageRef:  "f90f6034-2570-4974-8351-6b49732ef2eb",
+			FlavorRef: "1",
+		},
+		Foo: "bar",
+	}).Extract()
+	th.AssertNoErr(t, err)
+
+	th.CheckDeepEquals(t, ServerDerp, *actual)
+}
+
 func TestDeleteServer(t *testing.T) {
 	th.SetupHTTP()
 	defer th.TeardownHTTP()