]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: implement restful set user quota request
authorYehuda Sadeh <yehuda@inktank.com>
Wed, 15 Jan 2014 22:35:57 +0000 (14:35 -0800)
committerYehuda Sadeh <yehuda@inktank.com>
Fri, 24 Jan 2014 23:07:36 +0000 (15:07 -0800)
Signed-off-by: Yehuda Sadeh <yehuda@inktank.com>
src/rgw/rgw_rest.h
src/rgw/rgw_rest_replica_log.cc
src/rgw/rgw_rest_user.cc

index 15ac863aa52d99467bb28d4eb59e69c354dd089e..28b8a911cb31b76c63e1c86c926791c438ab24f0 100644 (file)
@@ -2,6 +2,8 @@
 #define CEPH_RGW_REST_H
 #define TIME_BUF_SIZE 128
 
+#include "common/ceph_json.h"
+#include "include/assert.h" /* needed because of common/ceph_json.h */
 #include "rgw_op.h"
 #include "rgw_formats.h"
 
@@ -18,6 +20,41 @@ extern void rgw_flush_formatter(struct req_state *s,
 
 extern int rgw_rest_read_all_input(struct req_state *s, char **data, int *plen, int max_len);
 
+template <class T>
+int rgw_rest_get_json_input(CephContext *cct, req_state *s, T& out, int max_len, bool *empty)
+{
+  int rv, data_len;
+  char *data;
+
+  if (empty)
+    *empty = false;
+
+  if ((rv = rgw_rest_read_all_input(s, &data, &data_len, max_len)) < 0) {
+    return rv;
+  }
+
+  if (!data_len) {
+    if (empty) {
+      *empty = true;
+    }
+
+    return -EINVAL;
+  }
+
+  JSONParser parser;
+
+  if (!parser.parse(data, data_len)) {
+    free(data);
+    return -EINVAL;
+  }
+
+  decode_json_obj(out, &parser);
+
+  free(data);
+  return 0;
+}
+
+
 class RESTArgs {
 public:
   static int get_string(struct req_state *s, const string& name, const string& def_val, string *val, bool *existed = NULL);
index 2543f32fba6e84f296103ddae883d20ffb033b78..e7dd962f0f7647bc0a93e2d3b156a56941a336d5 100644 (file)
@@ -38,29 +38,6 @@ static int parse_to_utime(string& in, utime_t& out) {
 }
 
 
-template <class T>
-static int get_input(req_state *s, T& out) {
-  int rv, data_len;
-  char *data;
-
-  if ((rv = rgw_rest_read_all_input(s, &data, &data_len, REPLICA_INPUT_MAX_LEN)) < 0) {
-    dout(5) << "Error - reading input data - " << rv << dendl;
-    return rv;
-  }
-
-  JSONParser parser;
-
-  if (!parser.parse(data, data_len)) {
-    free(data);
-    return -EINVAL;
-  }
-
-  decode_json_obj(out, &parser);
-  
-  free(data);
-  return 0;
-}
-
 void RGWOp_OBJLog_SetBounds::execute() {
   string id_str = s->info.args.get("id"),
          marker = s->info.args.get("marker"),
@@ -97,7 +74,8 @@ void RGWOp_OBJLog_SetBounds::execute() {
   bufferlist bl;
   list<RGWReplicaItemMarker> markers;
 
-  if ((http_ret = get_input(s, markers)) < 0) {
+  if ((http_ret = rgw_rest_get_json_input(store->ctx(), s, markers, REPLICA_INPUT_MAX_LEN, NULL)) < 0) {
+    dout(5) << "Error - retrieving input data - " << http_ret << dendl;
     return;
   }
 
@@ -211,7 +189,8 @@ void RGWOp_BILog_SetBounds::execute() {
   bufferlist bl;
   list<RGWReplicaItemMarker> markers;
 
-  if ((http_ret = get_input(s, markers)) < 0) {
+  if ((http_ret = rgw_rest_get_json_input(store->ctx(), s, markers, REPLICA_INPUT_MAX_LEN, NULL)) < 0) {
+    dout(5) << "Error - retrieving input data - " << http_ret << dendl;
     return;
   }
 
index 759997fa69bef3a998f36c9585f0362fda9f4ad4..0613c49f314585b1702b33d8294cd80df4ad285a 100644 (file)
@@ -615,6 +615,8 @@ struct UserQuotas {
   RGWQuotaInfo bucket_quota;
   RGWQuotaInfo user_quota;
 
+  UserQuotas() {}
+
   UserQuotas(RGWUserInfo& info) {
     bucket_quota = info.bucket_quota;
     user_quota = info.user_quota;
@@ -654,6 +656,11 @@ void RGWOp_Quota_Info::execute()
   RESTArgs::get_string(s, "uid", uid, &uid);
   RESTArgs::get_string(s, "quota-type", quota_type, &quota_type);
 
+  if (uid.empty()) {
+    http_ret = -EINVAL;
+    return;
+  }
+
   bool show_all = quota_type.empty();
   bool show_bucket = show_all || (quota_type == "bucket");
   bool show_user = show_all || (quota_type == "user");
@@ -663,7 +670,6 @@ void RGWOp_Quota_Info::execute()
     return;
   }
 
-
   op_state.set_user_id(uid);
 
   RGWUser user;
@@ -689,6 +695,179 @@ void RGWOp_Quota_Info::execute()
   flusher.flush();
 }
 
+class RGWOp_Quota_Set : public RGWRESTOp {
+
+public:
+  RGWOp_Quota_Set() {}
+
+  int check_caps(RGWUserCaps& caps) {
+    return caps.check_cap("users", RGW_CAP_WRITE);
+  }
+
+  void execute();
+
+  virtual const string name() { return "set_quota_info"; }
+};
+
+/**
+ * set quota
+ *
+ * two different ways to set the quota info: as json struct in the message body or via http params.
+ *
+ * as json:
+ *
+ * PUT /admin/user?uid=<uid>[&quota-type=<type>]
+ *
+ * whereas quota-type is optional and is either user, or bucket
+ *
+ * if quota-type is not specified then we expect to get a structure that contains both quotas,
+ * otherwise we'll only get the relevant configuration.
+ *
+ * E.g., if quota type not specified:
+ * {
+ *    "user_quota" : {
+ *      "max_size_kb" : 4096,
+ *      "max_objects" : -1,
+ *      "enabled" : false
+ *    },
+ *    "bucket_quota" : {
+ *      "max_size_kb" : 1024,
+ *      "max_objects" : -1,
+ *      "enabled" : true
+ *    }
+ * }
+ *
+ *
+ * or if quota type is specified:
+ * {
+ *   "max_size_kb" : 4096,
+ *   "max_objects" : -1,
+ *   "enabled" : false
+ * }
+ *
+ * Another option is not to pass any body and set the following http params:
+ *
+ *
+ * max-size-kb=<size>
+ * max-objects=<max objects>
+ * enabled[={true,false}]
+ *
+ * all params are optionals and default to the current settings. With this type of configuration the
+ * quota-type param is mandatory.
+ *
+ */
+
+void RGWOp_Quota_Set::execute()
+{
+  RGWUserAdminOpState op_state;
+
+  std::string uid;
+  std::string quota_type;
+
+  RESTArgs::get_string(s, "uid", uid, &uid);
+  RESTArgs::get_string(s, "quota-type", quota_type, &quota_type);
+
+  if (uid.empty()) {
+    http_ret = -EINVAL;
+    return;
+  }
+
+  bool set_all = quota_type.empty();
+  bool set_bucket = set_all || (quota_type == "bucket");
+  bool set_user = set_all || (quota_type == "user");
+
+  if (!(set_all || set_bucket || set_user)) {
+    ldout(store->ctx(), 20) << "invalid quota type" << dendl;
+    http_ret = -EINVAL;
+    return;
+  }
+
+  bool use_http_params;
+
+  if (s->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);
+  }
+
+  if (use_http_params && set_all) {
+    ldout(store->ctx(), 20) << "quota type was not specified, can't set all quotas via http headers" << dendl;
+    http_ret = -EINVAL;
+    return;
+  }
+
+  op_state.set_user_id(uid);
+
+  RGWUser user;
+  http_ret = user.init(store, op_state);
+  if (http_ret < 0) {
+    ldout(store->ctx(), 20) << "failed initializing user info: " << http_ret << dendl;
+    return;
+  }
+
+#define QUOTA_INPUT_MAX_LEN 1024
+  if (set_all) {
+    UserQuotas quotas;
+
+    if ((http_ret = rgw_rest_get_json_input(store->ctx(), s, quotas, QUOTA_INPUT_MAX_LEN, NULL)) < 0) {
+      ldout(store->ctx(), 20) << "failed to retrieve input" << dendl;
+      return;
+    }
+
+    op_state.set_user_quota(quotas.user_quota);
+    op_state.set_bucket_quota(quotas.bucket_quota);
+  } else {
+    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) {
+        ldout(store->ctx(), 20) << "failed to retrieve input" << dendl;
+        if (!empty)
+          return;
+
+        /* was probably chunked input, but no content provided, configure via http params */
+        use_http_params = true;
+      }
+    }
+
+    if (use_http_params) {
+      RGWUserInfo info;
+      string err_msg;
+      http_ret = user.info(info, &err_msg);
+      if (http_ret < 0) {
+        ldout(store->ctx(), 20) << "failed to get user info: " << http_ret << dendl;
+        return;
+      }
+      RGWQuotaInfo *old_quota;
+      if (set_user) {
+        old_quota = &info.user_quota;
+      } else {
+        old_quota = &info.bucket_quota;
+      }
+
+      RESTArgs::get_int64(s, "max-objects", old_quota->max_objects, &quota.max_objects);
+      RESTArgs::get_int64(s, "max-size-kb", old_quota->max_size_kb, &quota.max_size_kb);
+      RESTArgs::get_bool(s, "enabled", old_quota->enabled, &quota.enabled);
+    }
+
+    if (set_user) {
+      op_state.set_user_quota(quota);
+    } else {
+      op_state.set_bucket_quota(quota);
+    }
+  }
+
+  string err;
+  http_ret = user.modify(op_state, &err);
+  if (http_ret < 0) {
+    ldout(store->ctx(), 20) << "failed updating user info: " << http_ret << ": " << err << dendl;
+    return;
+  }
+}
+
 RGWOp *RGWHandler_User::op_get()
 {
   if (s->info.args.sub_resource_exists("quota"))