Making server action result types more consistent
diff --git a/_site/openstack/client.go b/_site/openstack/client.go
new file mode 100644
index 0000000..97556d6
--- /dev/null
+++ b/_site/openstack/client.go
@@ -0,0 +1,205 @@
+package openstack
+
+import (
+	"fmt"
+	"net/url"
+
+	"github.com/rackspace/gophercloud"
+	tokens2 "github.com/rackspace/gophercloud/openstack/identity/v2/tokens"
+	tokens3 "github.com/rackspace/gophercloud/openstack/identity/v3/tokens"
+	"github.com/rackspace/gophercloud/openstack/utils"
+)
+
+const (
+	v20 = "v2.0"
+	v30 = "v3.0"
+)
+
+// NewClient prepares an unauthenticated ProviderClient instance.
+// Most users will probably prefer using the AuthenticatedClient function instead.
+// This is useful if you wish to explicitly control the version of the identity service that's used for authentication explicitly,
+// for example.
+func NewClient(endpoint string) (*gophercloud.ProviderClient, error) {
+	u, err := url.Parse(endpoint)
+	if err != nil {
+		return nil, err
+	}
+	hadPath := u.Path != ""
+	u.Path, u.RawQuery, u.Fragment = "", "", ""
+	base := u.String()
+
+	endpoint = gophercloud.NormalizeURL(endpoint)
+	base = gophercloud.NormalizeURL(base)
+
+	if hadPath {
+		return &gophercloud.ProviderClient{
+			IdentityBase:     base,
+			IdentityEndpoint: endpoint,
+		}, nil
+	}
+
+	return &gophercloud.ProviderClient{
+		IdentityBase:     base,
+		IdentityEndpoint: "",
+	}, nil
+}
+
+// AuthenticatedClient logs in to an OpenStack cloud found at the identity endpoint specified by options, acquires a token, and
+// returns a Client instance that's ready to operate.
+// It first queries the root identity endpoint to determine which versions of the identity service are supported, then chooses
+// the most recent identity service available to proceed.
+func AuthenticatedClient(options gophercloud.AuthOptions) (*gophercloud.ProviderClient, error) {
+	client, err := NewClient(options.IdentityEndpoint)
+	if err != nil {
+		return nil, err
+	}
+
+	err = Authenticate(client, options)
+	if err != nil {
+		return nil, err
+	}
+	return client, nil
+}
+
+// Authenticate or re-authenticate against the most recent identity service supported at the provided endpoint.
+func Authenticate(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error {
+	versions := []*utils.Version{
+		&utils.Version{ID: v20, Priority: 20, Suffix: "/v2.0/"},
+		&utils.Version{ID: v30, Priority: 30, Suffix: "/v3/"},
+	}
+
+	chosen, endpoint, err := utils.ChooseVersion(client.IdentityBase, client.IdentityEndpoint, versions)
+	if err != nil {
+		return err
+	}
+
+	switch chosen.ID {
+	case v20:
+		return v2auth(client, endpoint, options)
+	case v30:
+		return v3auth(client, endpoint, options)
+	default:
+		// The switch statement must be out of date from the versions list.
+		return fmt.Errorf("Unrecognized identity version: %s", chosen.ID)
+	}
+}
+
+// AuthenticateV2 explicitly authenticates against the identity v2 endpoint.
+func AuthenticateV2(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error {
+	return v2auth(client, "", options)
+}
+
+func v2auth(client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions) error {
+	v2Client := NewIdentityV2(client)
+	if endpoint != "" {
+		v2Client.Endpoint = endpoint
+	}
+
+	result := tokens2.Create(v2Client, tokens2.AuthOptions{AuthOptions: options})
+
+	token, err := result.ExtractToken()
+	if err != nil {
+		return err
+	}
+
+	catalog, err := result.ExtractServiceCatalog()
+	if err != nil {
+		return err
+	}
+
+	client.TokenID = token.ID
+	client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) {
+		return V2EndpointURL(catalog, opts)
+	}
+
+	return nil
+}
+
+// AuthenticateV3 explicitly authenticates against the identity v3 service.
+func AuthenticateV3(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error {
+	return v3auth(client, "", options)
+}
+
+func v3auth(client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions) error {
+	// Override the generated service endpoint with the one returned by the version endpoint.
+	v3Client := NewIdentityV3(client)
+	if endpoint != "" {
+		v3Client.Endpoint = endpoint
+	}
+
+	token, err := tokens3.Create(v3Client, options, nil).Extract()
+	if err != nil {
+		return err
+	}
+	client.TokenID = token.ID
+
+	client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) {
+		return V3EndpointURL(v3Client, opts)
+	}
+
+	return nil
+}
+
+// NewIdentityV2 creates a ServiceClient that may be used to interact with the v2 identity service.
+func NewIdentityV2(client *gophercloud.ProviderClient) *gophercloud.ServiceClient {
+	v2Endpoint := client.IdentityBase + "v2.0/"
+
+	return &gophercloud.ServiceClient{
+		Provider: client,
+		Endpoint: v2Endpoint,
+	}
+}
+
+// NewIdentityV3 creates a ServiceClient that may be used to access the v3 identity service.
+func NewIdentityV3(client *gophercloud.ProviderClient) *gophercloud.ServiceClient {
+	v3Endpoint := client.IdentityBase + "v3/"
+
+	return &gophercloud.ServiceClient{
+		Provider: client,
+		Endpoint: v3Endpoint,
+	}
+}
+
+// NewStorageV1 creates a ServiceClient that may be used with the v1 object storage package.
+func NewStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
+	eo.ApplyDefaults("object-store")
+	url, err := client.EndpointLocator(eo)
+	if err != nil {
+		return nil, err
+	}
+	return &gophercloud.ServiceClient{Provider: client, Endpoint: url}, nil
+}
+
+// NewComputeV2 creates a ServiceClient that may be used with the v2 compute package.
+func NewComputeV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
+	eo.ApplyDefaults("compute")
+	url, err := client.EndpointLocator(eo)
+	if err != nil {
+		return nil, err
+	}
+	return &gophercloud.ServiceClient{Provider: client, Endpoint: url}, nil
+}
+
+// NewNetworkV2 creates a ServiceClient that may be used with the v2 network package.
+func NewNetworkV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
+	eo.ApplyDefaults("network")
+	url, err := client.EndpointLocator(eo)
+	if err != nil {
+		return nil, err
+	}
+	return &gophercloud.ServiceClient{
+		Provider:     client,
+		Endpoint:     url,
+		ResourceBase: url + "v2.0/",
+	}, nil
+}
+
+// NewBlockStorageV1 creates a ServiceClient that may be used to access the v1 block storage service.
+func NewBlockStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
+	eo.ApplyDefaults("volume")
+	url, err := client.EndpointLocator(eo)
+	if err != nil {
+		return nil, err
+	}
+	return &gophercloud.ServiceClient{Provider: client, Endpoint: url}, nil
+}