Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rgw: Admin API Support for bucket quota change #18324

Merged
merged 3 commits into from
Jan 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 11 additions & 0 deletions doc/radosgw/adminops.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1913,6 +1913,17 @@ The content must include a JSON representation of the quota settings
as encoded in the corresponding read operation.


Set Quota for an Individual Bucket
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

To set a quota, the user must have ``buckets`` capability set with ``write``
permission. ::

PUT /admin/bucket?quota&uid=<uid>&bucket=<bucket-name>&quota

The content must include a JSON representation of the quota settings
as mentioned in Set Bucket Quota section above.



Standard Error Responses
Expand Down
30 changes: 30 additions & 0 deletions src/rgw/rgw_bucket.cc
Original file line number Diff line number Diff line change
Expand Up @@ -917,6 +917,27 @@ int RGWBucket::unlink(RGWBucketAdminOpState& op_state, std::string *err_msg)
return r;
}

int RGWBucket::set_quota(RGWBucketAdminOpState& op_state, std::string *err_msg)
{
rgw_bucket bucket = op_state.get_bucket();
RGWBucketInfo bucket_info;
map<string, bufferlist> attrs;
RGWObjectCtx obj_ctx(store);
int r = store->get_bucket_info(obj_ctx, bucket.tenant, bucket.name, bucket_info, NULL, &attrs);
if (r < 0) {
set_err_msg(err_msg, "could not get bucket info for bucket=" + bucket.name + ": " + cpp_strerror(-r));
return r;
}

bucket_info.quota = op_state.quota;
r = store->put_bucket_instance_info(bucket_info, false, real_time(), &attrs);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adamemerson do you think this would be a good candidate for retry_raced_bucket_write()?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the comment. I will take a look

if (r < 0) {
set_err_msg(err_msg, "ERROR: failed writing bucket instance info: " + cpp_strerror(-r));
return r;
}
return r;
}

int RGWBucket::remove(RGWBucketAdminOpState& op_state, bool bypass_gc,
bool keep_index_consistent, std::string *err_msg)
{
Expand Down Expand Up @@ -1619,6 +1640,15 @@ int RGWBucketAdminOp::info(RGWRados *store, RGWBucketAdminOpState& op_state,
return 0;
}

int RGWBucketAdminOp::set_quota(RGWRados *store, RGWBucketAdminOpState& op_state)
{
RGWBucket bucket;

int ret = bucket.init(store, op_state);
if (ret < 0)
return ret;
return bucket.set_quota(op_state);
}

void rgw_data_change::dump(Formatter *f) const
{
Expand Down
8 changes: 8 additions & 0 deletions src/rgw/rgw_bucket.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,8 @@ struct RGWBucketAdminOpState {

rgw_bucket bucket;

RGWQuotaInfo quota;

void set_fetch_stats(bool value) { stat_buckets = value; }
void set_check_objects(bool value) { check_objects = value; }
void set_fix_index(bool value) { fix_index = value; }
Expand All @@ -231,6 +233,10 @@ struct RGWBucketAdminOpState {
void set_object(std::string& object_str) {
object_name = object_str;
}
void set_quota(RGWQuotaInfo& value) {
quota = value;
}


rgw_user& get_user_id() { return uid; }
std::string& get_user_display_name() { return display_name; }
Expand Down Expand Up @@ -300,6 +306,7 @@ class RGWBucket
int remove(RGWBucketAdminOpState& op_state, bool bypass_gc = false, bool keep_index_consistent = true, std::string *err_msg = NULL);
int link(RGWBucketAdminOpState& op_state, std::string *err_msg = NULL);
int unlink(RGWBucketAdminOpState& op_state, std::string *err_msg = NULL);
int set_quota(RGWBucketAdminOpState& op_state, std::string *err_msg = NULL);

int remove_object(RGWBucketAdminOpState& op_state, std::string *err_msg = NULL);
int policy_bl_to_stream(bufferlist& bl, ostream& o);
Expand Down Expand Up @@ -331,6 +338,7 @@ class RGWBucketAdminOp
const std::list<std::string>& user_ids,
RGWFormatterFlusher& flusher,
bool warnings_only = false);
static int set_quota(RGWRados *store, RGWBucketAdminOpState& op_state);
};


Expand Down
80 changes: 80 additions & 0 deletions src/rgw/rgw_rest_bucket.cc
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,84 @@ void RGWOp_Bucket_Remove::execute()
http_ret = RGWBucketAdminOp::remove_bucket(store, op_state);
}

class RGWOp_Set_Bucket_Quota : public RGWRESTOp {

public:
RGWOp_Set_Bucket_Quota() {}

int check_caps(RGWUserCaps& caps) {
return caps.check_cap("buckets", RGW_CAP_WRITE);
}

void execute();

virtual const string name() { return "set_bucket_quota"; }
};

#define QUOTA_INPUT_MAX_LEN 1024

void RGWOp_Set_Bucket_Quota::execute()
{
bool uid_arg_existed = false;
std::string uid_str;
RESTArgs::get_string(s, "uid", uid_str, &uid_str, &uid_arg_existed);
if (! uid_arg_existed) {
http_ret = -EINVAL;
return;
}
rgw_user uid(uid_str);
bool bucket_arg_existed = false;
std::string bucket;
RESTArgs::get_string(s, "bucket", bucket, &bucket, &bucket_arg_existed);
if (! bucket_arg_existed) {
http_ret = -EINVAL;
return;
}

bool use_http_params;

if (s->content_length > 0) {
use_http_params = false;
} else {
const char *encoding = s->info.env->get("HTTP_TRANSFER_ENCODING");
use_http_params = (!encoding || strcmp(encoding, "chunked") != 0);
}
RGWQuotaInfo quota;
if (!use_http_params) {
bool empty;
http_ret = rgw_rest_get_json_input(store->ctx(), s, quota, QUOTA_INPUT_MAX_LEN, &empty);
if (http_ret < 0) {
if (!empty)
return;
/* was probably chunked input, but no content provided, configure via http params */
use_http_params = true;
}
}
if (use_http_params) {
RGWBucketInfo bucket_info;
map<string, bufferlist> attrs;
RGWObjectCtx obj_ctx(store);
http_ret = store->get_bucket_info(obj_ctx, uid.tenant, bucket, bucket_info, NULL, &attrs);
if (http_ret < 0) {
return;
}
RGWQuotaInfo *old_quota = &bucket_info.quota;
int64_t old_max_size_kb = rgw_rounded_kb(old_quota->max_size);
int64_t max_size_kb;
RESTArgs::get_int64(s, "max-objects", old_quota->max_objects, &quota.max_objects);
RESTArgs::get_int64(s, "max-size-kb", old_max_size_kb, &max_size_kb);
quota.max_size = max_size_kb * 1024;
RESTArgs::get_bool(s, "enabled", old_quota->enabled, &quota.enabled);
}

RGWBucketAdminOpState op_state;
op_state.set_user_id(uid);
op_state.set_bucket_name(bucket);
op_state.set_quota(quota);

http_ret = RGWBucketAdminOp::set_quota(store, op_state);
}

class RGWOp_Object_Remove: public RGWRESTOp {

public:
Expand Down Expand Up @@ -250,6 +328,8 @@ RGWOp *RGWHandler_Bucket::op_get()

RGWOp *RGWHandler_Bucket::op_put()
{
if (s->info.args.sub_resource_exists("quota"))
return new RGWOp_Set_Bucket_Quota;
return new RGWOp_Bucket_Link;
}

Expand Down