dsl struct tags; wip
diff --git a/openstack/cdn/v1/base/requests.go b/openstack/cdn/v1/base/requests.go
index ed3e7a0..15f8346 100644
--- a/openstack/cdn/v1/base/requests.go
+++ b/openstack/cdn/v1/base/requests.go
@@ -5,17 +5,17 @@
 // Get retrieves the home document, allowing the user to discover the
 // entire API.
 func Get(c *gophercloud.ServiceClient) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(getURL(c), &res.Body, nil)
-	return res
+	var r GetResult
+	_, r.Err = c.Get(getURL(c), &r.Body, nil)
+	return r
 }
 
 // Ping retrieves a ping to the server.
 func Ping(c *gophercloud.ServiceClient) PingResult {
-	var res PingResult
-	_, res.Err = c.Get(pingURL(c), nil, &gophercloud.RequestOpts{
+	var r PingResult
+	_, r.Err = c.Get(pingURL(c), nil, &gophercloud.RequestOpts{
 		OkCodes:     []int{204},
 		MoreHeaders: map[string]string{"Accept": ""},
 	})
-	return res
+	return r
 }
diff --git a/openstack/cdn/v1/flavors/requests.go b/openstack/cdn/v1/flavors/requests.go
index a7c0232..d42d1d7 100644
--- a/openstack/cdn/v1/flavors/requests.go
+++ b/openstack/cdn/v1/flavors/requests.go
@@ -7,16 +7,14 @@
 
 // List returns a single page of CDN flavors.
 func List(c *gophercloud.ServiceClient) pagination.Pager {
-	url := listURL(c)
-	createPage := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(c, listURL(c), func(r pagination.PageResult) pagination.Page {
 		return FlavorPage{pagination.SinglePageBase(r)}
-	}
-	return pagination.NewPager(c, url, createPage)
+	})
 }
 
 // Get retrieves a specific flavor based on its unique ID.
 func Get(c *gophercloud.ServiceClient, id string) GetResult {
-	var res GetResult
-	_, res.Err = c.Get(getURL(c, id), &res.Body, nil)
-	return res
+	var r GetResult
+	_, r.Err = c.Get(getURL(c, id), &r.Body, nil)
+	return r
 }
diff --git a/openstack/cdn/v1/serviceassets/requests.go b/openstack/cdn/v1/serviceassets/requests.go
index 39afaff..3d0543e 100644
--- a/openstack/cdn/v1/serviceassets/requests.go
+++ b/openstack/cdn/v1/serviceassets/requests.go
@@ -24,10 +24,7 @@
 // ToCDNAssetDeleteParams formats a DeleteOpts into a query string.
 func (opts DeleteOpts) ToCDNAssetDeleteParams() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
+	return q.String(), err
 }
 
 // Delete accepts a unique service ID or URL and deletes the CDN service asset associated with
@@ -41,8 +38,15 @@
 	} else {
 		url = deleteURL(c, idOrURL)
 	}
-
-	var res DeleteResult
-	_, res.Err = c.Delete(url, nil)
-	return res
+	var r DeleteResult
+	if opts != nil {
+		q, err := opts.ToCDNAssetDeleteParams()
+		if err != nil {
+			r.Err = err
+			return r
+		}
+		url += q
+	}
+	_, r.Err = c.Delete(url, nil)
+	return r
 }
diff --git a/openstack/cdn/v1/services/requests.go b/openstack/cdn/v1/services/requests.go
index 89ee372..9a0f54b 100644
--- a/openstack/cdn/v1/services/requests.go
+++ b/openstack/cdn/v1/services/requests.go
@@ -24,10 +24,7 @@
 // ToCDNServiceListQuery formats a ListOpts into a query string.
 func (opts ListOpts) ToCDNServiceListQuery() (string, error) {
 	q, err := gophercloud.BuildQueryString(opts)
-	if err != nil {
-		return "", err
-	}
-	return q.String(), nil
+	return q.String(), err
 }
 
 // List returns a Pager which allows you to iterate over a collection of
@@ -42,15 +39,11 @@
 		}
 		url += query
 	}
-
-	createPage := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
 		p := ServicePage{pagination.MarkerPageBase{PageResult: r}}
 		p.MarkerPageBase.Owner = p
 		return p
-	}
-
-	pager := pagination.NewPager(c, url, createPage)
-	return pager
+	})
 }
 
 // CreateOptsBuilder is the interface options structs have to satisfy in order
@@ -64,123 +57,52 @@
 // CreateOpts is the common options struct used in this package's Create
 // operation.
 type CreateOpts struct {
-	// REQUIRED. Specifies the name of the service. The minimum length for name is
+	// Specifies the name of the service. The minimum length for name is
 	// 3. The maximum length is 256.
-	Name string
-	// REQUIRED. Specifies a list of domains used by users to access their website.
-	Domains []Domain
-	// REQUIRED. Specifies a list of origin domains or IP addresses where the
+	Name string `json:"name" required:"true"`
+	// Specifies a list of domains used by users to access their website.
+	Domains []Domain `json:"domains" required:"true"`
+	// Specifies a list of origin domains or IP addresses where the
 	// original assets are stored.
-	Origins []Origin
-	// REQUIRED. Specifies the CDN provider flavor ID to use. For a list of
+	Origins []Origin `json:"origins" required:"true"`
+	// Specifies the CDN provider flavor ID to use. For a list of
 	// flavors, see the operation to list the available flavors. The minimum
 	// length for flavor_id is 1. The maximum length is 256.
-	FlavorID string
-	// OPTIONAL. Specifies the TTL rules for the assets under this service. Supports wildcards for fine-grained control.
-	Caching []CacheRule
-	// OPTIONAL. Specifies the restrictions that define who can access assets (content from the CDN cache).
-	Restrictions []Restriction
+	FlavorID string `json:"flavor_id" required:"true"`
+	// Specifies the TTL rules for the assets under this service. Supports wildcards for fine-grained control.
+	Caching []CacheRule `json:"caching,omitempty"`
+	// Specifies the restrictions that define who can access assets (content from the CDN cache).
+	Restrictions []Restriction `json:"restrictions,omitempty"`
 }
 
 // ToCDNServiceCreateMap casts a CreateOpts struct to a map.
 func (opts CreateOpts) ToCDNServiceCreateMap() (map[string]interface{}, error) {
-	s := make(map[string]interface{})
 
-	if opts.Name == "" {
-		return nil, no("Name")
-	}
-	s["name"] = opts.Name
-
-	if opts.Domains == nil {
-		return nil, no("Domains")
-	}
-	for _, domain := range opts.Domains {
-		if domain.Domain == "" {
-			return nil, no("Domains[].Domain")
-		}
-	}
-	s["domains"] = opts.Domains
-
-	if opts.Origins == nil {
-		return nil, no("Origins")
-	}
-	for _, origin := range opts.Origins {
-		if origin.Origin == "" {
-			return nil, no("Origins[].Origin")
-		}
-		if origin.Rules == nil && len(opts.Origins) > 1 {
-			return nil, no("Origins[].Rules")
-		}
-		for _, rule := range origin.Rules {
-			if rule.Name == "" {
-				return nil, no("Origins[].Rules[].Name")
-			}
-			if rule.RequestURL == "" {
-				return nil, no("Origins[].Rules[].RequestURL")
+	/*
+		for _, origin := range opts.Origins {
+			if origin.Rules == nil && len(opts.Origins) > 1 {
+				return nil, no("Origins[].Rules")
 			}
 		}
-	}
-	s["origins"] = opts.Origins
+	*/
 
-	if opts.FlavorID == "" {
-		return nil, no("FlavorID")
-	}
-	s["flavor_id"] = opts.FlavorID
-
-	if opts.Caching != nil {
-		for _, cache := range opts.Caching {
-			if cache.Name == "" {
-				return nil, no("Caching[].Name")
-			}
-			if cache.Rules != nil {
-				for _, rule := range cache.Rules {
-					if rule.Name == "" {
-						return nil, no("Caching[].Rules[].Name")
-					}
-					if rule.RequestURL == "" {
-						return nil, no("Caching[].Rules[].RequestURL")
-					}
-				}
-			}
-		}
-		s["caching"] = opts.Caching
-	}
-
-	if opts.Restrictions != nil {
-		for _, restriction := range opts.Restrictions {
-			if restriction.Name == "" {
-				return nil, no("Restrictions[].Name")
-			}
-			if restriction.Rules != nil {
-				for _, rule := range restriction.Rules {
-					if rule.Name == "" {
-						return nil, no("Restrictions[].Rules[].Name")
-					}
-				}
-			}
-		}
-		s["restrictions"] = opts.Restrictions
-	}
-
-	return s, nil
+	return gophercloud.BuildRequestBody(opts, "")
 }
 
 // Create accepts a CreateOpts struct and creates a new CDN service using the
 // values provided.
 func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var res CreateResult
-
-	reqBody, err := opts.ToCDNServiceCreateMap()
+	var r CreateResult
+	b, err := opts.ToCDNServiceCreateMap()
 	if err != nil {
-		res.Err = err
-		return res
+		r.Err = err
+		return r
 	}
-
 	// Send request to API
-	resp, err := c.Post(createURL(c), &reqBody, nil, nil)
-	res.Header = resp.Header
-	res.Err = err
-	return res
+	resp, err := c.Post(createURL(c), &b, nil, nil)
+	r.Header = resp.Header
+	r.Err = err
+	return r
 }
 
 // Get retrieves a specific service based on its URL or its unique ID. For
@@ -195,9 +117,9 @@
 		url = getURL(c, idOrURL)
 	}
 
-	var res GetResult
-	_, res.Err = c.Get(url, &res.Body, nil)
-	return res
+	var r GetResult
+	_, r.Err = c.Get(url, &r.Body, nil)
+	return r
 }
 
 // Path is a JSON pointer location that indicates which service parameter is being added, replaced,
@@ -251,11 +173,11 @@
 
 // ToCDNServiceUpdateMap converts an Insertion into a request body fragment suitable for the
 // Update call.
-func (i Insertion) ToCDNServiceUpdateMap() map[string]interface{} {
+func (opts Insertion) ToCDNServiceUpdateMap() map[string]interface{} {
 	return map[string]interface{}{
 		"op":    "add",
-		"path":  i.Value.renderRootOr(func(p Path) string { return p.renderIndex(i.Index) }),
-		"value": i.Value.toPatchValue(),
+		"path":  opts.Value.renderRootOr(func(p Path) string { return p.renderIndex(opts.Index) }),
+		"value": opts.Value.toPatchValue(),
 	}
 }
 
@@ -320,16 +242,17 @@
 
 // ToCDNServiceUpdateMap converts a Removal into a request body fragment suitable for the
 // Update call.
-func (r Removal) ToCDNServiceUpdateMap() map[string]interface{} {
-	result := map[string]interface{}{"op": "remove"}
-	if r.All {
-		result["path"] = r.Path.renderRoot()
+func (opts Removal) ToCDNServiceUpdateMap() map[string]interface{} {
+	b := map[string]interface{}{"op": "remove"}
+	if opts.All {
+		b["path"] = opts.Path.renderRoot()
 	} else {
-		result["path"] = r.Path.renderIndex(r.Index)
+		b["path"] = opts.Path.renderIndex(opts.Index)
 	}
-	return result
+	return b
 }
 
+// UpdateOpts is a slice of Patches used to update a CDN service
 type UpdateOpts []Patch
 
 // Update accepts a slice of Patch operations (Insertion, Append, Replacement or Removal) and
@@ -345,19 +268,19 @@
 		url = updateURL(c, idOrURL)
 	}
 
-	reqBody := make([]map[string]interface{}, len(opts))
+	b := make([]map[string]interface{}, len(opts))
 	for i, patch := range opts {
-		reqBody[i] = patch.ToCDNServiceUpdateMap()
+		b[i] = patch.ToCDNServiceUpdateMap()
 	}
 
 	resp, err := c.Request("PATCH", url, &gophercloud.RequestOpts{
-		JSONBody: &reqBody,
+		JSONBody: &b,
 		OkCodes:  []int{202},
 	})
-	var result UpdateResult
-	result.Header = resp.Header
-	result.Err = err
-	return result
+	var r UpdateResult
+	r.Header = resp.Header
+	r.Err = err
+	return r
 }
 
 // Delete accepts a service's ID or its URL and deletes the CDN service
@@ -372,7 +295,7 @@
 		url = deleteURL(c, idOrURL)
 	}
 
-	var res DeleteResult
-	_, res.Err = c.Delete(url, nil)
-	return res
+	var r DeleteResult
+	_, r.Err = c.Delete(url, nil)
+	return r
 }
diff --git a/openstack/cdn/v1/services/results.go b/openstack/cdn/v1/services/results.go
index 6de3497..f9a1caa 100644
--- a/openstack/cdn/v1/services/results.go
+++ b/openstack/cdn/v1/services/results.go
@@ -9,7 +9,7 @@
 type Domain struct {
 	// Specifies the domain used to access the assets on their website, for which
 	// a CNAME is given to the CDN provider.
-	Domain string `json:"domain"`
+	Domain string `json:"domain" required:"true"`
 	// Specifies the protocol used to access the assets on this domain. Only "http"
 	// or "https" are currently allowed. The default is "http".
 	Protocol string `json:"protocol,omitempty"`
@@ -54,17 +54,17 @@
 // OriginRule represents a rule that defines when an origin should be accessed.
 type OriginRule struct {
 	// Specifies the name of this rule.
-	Name string `json:"name"`
+	Name string `json:"name" required:"true"`
 	// Specifies the request URL this rule should match for this origin to be used. Regex is supported.
-	RequestURL string `json:"request_url"`
+	RequestURL string `json:"request_url" required:"true"`
 }
 
 // Origin specifies a list of origin domains or IP addresses where the original assets are stored.
 type Origin struct {
 	// Specifies the URL or IP address to pull origin content from.
-	Origin string `json:"origin"`
+	Origin string `json:"origin" required:"true"`
 	// Specifies the port used to access the origin. The default is port 80.
-	Port int `json:"port"`
+	Port int `json:"port,omitempty"`
 	// Specifies whether or not to use HTTPS to access the origin. The default
 	// is false.
 	SSL bool `json:"ssl"`
@@ -119,17 +119,17 @@
 // TTLRule specifies a rule that determines if a TTL should be applied to an asset.
 type TTLRule struct {
 	// Specifies the name of this rule.
-	Name string `json:"name"`
+	Name string `json:"name" required:"true"`
 	// Specifies the request URL this rule should match for this TTL to be used. Regex is supported.
-	RequestURL string `json:"request_url"`
+	RequestURL string `json:"request_url" required:"true"`
 }
 
 // CacheRule specifies the TTL rules for the assets under this service.
 type CacheRule struct {
 	// Specifies the name of this caching rule. Note: 'default' is a reserved name used for the default TTL setting.
-	Name string `json:"name"`
+	Name string `json:"name" required:"true"`
 	// Specifies the TTL to apply.
-	TTL int `json:"ttl"`
+	TTL int `json:"ttl,omitempty"`
 	// Specifies a collection of rules that determine if this TTL should be applied to an asset.
 	Rules []TTLRule `json:"rules,omitempty"`
 }
@@ -177,17 +177,17 @@
 // RestrictionRule specifies a rule that determines if this restriction should be applied to an asset.
 type RestrictionRule struct {
 	// Specifies the name of this rule.
-	Name string `json:"name"`
+	Name string `json:"name" required:"true"`
 	// Specifies the http host that requests must come from.
-	Referrer string `json:"referrer"`
+	Referrer string `json:"referrer,omitempty"`
 }
 
 // Restriction specifies a restriction that defines who can access assets (content from the CDN cache).
 type Restriction struct {
 	// Specifies the name of this restriction.
-	Name string `json:"name"`
+	Name string `json:"name" required:"true"`
 	// Specifies a collection of rules that determine if this TTL should be applied to an asset.
-	Rules []RestrictionRule `json:"rules"`
+	Rules []RestrictionRule `json:"rules,omitempty"`
 }
 
 // Error specifies an error that occurred during the previous service action.