Let's expose it in openstack/ instead.

I can't move endpoint location into a method on anything in identity v3, so for symmetry
I'm keeping them in the openstack/ provider directory. I will export them, though, so I
can call them in rackspace/.
diff --git a/openstack/endpoint_location.go b/openstack/endpoint_location.go
new file mode 100644
index 0000000..52c5221
--- /dev/null
+++ b/openstack/endpoint_location.go
@@ -0,0 +1,52 @@
+package openstack
+
+import (
+	"fmt"
+
+	"github.com/rackspace/gophercloud"
+	tokens2 "github.com/rackspace/gophercloud/openstack/identity/v2/tokens"
+)
+
+// V2EndpointURL discovers the endpoint URL for a specific service from a ServiceCatalog acquired
+// during the v2 identity service. The specified EndpointOpts are used to identify a unique,
+// unambiguous endpoint to return. It's an error both when multiple endpoints match the provided
+// criteria and when none do. The minimum that can be specified is a Type, but you will also often
+// need to specify a Name and/or a Region depending on what's available on your OpenStack
+// deployment.
+func V2EndpointURL(catalog *tokens2.ServiceCatalog, opts gophercloud.EndpointOpts) (string, error) {
+	// Extract Endpoints from the catalog entries that match the requested Type, Name if provided, and Region if provided.
+	var endpoints = make([]tokens2.Endpoint, 0, 1)
+	for _, entry := range catalog.Entries {
+		if (entry.Type == opts.Type) && (opts.Name == "" || entry.Name == opts.Name) {
+			for _, endpoint := range entry.Endpoints {
+				if opts.Region == "" || endpoint.Region == opts.Region {
+					endpoints = append(endpoints, endpoint)
+				}
+			}
+		}
+	}
+
+	// Report an error if the options were ambiguous.
+	if len(endpoints) == 0 {
+		return "", gophercloud.ErrEndpointNotFound
+	}
+	if len(endpoints) > 1 {
+		return "", fmt.Errorf("Discovered %d matching endpoints: %#v", len(endpoints), endpoints)
+	}
+
+	// Extract the appropriate URL from the matching Endpoint.
+	for _, endpoint := range endpoints {
+		switch opts.Availability {
+		case gophercloud.AvailabilityPublic:
+			return gophercloud.NormalizeURL(endpoint.PublicURL), nil
+		case gophercloud.AvailabilityInternal:
+			return gophercloud.NormalizeURL(endpoint.InternalURL), nil
+		case gophercloud.AvailabilityAdmin:
+			return gophercloud.NormalizeURL(endpoint.AdminURL), nil
+		default:
+			return "", fmt.Errorf("Unexpected availability in endpoint query: %s", opts.Availability)
+		}
+	}
+
+	return "", gophercloud.ErrEndpointNotFound
+}
diff --git a/openstack/identity/v2/tokens/results.go b/openstack/identity/v2/tokens/results.go
index d5ad8c5..e88b2c7 100644
--- a/openstack/identity/v2/tokens/results.go
+++ b/openstack/identity/v2/tokens/results.go
@@ -1,7 +1,6 @@
 package tokens
 
 import (
-	"fmt"
 	"time"
 
 	"github.com/mitchellh/mapstructure"
@@ -132,45 +131,3 @@
 func createErr(err error) CreateResult {
 	return CreateResult{gophercloud.CommonResult{Err: err}}
 }
-
-// EndpointURL discovers the endpoint URL for a specific service with a ServiceCatalog. The
-// specified EndpointOpts are used to identify a unique, unambiguous endpoint to return. The minimum
-// that can be specified is a Type, but you will also often need to specify a Name and/or a Region
-// depending on what's available on your OpenStack deployment.
-func (catalog *ServiceCatalog) EndpointURL(opts gophercloud.EndpointOpts) (string, error) {
-	// Extract Endpoints from the catalog entries that match the requested Type, Name if provided, and Region if provided.
-	var endpoints = make([]Endpoint, 0, 1)
-	for _, entry := range catalog.Entries {
-		if (entry.Type == opts.Type) && (opts.Name == "" || entry.Name == opts.Name) {
-			for _, endpoint := range entry.Endpoints {
-				if opts.Region == "" || endpoint.Region == opts.Region {
-					endpoints = append(endpoints, endpoint)
-				}
-			}
-		}
-	}
-
-	// Report an error if the options were ambiguous.
-	if len(endpoints) == 0 {
-		return "", gophercloud.ErrEndpointNotFound
-	}
-	if len(endpoints) > 1 {
-		return "", fmt.Errorf("Discovered %d matching endpoints: %#v", len(endpoints), endpoints)
-	}
-
-	// Extract the appropriate URL from the matching Endpoint.
-	for _, endpoint := range endpoints {
-		switch opts.Availability {
-		case gophercloud.AvailabilityPublic:
-			return gophercloud.NormalizeURL(endpoint.PublicURL), nil
-		case gophercloud.AvailabilityInternal:
-			return gophercloud.NormalizeURL(endpoint.InternalURL), nil
-		case gophercloud.AvailabilityAdmin:
-			return gophercloud.NormalizeURL(endpoint.AdminURL), nil
-		default:
-			return "", fmt.Errorf("Unexpected availability in endpoint query: %s", opts.Availability)
-		}
-	}
-
-	return "", gophercloud.ErrEndpointNotFound
-}