Add ListAddresses method for cloud servers
diff --git a/acceptance/14-list-addresses.go b/acceptance/14-list-addresses.go
new file mode 100644
index 0000000..d1835e1
--- /dev/null
+++ b/acceptance/14-list-addresses.go
@@ -0,0 +1,44 @@
+package main
+
+import (
+	"fmt"
+	"flag"
+	"github.com/rackspace/gophercloud"
+)
+
+var quiet = flag.Bool("quiet", false, "Quiet mode, for acceptance testing.  $? still indicates errors though.")
+
+func main() {
+	flag.Parse()
+	withIdentity(false, func(acc gophercloud.AccessProvider) {
+		withServerApi(acc, func(servers gophercloud.CloudServersProvider) {
+			log("Creating server")
+			id, err := createServer(servers, "", "", "", "")
+			if err != nil {
+				panic(err)
+			}
+			waitForServerState(servers, id, "ACTIVE")
+			defer servers.DeleteServerById(id)
+
+			log("Getting list of addresses...")
+			addresses, err := servers.ListAddresses(id)
+			if (err != nil) && (err != gophercloud.WarnUnauthoritative) {
+				panic(err)
+			}
+			if err == gophercloud.WarnUnauthoritative {
+				log("Uh oh -- got a response back, but it's not authoritative for some reason.")
+			}
+			for _, addr := range addresses.Public {
+				log("Address:", addr.Addr, "  IPv", addr.Version)
+			}
+
+			log("Done")
+		})
+	})
+}
+
+func log(s... interface{}) {
+	if !*quiet {
+		fmt.Println(s...)
+	}
+}
diff --git a/errors.go b/errors.go
index 5ea3991..1719fd2 100644
--- a/errors.go
+++ b/errors.go
@@ -35,3 +35,10 @@
 // responsible for a previous request bombing with an error, but pass in an
 // error interface which doesn't belong to the web client.
 var ErrError = fmt.Errorf("Attempt to solicit actual HTTP response code from error entity which doesn't know")
+
+// WarnUnauthoritative warnings happen when a service believes its response
+// to be correct, but is not in a position of knowing for sure at the moment.
+// For example, the service could be responding with cached data that has
+// exceeded its time-to-live setting, but which has not yet received an official
+// update from an authoritative source.
+var WarnUnauthoritative = fmt.Errorf("Unauthoritative data")
diff --git a/interfaces.go b/interfaces.go
index 626e531..c986fce 100644
--- a/interfaces.go
+++ b/interfaces.go
@@ -128,6 +128,14 @@
 	// Other providers may reserve the right to act on additional fields.
 	RebuildServer(id string, ns NewServer) (*Server, error)
 
+	// Addresses
+
+	// ListAddresses yields the list of available addresses for the server.
+	// This information is also returned by ServerById() in the Server.Addresses
+	// field.  However, if you have a lot of servers and all you need are addresses,
+	// this function might be more efficient.
+	ListAddresses(id string) (AddressSet, error)
+
 	// Images
 
 	// ListImages yields the list of available operating system images.  This function
diff --git a/servers.go b/servers.go
index 85f49ff..7133d93 100644
--- a/servers.go
+++ b/servers.go
@@ -288,6 +288,32 @@
 	return s, err
 }
 
+// See the CloudServersProvider interface for details.
+func (gsp *genericServersProvider) ListAddresses(id string) (AddressSet, error) {
+	var pas *AddressSet
+	var statusCode int
+
+	err := gsp.context.WithReauth(gsp.access, func() error {
+		ep := fmt.Sprintf("%s/servers/%s/ips", gsp.endpoint, id)
+		return perigee.Get(ep, perigee.Options{
+			Results: &struct{ Addresses **AddressSet }{&pas},
+			MoreHeaders: map[string]string{
+				"X-Auth-Token": gsp.access.AuthToken(),
+			},
+			OkCodes: []int{200, 203},
+			StatusCode: &statusCode,
+		})
+	})
+
+	if err != nil {
+		if statusCode == 203 {
+			err = WarnUnauthoritative
+		}
+	}
+
+	return *pas, err
+}
+
 // RaxBandwidth provides measurement of server bandwidth consumed over a given audit interval.
 type RaxBandwidth struct {
 	AuditPeriodEnd    string `json:"audit_period_end"`