more error types for compute v2
diff --git a/openstack/compute/v2/extensions/bootfromvolume/requests.go b/openstack/compute/v2/extensions/bootfromvolume/requests.go
index 6c063be..7171afa 100644
--- a/openstack/compute/v2/extensions/bootfromvolume/requests.go
+++ b/openstack/compute/v2/extensions/bootfromvolume/requests.go
@@ -1,7 +1,7 @@
 package bootfromvolume
 
 import (
-	"errors"
+	"fmt"
 	"strconv"
 
 	"github.com/gophercloud/gophercloud"
@@ -62,7 +62,10 @@
 	}
 
 	if len(opts.BlockDevice) == 0 {
-		return nil, errors.New("Required fields UUID and SourceType not set.")
+		err := gophercloud.ErrMissingInput{}
+		err.Function = "bootfromvolume.ToServerCreateMap"
+		err.Argument = "bootfromvolume.CreateOptsExt.BlockDevice"
+		return nil, err
 	}
 
 	serverMap := base["server"].(map[string]interface{})
@@ -71,7 +74,10 @@
 
 	for i, bd := range opts.BlockDevice {
 		if string(bd.SourceType) == "" {
-			return nil, errors.New("SourceType must be one of: volume, image, snapshot.")
+			err := gophercloud.ErrMissingInput{}
+			err.Function = "bootfromvolume.ToServerCreateMap"
+			err.Argument = fmt.Sprintf("bootfromvolume.CreateOptsExt.BlockDevice[%d].SourceType", i)
+			return nil, err
 		}
 
 		blockDevice[i] = make(map[string]interface{})
diff --git a/openstack/compute/v2/extensions/defsecrules/requests.go b/openstack/compute/v2/extensions/defsecrules/requests.go
index fc3e3a8..5e2d686 100644
--- a/openstack/compute/v2/extensions/defsecrules/requests.go
+++ b/openstack/compute/v2/extensions/defsecrules/requests.go
@@ -1,8 +1,6 @@
 package defsecrules
 
 import (
-	"errors"
-
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/pagination"
 )
@@ -40,21 +38,33 @@
 
 // ToRuleCreateMap builds the create rule options into a serializable format.
 func (opts CreateOpts) ToRuleCreateMap() (map[string]interface{}, error) {
-	rule := make(map[string]interface{})
-
 	if opts.FromPort == 0 {
-		return rule, errors.New("A FromPort must be set")
+		err := gophercloud.ErrMissingInput{}
+		err.Function = "defsecrules.ToRuleCreateMap"
+		err.Argument = "defsecrules.CreateOpts.FromPort"
+		return nil, err
 	}
 	if opts.ToPort == 0 {
-		return rule, errors.New("A ToPort must be set")
+		err := gophercloud.ErrMissingInput{}
+		err.Function = "defsecrules.ToRuleCreateMap"
+		err.Argument = "defsecrules.CreateOpts.ToPort"
+		return nil, err
 	}
 	if opts.IPProtocol == "" {
-		return rule, errors.New("A IPProtocol must be set")
+		err := gophercloud.ErrMissingInput{}
+		err.Function = "defsecrules.ToRuleCreateMap"
+		err.Argument = "defsecrules.CreateOpts.IPProtocol"
+		return nil, err
 	}
 	if opts.CIDR == "" {
-		return rule, errors.New("A CIDR must be set")
+		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
diff --git a/openstack/compute/v2/extensions/diskconfig/requests.go b/openstack/compute/v2/extensions/diskconfig/requests.go
index 3fd9c9e..19798d7 100644
--- a/openstack/compute/v2/extensions/diskconfig/requests.go
+++ b/openstack/compute/v2/extensions/diskconfig/requests.go
@@ -1,8 +1,7 @@
 package diskconfig
 
 import (
-	"errors"
-
+	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
 )
 
@@ -23,19 +22,6 @@
 	Manual DiskConfig = "MANUAL"
 )
 
-// ErrInvalidDiskConfig is returned if an invalid string is specified for a DiskConfig option.
-var ErrInvalidDiskConfig = errors.New("DiskConfig must be either diskconfig.Auto or diskconfig.Manual.")
-
-// Validate ensures that a DiskConfig contains an appropriate value.
-func (config DiskConfig) validate() error {
-	switch config {
-	case Auto, Manual:
-		return nil
-	default:
-		return ErrInvalidDiskConfig
-	}
-}
-
 // CreateOptsExt adds a DiskConfig option to the base CreateOpts.
 type CreateOptsExt struct {
 	servers.CreateOptsBuilder
@@ -71,8 +57,11 @@
 
 // ToServerRebuildMap adds the diskconfig option to the base server rebuild options.
 func (opts RebuildOptsExt) ToServerRebuildMap() (map[string]interface{}, error) {
-	err := opts.DiskConfig.validate()
-	if err != nil {
+	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
 	}
 
@@ -97,8 +86,11 @@
 
 // ToServerResizeMap adds the diskconfig option to the base server creation options.
 func (opts ResizeOptsExt) ToServerResizeMap() (map[string]interface{}, error) {
-	err := opts.DiskConfig.validate()
-	if err != nil {
+	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 d99033e..00b2520 100644
--- a/openstack/compute/v2/extensions/floatingips/requests.go
+++ b/openstack/compute/v2/extensions/floatingips/requests.go
@@ -1,8 +1,6 @@
 package floatingips
 
 import (
-	"errors"
-
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/pagination"
 )
@@ -41,7 +39,10 @@
 // ToFloatingIPCreateMap constructs a request body from CreateOpts.
 func (opts CreateOpts) ToFloatingIPCreateMap() (map[string]interface{}, error) {
 	if opts.Pool == "" {
-		return nil, errors.New("Missing field required for floating IP creation: Pool")
+		err := gophercloud.ErrMissingInput{}
+		err.Function = "floatingips.ToFloatingIPCreateMap"
+		err.Argument = "floatingips.CreateOpts.Pool"
+		return nil, err
 	}
 
 	return map[string]interface{}{"pool": opts.Pool}, nil
@@ -50,11 +51,17 @@
 // ToAssociateMap constructs a request body from AssociateOpts.
 func (opts AssociateOpts) ToAssociateMap() (map[string]interface{}, error) {
 	if opts.ServerID == "" {
-		return nil, errors.New("Required field missing for floating IP association: ServerID")
+		err := gophercloud.ErrMissingInput{}
+		err.Function = "floatingips.ToAssociateMap"
+		err.Argument = "floatingips.AssociateOpts.ServerID"
+		return nil, err
 	}
 
 	if opts.FloatingIP == "" {
-		return nil, errors.New("Required field missing for floating IP association: FloatingIP")
+		err := gophercloud.ErrMissingInput{}
+		err.Function = "floatingips.ToAssociateMap"
+		err.Argument = "floatingips.AssociateOpts.FloatingIP"
+		return nil, err
 	}
 
 	associateInfo := map[string]interface{}{
diff --git a/openstack/compute/v2/extensions/keypairs/requests.go b/openstack/compute/v2/extensions/keypairs/requests.go
index af05a8e..b9b32ee 100644
--- a/openstack/compute/v2/extensions/keypairs/requests.go
+++ b/openstack/compute/v2/extensions/keypairs/requests.go
@@ -1,8 +1,6 @@
 package keypairs
 
 import (
-	"errors"
-
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
 	"github.com/gophercloud/gophercloud/pagination"
@@ -58,7 +56,10 @@
 // ToKeyPairCreateMap constructs a request body from CreateOpts.
 func (opts CreateOpts) ToKeyPairCreateMap() (map[string]interface{}, error) {
 	if opts.Name == "" {
-		return nil, errors.New("Missing field required for keypair creation: Name")
+		err := gophercloud.ErrMissingInput{}
+		err.Function = "keypairs.ToKeyPairCreateMap"
+		err.Argument = "keypairs.CreateOpts.Name"
+		return nil, err
 	}
 
 	keypair := make(map[string]interface{})
diff --git a/openstack/compute/v2/extensions/schedulerhints/requests.go b/openstack/compute/v2/extensions/schedulerhints/requests.go
index d9aa733..5713e72 100644
--- a/openstack/compute/v2/extensions/schedulerhints/requests.go
+++ b/openstack/compute/v2/extensions/schedulerhints/requests.go
@@ -1,11 +1,11 @@
 package schedulerhints
 
 import (
-	"fmt"
 	"net"
 	"regexp"
 	"strings"
 
+	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
 )
 
@@ -47,7 +47,12 @@
 
 	if opts.Group != "" {
 		if !uuidRegex.MatchString(opts.Group) {
-			return nil, fmt.Errorf("Group must be a UUID")
+			err := gophercloud.ErrInvalidInput{}
+			err.Function = "schedulerhints.ToServerSchedulerHintsMap"
+			err.Argument = "schedulerhints.SchedulerHints.Group"
+			err.Value = opts.Group
+			err.Info = "Group must be a UUID"
+			return nil, err
 		}
 		sh["group"] = opts.Group
 	}
@@ -55,7 +60,12 @@
 	if len(opts.DifferentHost) > 0 {
 		for _, diffHost := range opts.DifferentHost {
 			if !uuidRegex.MatchString(diffHost) {
-				return nil, fmt.Errorf("The hosts in DifferentHost must be in UUID format.")
+				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."
+				return nil, err
 			}
 		}
 		sh["different_host"] = opts.DifferentHost
@@ -64,7 +74,12 @@
 	if len(opts.SameHost) > 0 {
 		for _, sameHost := range opts.SameHost {
 			if !uuidRegex.MatchString(sameHost) {
-				return nil, fmt.Errorf("The hosts in SameHost must be in UUID format.")
+				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."
+				return nil, err
 			}
 		}
 		sh["same_host"] = opts.SameHost
@@ -83,7 +98,12 @@
 	*/
 	if len(opts.Query) > 0 {
 		if len(opts.Query) < 3 {
-			return nil, fmt.Errorf("Query must be a conditional statement in the format of [op,variable,value]")
+			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]"
+			return nil, err
 		}
 		sh["query"] = opts.Query
 	}
@@ -94,7 +114,12 @@
 
 	if opts.BuildNearHostIP != "" {
 		if _, _, err := net.ParseCIDR(opts.BuildNearHostIP); err != nil {
-			return nil, fmt.Errorf("BuildNearHostIP must be a valid subnet in the form 192.168.1.1/24")
+			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"
+			return nil, err
 		}
 		ipParts := strings.Split(opts.BuildNearHostIP, "/")
 		sh["build_near_host_ip"] = ipParts[0]
diff --git a/openstack/compute/v2/extensions/secgroups/requests.go b/openstack/compute/v2/extensions/secgroups/requests.go
index 758ba09..a0bb025 100644
--- a/openstack/compute/v2/extensions/secgroups/requests.go
+++ b/openstack/compute/v2/extensions/secgroups/requests.go
@@ -1,8 +1,6 @@
 package secgroups
 
 import (
-	"errors"
-
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/pagination"
 )
@@ -46,20 +44,21 @@
 	ToSecGroupCreateMap() (map[string]interface{}, error)
 }
 
-var (
-	errName = errors.New("Name is a required field")
-	errDesc = errors.New("Description is a required field")
-)
-
 // 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 == "" {
-		return sg, errName
+		err := gophercloud.ErrMissingInput{}
+		err.Function = "secgroups.ToSecGroupCreateMap"
+		err.Argument = "secgroups.CreateOpts.Name"
+		return nil, err
 	}
 	if opts.Description == "" {
-		return sg, errDesc
+		err := gophercloud.ErrMissingInput{}
+		err.Function = "secgroups.ToSecGroupCreateMap"
+		err.Argument = "secgroups.CreateOpts.Description"
+		return nil, err
 	}
 
 	sg["name"] = opts.Name
@@ -98,10 +97,16 @@
 	sg := make(map[string]interface{})
 
 	if opts.Name == "" {
-		return sg, errName
+		err := gophercloud.ErrMissingInput{}
+		err.Function = "secgroups.ToSecGroupUpdateMap"
+		err.Argument = "secgroups.UpdateOpts.Name"
+		return nil, err
 	}
 	if opts.Description == "" {
-		return sg, errDesc
+		err := gophercloud.ErrMissingInput{}
+		err.Function = "secgroups.ToSecGroupUpdateMap"
+		err.Argument = "secgroups.UpdateOpts.Description"
+		return nil, err
 	}
 
 	sg["name"] = opts.Name
@@ -176,24 +181,39 @@
 
 // ToRuleCreateMap builds the create rule options into a serializable format.
 func (opts CreateRuleOpts) ToRuleCreateMap() (map[string]interface{}, error) {
-	rule := make(map[string]interface{})
-
 	if opts.ParentGroupID == "" {
-		return rule, errors.New("A ParentGroupID must be set")
+		err := gophercloud.ErrMissingInput{}
+		err.Function = "secgroups.ToRuleCreateMap"
+		err.Argument = "secgroups.CreateRuleOpts.ParentGroupID"
+		return nil, err
 	}
 	if opts.FromPort == 0 {
-		return rule, errors.New("A FromPort must be set")
+		err := gophercloud.ErrMissingInput{}
+		err.Function = "secgroups.ToRuleCreateMap"
+		err.Argument = "secgroups.CreateRuleOpts.FromPort"
+		return nil, err
 	}
 	if opts.ToPort == 0 {
-		return rule, errors.New("A ToPort must be set")
+		err := gophercloud.ErrMissingInput{}
+		err.Function = "secgroups.ToRuleCreateMap"
+		err.Argument = "secgroups.CreateRuleOpts.ToPort"
+		return nil, err
 	}
 	if opts.IPProtocol == "" {
-		return rule, errors.New("A IPProtocol must be set")
+		err := gophercloud.ErrMissingInput{}
+		err.Function = "secgroups.ToRuleCreateMap"
+		err.Argument = "secgroups.CreateRuleOpts.IPProtocol"
+		return nil, err
 	}
 	if opts.CIDR == "" && opts.FromGroupID == "" {
-		return rule, errors.New("A CIDR or FromGroupID must be set")
+		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
diff --git a/openstack/compute/v2/extensions/servergroups/requests.go b/openstack/compute/v2/extensions/servergroups/requests.go
index e3b2493..212edac 100644
--- a/openstack/compute/v2/extensions/servergroups/requests.go
+++ b/openstack/compute/v2/extensions/servergroups/requests.go
@@ -1,8 +1,6 @@
 package servergroups
 
 import (
-	"errors"
-
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/pagination"
 )
@@ -32,11 +30,17 @@
 // ToServerGroupCreateMap constructs a request body from CreateOpts.
 func (opts CreateOpts) ToServerGroupCreateMap() (map[string]interface{}, error) {
 	if opts.Name == "" {
-		return nil, errors.New("Missing field required for server group creation: Name")
+		err := gophercloud.ErrMissingInput{}
+		err.Function = "servergroups.ToServerGroupCreateMap"
+		err.Argument = "servergroups.CreateOpts.Name"
+		return nil, err
 	}
 
 	if len(opts.Policies) < 1 {
-		return nil, errors.New("Missing field required for server group creation: Policies")
+		err := gophercloud.ErrMissingInput{}
+		err.Function = "servergroups.ToServerGroupCreateMap"
+		err.Argument = "servergroups.CreateOpts.Policies"
+		return nil, err
 	}
 
 	serverGroup := make(map[string]interface{})
diff --git a/openstack/compute/v2/extensions/volumeattach/requests.go b/openstack/compute/v2/extensions/volumeattach/requests.go
index 17cb72e..c2bc2ee 100644
--- a/openstack/compute/v2/extensions/volumeattach/requests.go
+++ b/openstack/compute/v2/extensions/volumeattach/requests.go
@@ -1,8 +1,6 @@
 package volumeattach
 
 import (
-	"errors"
-
 	"github.com/gophercloud/gophercloud"
 	"github.com/gophercloud/gophercloud/pagination"
 )
@@ -32,7 +30,10 @@
 // ToVolumeAttachmentCreateMap constructs a request body from CreateOpts.
 func (opts CreateOpts) ToVolumeAttachmentCreateMap() (map[string]interface{}, error) {
 	if opts.VolumeID == "" {
-		return nil, errors.New("Missing field required for volume attachment creation: VolumeID")
+		err := gophercloud.ErrMissingInput{}
+		err.Function = "volumeattach.ToVolumeAttachmentCreateMap"
+		err.Argument = "volumeattach.CreateOpts.VolumeID"
+		return nil, err
 	}
 
 	volumeAttachment := make(map[string]interface{})