Error handling on struct to map mappings
diff --git a/openstack/networking/v2/extensions/fwaas/firewalls/errors.go b/openstack/networking/v2/extensions/fwaas/firewalls/errors.go
new file mode 100644
index 0000000..dd92bb2
--- /dev/null
+++ b/openstack/networking/v2/extensions/fwaas/firewalls/errors.go
@@ -0,0 +1,11 @@
+package firewalls
+
+import "fmt"
+
+func err(str string) error {
+	return fmt.Errorf("%s", str)
+}
+
+var (
+	errPolicyRequired = err("A policy ID is required")
+)
diff --git a/openstack/networking/v2/extensions/fwaas/firewalls/requests.go b/openstack/networking/v2/extensions/fwaas/firewalls/requests.go
index a1df87b..455cf79 100644
--- a/openstack/networking/v2/extensions/fwaas/firewalls/requests.go
+++ b/openstack/networking/v2/extensions/fwaas/firewalls/requests.go
@@ -67,42 +67,46 @@
 	PolicyID     string
 }
 
-// ToPolicyCreateMap casts a CreateOpts struct to a map.
-func (opts CreateOpts) ToPolicyCreateMap() map[string]interface{} {
-	p := make(map[string]interface{})
+// ToFirewallCreateMap casts a CreateOpts struct to a map.
+func (opts CreateOpts) ToFirewallCreateMap() (map[string]interface{}, error) {
+	if opts.PolicyID == "" {
+		return nil, errPolicyRequired
+	}
+
+	f := make(map[string]interface{})
 
 	if opts.TenantID != "" {
-		p["tenant_id"] = opts.TenantID
+		f["tenant_id"] = opts.TenantID
 	}
 	if opts.Name != "" {
-		p["name"] = opts.Name
+		f["name"] = opts.Name
 	}
 	if opts.Description != "" {
-		p["description"] = opts.Description
+		f["description"] = opts.Description
 	}
 	if opts.Shared != nil {
-		p["shared"] = *opts.Shared
+		f["shared"] = *opts.Shared
 	}
 	if opts.AdminStateUp != nil {
-		p["admin_state_up"] = *opts.AdminStateUp
+		f["admin_state_up"] = *opts.AdminStateUp
 	}
 	if opts.PolicyID != "" {
-		p["firewall_policy_id"] = opts.PolicyID
+		f["firewall_policy_id"] = opts.PolicyID
 	}
 
-	return p
+	return map[string]interface{}{"firewall": f}, nil
 }
 
 // Create accepts a CreateOpts struct and uses the values to create a new firewall
 func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
+	var res CreateResult
 
-	type request struct {
-		Firewall map[string]interface{} `json:"firewall"`
+	reqBody, err := opts.ToFirewallCreateMap()
+	if err != nil {
+		res.Err = err
+		return res
 	}
 
-	reqBody := request{Firewall: opts.ToPolicyCreateMap()}
-
-	var res CreateResult
 	_, res.Err = perigee.Request("POST", rootURL(c), perigee.Options{
 		MoreHeaders: c.AuthenticatedHeaders(),
 		ReqBody:     &reqBody,
@@ -133,40 +137,40 @@
 	PolicyID     string
 }
 
-// ToPolicyUpdateMap casts a CreateOpts struct to a map.
-func (opts UpdateOpts) ToPolicyUpdateMap() map[string]interface{} {
-	p := make(map[string]interface{})
+// ToFirewallUpdateMap casts a CreateOpts struct to a map.
+func (opts UpdateOpts) ToFirewallUpdateMap() (map[string]interface{}, error) {
+	f := make(map[string]interface{})
 
 	if opts.Name != nil {
-		p["name"] = *opts.Name
+		f["name"] = *opts.Name
 	}
 	if opts.Description != nil {
-		p["description"] = *opts.Description
+		f["description"] = *opts.Description
 	}
 	if opts.Shared != nil {
-		p["shared"] = *opts.Shared
+		f["shared"] = *opts.Shared
 	}
 	if opts.AdminStateUp != nil {
-		p["admin_state_up"] = *opts.AdminStateUp
+		f["admin_state_up"] = *opts.AdminStateUp
 	}
 	if opts.PolicyID != "" {
-		p["firewall_policy_id"] = opts.PolicyID
+		f["firewall_policy_id"] = opts.PolicyID
 	}
 
-	return p
+	return map[string]interface{}{"firewall": f}, nil
 }
 
 // Update allows firewalls to be updated.
 func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResult {
+	var res UpdateResult
 
-	type request struct {
-		Firewall map[string]interface{} `json:"firewall"`
+	reqBody, err := opts.ToFirewallUpdateMap()
+	if err != nil {
+		res.Err = err
+		return res
 	}
 
-	reqBody := request{Firewall: opts.ToPolicyUpdateMap()}
-
 	// Send request to API
-	var res UpdateResult
 	_, res.Err = perigee.Request("PUT", resourceURL(c, id), perigee.Options{
 		MoreHeaders: c.AuthenticatedHeaders(),
 		ReqBody:     &reqBody,
diff --git a/openstack/networking/v2/extensions/fwaas/policies/requests.go b/openstack/networking/v2/extensions/fwaas/policies/requests.go
index fec7c2e..21e45dd 100644
--- a/openstack/networking/v2/extensions/fwaas/policies/requests.go
+++ b/openstack/networking/v2/extensions/fwaas/policies/requests.go
@@ -61,7 +61,7 @@
 }
 
 // ToPolicyCreateMap casts a CreateOpts struct to a map.
-func (opts CreateOpts) ToPolicyCreateMap() map[string]interface{} {
+func (opts CreateOpts) ToPolicyCreateMap() (map[string]interface{}, error) {
 	p := make(map[string]interface{})
 
 	if opts.TenantID != "" {
@@ -83,19 +83,19 @@
 		p["firewall_rules"] = opts.Rules
 	}
 
-	return p
+	return map[string]interface{}{"firewall_policy": p}, nil
 }
 
 // Create accepts a CreateOpts struct and uses the values to create a new firewall policy
 func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
+	var res CreateResult
 
-	type request struct {
-		Policy map[string]interface{} `json:"firewall_policy"`
+	reqBody, err := opts.ToPolicyCreateMap()
+	if err != nil {
+		res.Err = err
+		return res
 	}
 
-	reqBody := request{Policy: opts.ToPolicyCreateMap()}
-
-	var res CreateResult
 	_, res.Err = perigee.Request("POST", rootURL(c), perigee.Options{
 		MoreHeaders: c.AuthenticatedHeaders(),
 		ReqBody:     &reqBody,
@@ -127,7 +127,7 @@
 }
 
 // ToPolicyUpdateMap casts a CreateOpts struct to a map.
-func (opts UpdateOpts) ToPolicyUpdateMap() map[string]interface{} {
+func (opts UpdateOpts) ToPolicyUpdateMap() (map[string]interface{}, error) {
 	p := make(map[string]interface{})
 
 	if opts.Name != nil {
@@ -146,20 +146,20 @@
 		p["firewall_rules"] = opts.Rules
 	}
 
-	return p
+	return map[string]interface{}{"firewall_policy": p}, nil
 }
 
 // Update allows firewall policies to be updated.
 func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResult {
+	var res UpdateResult
 
-	type request struct {
-		Policy map[string]interface{} `json:"firewall_policy"`
+	reqBody, err := opts.ToPolicyUpdateMap()
+	if err != nil {
+		res.Err = err
+		return res
 	}
 
-	reqBody := request{Policy: opts.ToPolicyUpdateMap()}
-
 	// Send request to API
-	var res UpdateResult
 	_, res.Err = perigee.Request("PUT", resourceURL(c, id), perigee.Options{
 		MoreHeaders: c.AuthenticatedHeaders(),
 		ReqBody:     &reqBody,
diff --git a/openstack/networking/v2/extensions/fwaas/rules/errors.go b/openstack/networking/v2/extensions/fwaas/rules/errors.go
new file mode 100644
index 0000000..0b29d39
--- /dev/null
+++ b/openstack/networking/v2/extensions/fwaas/rules/errors.go
@@ -0,0 +1,12 @@
+package rules
+
+import "fmt"
+
+func err(str string) error {
+	return fmt.Errorf("%s", str)
+}
+
+var (
+	errProtocolRequired = err("A protocol is required (tcp, udp, icmp or any)")
+	errActionRequired   = err("An action is required (allow or deny)")
+)
diff --git a/openstack/networking/v2/extensions/fwaas/rules/requests.go b/openstack/networking/v2/extensions/fwaas/rules/requests.go
index 18e37ad..cefbf86 100644
--- a/openstack/networking/v2/extensions/fwaas/rules/requests.go
+++ b/openstack/networking/v2/extensions/fwaas/rules/requests.go
@@ -78,7 +78,15 @@
 }
 
 // ToRuleCreateMap casts a CreateOpts struct to a map.
-func (opts CreateOpts) ToRuleCreateMap() map[string]interface{} {
+func (opts CreateOpts) ToRuleCreateMap() (map[string]interface{}, error) {
+	if opts.Protocol == "" {
+		return nil, errProtocolRequired
+	}
+
+	if opts.Action == "" {
+		return nil, errActionRequired
+	}
+
 	r := make(map[string]interface{})
 
 	r["protocol"] = opts.Protocol
@@ -115,19 +123,19 @@
 		r["enabled"] = *opts.Enabled
 	}
 
-	return r
+	return map[string]interface{}{"firewall_rule": r}, nil
 }
 
 // Create accepts a CreateOpts struct and uses the values to create a new firewall rule
 func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
+	var res CreateResult
 
-	type request struct {
-		Rule map[string]interface{} `json:"firewall_rule"`
+	reqBody, err := opts.ToRuleCreateMap()
+	if err != nil {
+		res.Err = err
+		return res
 	}
 
-	reqBody := request{Rule: opts.ToRuleCreateMap()}
-
-	var res CreateResult
 	_, res.Err = perigee.Request("POST", rootURL(c), perigee.Options{
 		MoreHeaders: c.AuthenticatedHeaders(),
 		ReqBody:     &reqBody,
@@ -165,7 +173,7 @@
 }
 
 // ToRuleUpdateMap casts a UpdateOpts struct to a map.
-func (opts UpdateOpts) ToRuleUpdateMap() map[string]interface{} {
+func (opts UpdateOpts) ToRuleUpdateMap() (map[string]interface{}, error) {
 	r := make(map[string]interface{})
 
 	if opts.Protocol != "" {
@@ -222,20 +230,20 @@
 		r["enabled"] = *opts.Enabled
 	}
 
-	return r
+	return map[string]interface{}{"firewall_rule": r}, nil
 }
 
 // Update allows firewall policies to be updated.
 func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResult {
+	var res UpdateResult
 
-	type request struct {
-		Rule map[string]interface{} `json:"firewall_rule"`
+	reqBody, err := opts.ToRuleUpdateMap()
+	if err != nil {
+		res.Err = err
+		return res
 	}
 
-	reqBody := request{Rule: opts.ToRuleUpdateMap()}
-
 	// Send request to API
-	var res UpdateResult
 	_, res.Err = perigee.Request("PUT", resourceURL(c, id), perigee.Options{
 		MoreHeaders: c.AuthenticatedHeaders(),
 		ReqBody:     &reqBody,