Added functionality for updating and resetting compute quotas. (#214)
* Added functionality for updating and resetting compute quotas.
Unit and acceptance tests added.
* Forgot to add my latest changes.
Modified acceptance test to better find the tenant-id
* Improved test coverage.
And fixed a bug while doing this.
* Moved FillFromQuotaSet to acceptance test package
Refractored ToComputeQuotaUpdateMap()
diff --git a/openstack/compute/v2/extensions/quotasets/requests.go b/openstack/compute/v2/extensions/quotasets/requests.go
index 76beb17..64fc8ff 100644
--- a/openstack/compute/v2/extensions/quotasets/requests.go
+++ b/openstack/compute/v2/extensions/quotasets/requests.go
@@ -10,3 +10,70 @@
_, res.Err = client.Get(getURL(client, tenantID), &res.Body, nil)
return res
}
+
+//Updates the quotas for the given tenantID and returns the new quota-set
+func Update(client *gophercloud.ServiceClient, tenantID string, opts UpdateOptsBuilder) (res UpdateResult) {
+ reqBody, err := opts.ToComputeQuotaUpdateMap()
+ if err != nil {
+ res.Err = err
+ return
+ }
+
+ _, res.Err = client.Put(updateURL(client, tenantID), reqBody, &res.Body, &gophercloud.RequestOpts{OkCodes: []int{200}})
+ return res
+}
+
+//Resets the uotas for the given tenant to their default values
+func Delete(client *gophercloud.ServiceClient, tenantID string) (res DeleteResult) {
+ _, res.Err = client.Delete(deleteURL(client, tenantID), nil)
+ return
+}
+
+//Options for Updating the quotas of a Tenant
+//All int-values are pointers so they can be nil if they are not needed
+//you can use gopercloud.IntToPointer() for convenience
+type UpdateOpts struct {
+ //FixedIps is number of fixed ips alloted this quota_set
+ FixedIps *int `json:"fixed_ips,omitempty"`
+ // FloatingIps is number of floating ips alloted this quota_set
+ FloatingIps *int `json:"floating_ips,omitempty"`
+ // InjectedFileContentBytes is content bytes allowed for each injected file
+ InjectedFileContentBytes *int `json:"injected_file_content_bytes,omitempty"`
+ // InjectedFilePathBytes is allowed bytes for each injected file path
+ InjectedFilePathBytes *int `json:"injected_file_path_bytes,omitempty"`
+ // InjectedFiles is injected files allowed for each project
+ InjectedFiles *int `json:"injected_files,omitempty"`
+ // KeyPairs is number of ssh keypairs
+ KeyPairs *int `json:"key_pairs,omitempty"`
+ // MetadataItems is number of metadata items allowed for each instance
+ MetadataItems *int `json:"metadata_items,omitempty"`
+ // Ram is megabytes allowed for each instance
+ Ram *int `json:"ram,omitempty"`
+ // SecurityGroupRules is rules allowed for each security group
+ SecurityGroupRules *int `json:"security_group_rules,omitempty"`
+ // SecurityGroups security groups allowed for each project
+ SecurityGroups *int `json:"security_groups,omitempty"`
+ // Cores is number of instance cores allowed for each project
+ Cores *int `json:"cores,omitempty"`
+ // Instances is number of instances allowed for each project
+ Instances *int `json:"instances,omitempty"`
+ // Number of ServerGroups allowed for the project
+ ServerGroups *int `json:"server_groups,omitempty"`
+ // Max number of Members for each ServerGroup
+ ServerGroupMembers *int `json:"server_group_members,omitempty"`
+ //Users can force the update even if the quota has already been used and the reserved quota exceeds the new quota.
+ Force bool `json:"force,omitempty"`
+}
+
+type UpdateOptsBuilder interface {
+ //Extra specific name to prevent collisions with interfaces for other quotas (e.g. neutron)
+ ToComputeQuotaUpdateMap() (map[string]interface{}, error)
+}
+
+func (opts UpdateOpts) ToComputeQuotaUpdateMap() (map[string]interface{}, error) {
+
+ return gophercloud.BuildRequestBody(opts, "quota_set")
+}
+
+
+
diff --git a/openstack/compute/v2/extensions/quotasets/results.go b/openstack/compute/v2/extensions/quotasets/results.go
index f6c4e5a..44e6b06 100644
--- a/openstack/compute/v2/extensions/quotasets/results.go
+++ b/openstack/compute/v2/extensions/quotasets/results.go
@@ -20,7 +20,7 @@
// InjectedFiles is injected files allowed for each project
InjectedFiles int `json:"injected_files"`
// KeyPairs is number of ssh keypairs
- KeyPairs int `json:"keypairs"`
+ KeyPairs int `json:"key_pairs"`
// MetadataItems is number of metadata items allowed for each instance
MetadataItems int `json:"metadata_items"`
// Ram is megabytes allowed for each instance
@@ -33,6 +33,10 @@
Cores int `json:"cores"`
// Instances is number of instances allowed for each project
Instances int `json:"instances"`
+ // Number of ServerGroups allowed for the project
+ ServerGroups int `json:"server_groups"`
+ // Max number of Members for each ServerGroup
+ ServerGroupMembers int `json:"server_group_members"`
}
// QuotaSetPage stores a single, only page of QuotaSet results from a List call.
@@ -73,3 +77,15 @@
type GetResult struct {
quotaResult
}
+
+// UpdateResult is the response from a Update operation. Call its Extract method to interpret it
+// as a QuotaSet.
+type UpdateResult struct {
+ quotaResult
+}
+
+// DeleteResult is the response from a Delete operation. Call its Extract method to interpret it
+// as a QuotaSet.
+type DeleteResult struct {
+ quotaResult
+}
diff --git a/openstack/compute/v2/extensions/quotasets/testing/fixtures.go b/openstack/compute/v2/extensions/quotasets/testing/fixtures.go
index 3fef872..0945f9b 100644
--- a/openstack/compute/v2/extensions/quotasets/testing/fixtures.go
+++ b/openstack/compute/v2/extensions/quotasets/testing/fixtures.go
@@ -4,10 +4,10 @@
"fmt"
"net/http"
"testing"
-
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/quotasets"
th "github.com/gophercloud/gophercloud/testhelper"
"github.com/gophercloud/gophercloud/testhelper/client"
+ "github.com/gophercloud/gophercloud"
)
// GetOutput is a sample response to a Get call.
@@ -22,12 +22,13 @@
"injected_files" : 5,
"metadata_items" : 128,
"ram" : 200000,
- "keypairs" : 10,
- "injected_file_path_bytes" : 255
+ "key_pairs" : 10,
+ "injected_file_path_bytes" : 255,
+ "server_groups" : 2,
+ "server_group_members" : 3
}
}
`
-
const FirstTenantID = "555544443333222211110000ffffeeee"
// FirstQuotaSet is the first result in ListOutput.
@@ -44,6 +45,34 @@
SecurityGroups: 10,
Cores: 200,
Instances: 25,
+ ServerGroups: 2,
+ ServerGroupMembers: 3,
+}
+
+
+//The expected update Body. Is also returned by PUT request
+const UpdateOutput = `{"quota_set":{"cores":200,"fixed_ips":0,"floating_ips":0,"injected_file_content_bytes":10240,"injected_file_path_bytes":255,"injected_files":5,"instances":25,"key_pairs":10,"metadata_items":128,"ram":200000,"security_group_rules":20,"security_groups":10,"server_groups":2,"server_group_members":3}}`
+
+//The expected partialupdate Body. Is also returned by PUT request
+const PartialUpdateBody = `{"quota_set":{"cores":200, "force":true}}`
+
+
+//Result of Quota-update
+var UpdatedQuotaSet = quotasets.UpdateOpts{
+ FixedIps: gophercloud.IntToPointer(0),
+ FloatingIps: gophercloud.IntToPointer(0),
+ InjectedFileContentBytes: gophercloud.IntToPointer(10240),
+ InjectedFilePathBytes: gophercloud.IntToPointer(255),
+ InjectedFiles: gophercloud.IntToPointer(5),
+ KeyPairs: gophercloud.IntToPointer(10),
+ MetadataItems: gophercloud.IntToPointer(128),
+ Ram: gophercloud.IntToPointer(200000),
+ SecurityGroupRules: gophercloud.IntToPointer(20),
+ SecurityGroups: gophercloud.IntToPointer(10),
+ Cores: gophercloud.IntToPointer(200),
+ Instances: gophercloud.IntToPointer(25),
+ ServerGroups: gophercloud.IntToPointer(2),
+ ServerGroupMembers: gophercloud.IntToPointer(3),
}
// HandleGetSuccessfully configures the test server to respond to a Get request for sample tenant
@@ -56,3 +85,36 @@
fmt.Fprintf(w, GetOutput)
})
}
+
+// HandlePutSuccessfully configures the test server to respond to a Put request for sample tenant
+func HandlePutSuccessfully(t *testing.T) {
+ th.Mux.HandleFunc("/os-quota-sets/"+FirstTenantID, func(w http.ResponseWriter, r *http.Request) {
+ th.TestMethod(t, r, "PUT")
+ th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+ th.TestJSONRequest(t,r,UpdateOutput)
+ w.Header().Add("Content-Type", "application/json")
+ fmt.Fprintf(w, UpdateOutput)
+ })
+}
+
+// HandlePartialPutSuccessfully configures the test server to respond to a Put request for sample tenant that only containes specific values
+func HandlePartialPutSuccessfully(t *testing.T) {
+ th.Mux.HandleFunc("/os-quota-sets/"+FirstTenantID, func(w http.ResponseWriter, r *http.Request) {
+ th.TestMethod(t, r, "PUT")
+ th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+ th.TestJSONRequest(t,r,PartialUpdateBody)
+ w.Header().Add("Content-Type", "application/json")
+ fmt.Fprintf(w, UpdateOutput)
+ })
+}
+
+// HandleDeleteSuccessfully configures the test server to respond to a Delete request for sample tenant
+func HandleDeleteSuccessfully(t *testing.T) {
+ th.Mux.HandleFunc("/os-quota-sets/"+FirstTenantID, func(w http.ResponseWriter, r *http.Request) {
+ th.TestMethod(t, r, "DELETE")
+ th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+ th.TestBody(t,r,"")
+ w.Header().Add("Content-Type", "application/json")
+ w.WriteHeader(202)
+ })
+}
\ No newline at end of file
diff --git a/openstack/compute/v2/extensions/quotasets/testing/requests_test.go b/openstack/compute/v2/extensions/quotasets/testing/requests_test.go
index 8fc1fd4..e63f6aa 100644
--- a/openstack/compute/v2/extensions/quotasets/testing/requests_test.go
+++ b/openstack/compute/v2/extensions/quotasets/testing/requests_test.go
@@ -6,6 +6,8 @@
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/quotasets"
th "github.com/gophercloud/gophercloud/testhelper"
"github.com/gophercloud/gophercloud/testhelper/client"
+ "github.com/gophercloud/gophercloud"
+ "errors"
)
func TestGet(t *testing.T) {
@@ -16,3 +18,48 @@
th.AssertNoErr(t, err)
th.CheckDeepEquals(t, &FirstQuotaSet, actual)
}
+
+func TestUpdate(t *testing.T) {
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+ HandlePutSuccessfully(t)
+ actual, err := quotasets.Update(client.ServiceClient(), FirstTenantID, UpdatedQuotaSet).Extract()
+ th.AssertNoErr(t, err)
+ th.CheckDeepEquals(t, &FirstQuotaSet, actual)
+}
+
+func TestPartialUpdate(t *testing.T) {
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+ HandlePartialPutSuccessfully(t)
+ opts := quotasets.UpdateOpts{Cores:gophercloud.IntToPointer(200), Force:true}
+ actual, err := quotasets.Update(client.ServiceClient(), FirstTenantID, opts).Extract()
+ th.AssertNoErr(t, err)
+ th.CheckDeepEquals(t, &FirstQuotaSet, actual)
+}
+
+func TestDelete(t *testing.T) {
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+ HandleDeleteSuccessfully(t)
+ _, err := quotasets.Delete(client.ServiceClient(), FirstTenantID).Extract()
+ th.AssertNoErr(t, err)
+}
+
+type ErrorUpdateOpts quotasets.UpdateOpts;
+
+func (opts ErrorUpdateOpts) ToComputeQuotaUpdateMap() (map[string]interface{}, error) {
+ return nil, errors.New("This is an error")
+}
+
+func TestErrorInToComputeQuotaUpdateMap(t *testing.T){
+ opts := &ErrorUpdateOpts{}
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+ HandlePutSuccessfully(t)
+ _, err := quotasets.Update(client.ServiceClient(), FirstTenantID, opts).Extract()
+ if err == nil{
+ t.Fatal("Error handling failed")
+ }
+}
+
diff --git a/openstack/compute/v2/extensions/quotasets/urls.go b/openstack/compute/v2/extensions/quotasets/urls.go
index e910376..4e15613 100644
--- a/openstack/compute/v2/extensions/quotasets/urls.go
+++ b/openstack/compute/v2/extensions/quotasets/urls.go
@@ -11,3 +11,11 @@
func getURL(c *gophercloud.ServiceClient, tenantID string) string {
return c.ServiceURL(resourcePath, tenantID)
}
+
+func updateURL(c *gophercloud.ServiceClient, tenantID string) string {
+ return getURL(c, tenantID)
+}
+
+func deleteURL(c *gophercloud.ServiceClient, tenantID string) string {
+ return getURL(c, tenantID)
+}
\ No newline at end of file