initial os-quota-set api; GET unit test
diff --git a/openstack/compute/v2/extensions/quota/doc.go b/openstack/compute/v2/extensions/quota/doc.go
new file mode 100644
index 0000000..9d62849
--- /dev/null
+++ b/openstack/compute/v2/extensions/quota/doc.go
@@ -0,0 +1,3 @@
+// Package quotas provides information and interaction with Quotas
+// extension for the OpenStack Compute service.
+package quotas
diff --git a/openstack/compute/v2/extensions/quota/fixtures.go b/openstack/compute/v2/extensions/quota/fixtures.go
new file mode 100644
index 0000000..992beff
--- /dev/null
+++ b/openstack/compute/v2/extensions/quota/fixtures.go
@@ -0,0 +1,59 @@
+// +build fixtures
+
+package quotas
+
+import (
+ "fmt"
+ "net/http"
+ "testing"
+
+ th "github.com/rackspace/gophercloud/testhelper"
+ "github.com/rackspace/gophercloud/testhelper/client"
+)
+
+// GetOutput is a sample response to a Get call.
+const GetOutput = `
+{
+ "quota_set" : {
+ "instances" : 25,
+ "security_groups" : 10,
+ "security_group_rules" : 20,
+ "cores" : 200,
+ "injected_file_content_bytes" : 10240,
+ "injected_files" : 5,
+ "metadata_items" : 128,
+ "ram" : 200000,
+ "keypairs" : 10,
+ "injected_file_path_bytes" : 255
+ }
+}
+`
+
+const FirstTenantID = "555544443333222211110000ffffeeee"
+
+// FirstQuota is the first result in ListOutput.
+var FirstQuota = Quota{
+ FixedIps: 0,
+ FloatingIps: 0,
+ InjectedFileContentBytes: 10240,
+ InjectedFilePathBytes: 255,
+ InjectedFiles: 5,
+ KeyPairs: 10,
+ MetadataItems: 128,
+ Ram: 200000,
+ SecurityGroupRules: 20,
+ SecurityGroups: 10,
+ Cores: 200,
+ Instances: 25,
+}
+
+// HandleGetSuccessfully configures the test server to respond to a Get request for sample tenant
+func HandleGetSuccessfully(t *testing.T) {
+ th.Mux.HandleFunc("/os-quota-sets/"+FirstTenantID, func(w http.ResponseWriter, r *http.Request) {
+ th.TestMethod(t, r, "GET")
+ th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+
+ w.Header().Add("Content-Type", "application/json")
+ fmt.Fprintf(w, GetOutput)
+ })
+}
diff --git a/openstack/compute/v2/extensions/quota/requests.go b/openstack/compute/v2/extensions/quota/requests.go
new file mode 100644
index 0000000..7c3de24
--- /dev/null
+++ b/openstack/compute/v2/extensions/quota/requests.go
@@ -0,0 +1,19 @@
+package quotas
+
+import (
+ "github.com/rackspace/gophercloud"
+ "github.com/rackspace/gophercloud/pagination"
+)
+
+func List(client *gophercloud.ServiceClient) pagination.Pager {
+ return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page {
+ return QuotaPage{pagination.SinglePageBase(r)}
+ })
+}
+
+// Get returns public data about a previously created Quota.
+func Get(client *gophercloud.ServiceClient, name string) GetResult {
+ var res GetResult
+ _, res.Err = client.Get(getURL(client, name), &res.Body, nil)
+ return res
+}
diff --git a/openstack/compute/v2/extensions/quota/requests_test.go b/openstack/compute/v2/extensions/quota/requests_test.go
new file mode 100644
index 0000000..ec55cc7
--- /dev/null
+++ b/openstack/compute/v2/extensions/quota/requests_test.go
@@ -0,0 +1,16 @@
+package quotas
+
+import (
+ th "github.com/rackspace/gophercloud/testhelper"
+ "github.com/rackspace/gophercloud/testhelper/client"
+ "testing"
+)
+
+func TestGet(t *testing.T) {
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+ HandleGetSuccessfully(t)
+ actual, err := Get(client.ServiceClient(), FirstTenantID).Extract()
+ th.AssertNoErr(t, err)
+ th.CheckDeepEquals(t, &FirstQuota, actual)
+}
diff --git a/openstack/compute/v2/extensions/quota/results.go b/openstack/compute/v2/extensions/quota/results.go
new file mode 100644
index 0000000..07031f1
--- /dev/null
+++ b/openstack/compute/v2/extensions/quota/results.go
@@ -0,0 +1,117 @@
+package quotas
+
+import (
+ "github.com/mitchellh/mapstructure"
+ "github.com/rackspace/gophercloud"
+ "github.com/rackspace/gophercloud/pagination"
+)
+
+// Quota is a set of operational limits that allow for control of compute usage.
+const sample = `
+{
+ "quota_set" : {
+ "fixed_ips" : -1,
+ "security_groups" : 10,
+ "id" : "56b6c3eb639e48c691052919e5a60dc3",
+ "injected_files" : 5,
+ "injected_file_path_bytes" : 255,
+ "cores" : 108,
+ "security_group_rules" : 20,
+ "keypairs" : 10,
+ "instances" : 25,
+ "ram" : 204800,
+ "metadata_items" : 128,
+ "injected_file_content_bytes" : 10240
+ }
+}
+`
+
+type Quota struct {
+ //ID is tenant associated with this quota_set
+ ID string `mapstructure:"id"`
+ //FixedIps is number of fixed ips alloted this quota_set
+ FixedIps int `mapstructure:"fixed_ips"`
+ // FloatingIps is number of floatinh ips alloted this quota_set
+ FloatingIps int `mapstructure:"floating_ips"`
+ // InjectedFileContentBytes is content bytes allowed for each injected file
+ InjectedFileContentBytes int `mapstructure:"injected_file_content_bytes"`
+ // InjectedFilePathBytes is allowed bytes for each injected file path
+ InjectedFilePathBytes int `mapstructure:"injected_file_path_bytes"`
+ // InjectedFiles is injected files allowed for each project
+ InjectedFiles int `mapstructure:"injected_files"`
+ // KeyPairs is number of ssh keypairs
+ KeyPairs int `mapstructure:"keypairs"`
+ // MetadataItems is number of metadata items allowed for each instance
+ MetadataItems int `mapstructure:"metadata_items"`
+ // Ram is megabytes allowed for each instance
+ Ram int `mapstructure:"ram"`
+ // SecurityGroupRules is rules allowed for each security group
+ SecurityGroupRules int `mapstructure:"security_group_rules"`
+ // SecurityGroups security groups allowed for each project
+ SecurityGroups int `mapstructure:"security_groups"`
+ // Cores is number of instance cores allowed for each project
+ Cores int `mapstructure:"cores"`
+ // Instances is number of instances allowed for each project
+ Instances int `mapstructure:"instances"`
+}
+
+// QuotaPage stores a single, only page of Quota results from a List call.
+type QuotaPage struct {
+ pagination.SinglePageBase
+}
+
+// IsEmpty determines whether or not a QuotaPage is empty.
+func (page QuotaPage) IsEmpty() (bool, error) {
+ ks, err := ExtractQuotas(page)
+ return len(ks) == 0, err
+}
+
+// ExtractQuotas interprets a page of results as a slice of Quotas.
+func ExtractQuotas(page pagination.Page) ([]Quota, error) {
+ var resp struct {
+ Quotas []Quota `mapstructure:"quotas"`
+ }
+
+ err := mapstructure.Decode(page.(QuotaPage).Body, &resp)
+ results := make([]Quota, len(resp.Quotas))
+ for i, q := range resp.Quotas {
+ results[i] = q
+ }
+ return results, err
+}
+
+type quotaResult struct {
+ gophercloud.Result
+}
+
+// Extract is a method that attempts to interpret any Quota resource response as a Quota struct.
+func (r quotaResult) Extract() (*Quota, error) {
+ if r.Err != nil {
+ return nil, r.Err
+ }
+
+ var res struct {
+ Quota *Quota `json:"quota_set" mapstructure:"quota_set"`
+ }
+
+ err := mapstructure.Decode(r.Body, &res)
+ return res.Quota, err
+}
+
+// CreateResult is the response from a Create operation. Call its Extract method to interpret it
+// as a Quota.
+type CreateResult struct {
+ quotaResult
+}
+
+// GetResult is the response from a Get operation. Call its Extract method to interpret it
+// as a Quota.
+type GetResult struct {
+ quotaResult
+}
+
+// DeleteResult is the response from a Delete operation. Call its Extract method to determine if
+// the call succeeded or failed.
+type DeleteResult struct {
+ gophercloud.ErrResult
+}
diff --git a/openstack/compute/v2/extensions/quota/urls.go b/openstack/compute/v2/extensions/quota/urls.go
new file mode 100644
index 0000000..2900b89
--- /dev/null
+++ b/openstack/compute/v2/extensions/quota/urls.go
@@ -0,0 +1,25 @@
+package quotas
+
+import "github.com/rackspace/gophercloud"
+
+const resourcePath = "os-quota-sets"
+
+func resourceURL(c *gophercloud.ServiceClient) string {
+ return c.ServiceURL(resourcePath)
+}
+
+func listURL(c *gophercloud.ServiceClient) string {
+ return resourceURL(c)
+}
+
+func createURL(c *gophercloud.ServiceClient) string {
+ return resourceURL(c)
+}
+
+func getURL(c *gophercloud.ServiceClient, name string) string {
+ return c.ServiceURL(resourcePath, name)
+}
+
+func deleteURL(c *gophercloud.ServiceClient, name string) string {
+ return getURL(c, name)
+}
diff --git a/openstack/compute/v2/extensions/quota/urls_test.go b/openstack/compute/v2/extensions/quota/urls_test.go
new file mode 100644
index 0000000..cc0daf7
--- /dev/null
+++ b/openstack/compute/v2/extensions/quota/urls_test.go
@@ -0,0 +1,40 @@
+package quotas
+
+import (
+ "testing"
+
+ th "github.com/rackspace/gophercloud/testhelper"
+ "github.com/rackspace/gophercloud/testhelper/client"
+)
+
+func TestListURL(t *testing.T) {
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+ c := client.ServiceClient()
+
+ th.CheckEquals(t, c.Endpoint+"os-quota-sets", listURL(c))
+}
+
+func TestCreateURL(t *testing.T) {
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+ c := client.ServiceClient()
+
+ th.CheckEquals(t, c.Endpoint+"os-quota-sets", createURL(c))
+}
+
+func TestGetURL(t *testing.T) {
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+ c := client.ServiceClient()
+
+ th.CheckEquals(t, c.Endpoint+"os-quota-sets/wat", getURL(c, "wat"))
+}
+
+func TestDeleteURL(t *testing.T) {
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+ c := client.ServiceClient()
+
+ th.CheckEquals(t, c.Endpoint+"os-quota-sets/wat", deleteURL(c, "wat"))
+}