/* Copyright (c) 2011-2012 Red Hat, Inc. This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser General Public License, version 3 or any later version (LGPLv3 or later), or the GNU General Public License, version 2 (GPLv2), in all cases as published by the Free Software Foundation. */ #ifndef _CONFIG_H #define _CONFIG_H #include "config.h" #endif #include "common-utils.h" #include "cli1-xdr.h" #include "xdr-generic.h" #include "glusterd.h" #include "glusterd-op-sm.h" #include "glusterd-store.h" #include "glusterd-utils.h" #include "glusterd-volgen.h" #include "run.h" #include const char *gd_quota_op_list[GF_QUOTA_OPTION_TYPE_DEFAULT_SOFT_LIMIT+1] = { [GF_QUOTA_OPTION_TYPE_NONE] = "none", [GF_QUOTA_OPTION_TYPE_ENABLE] = "enable", [GF_QUOTA_OPTION_TYPE_DISABLE] = "disable", [GF_QUOTA_OPTION_TYPE_LIMIT_USAGE] = "limit-usage", [GF_QUOTA_OPTION_TYPE_REMOVE] = "remove", [GF_QUOTA_OPTION_TYPE_LIST] = "list", [GF_QUOTA_OPTION_TYPE_VERSION] = "version", [GF_QUOTA_OPTION_TYPE_SOFT_LIMIT] = "soft-limit", [GF_QUOTA_OPTION_TYPE_ALERT_TIME] = "alert-time", [GF_QUOTA_OPTION_TYPE_SOFT_TIMEOUT] = "soft-timeout", [GF_QUOTA_OPTION_TYPE_HARD_TIMEOUT] = "hard-timeout", [GF_QUOTA_OPTION_TYPE_DEFAULT_SOFT_LIMIT] = "default-soft-limit", }; int __glusterd_handle_quota (rpcsvc_request_t *req) { int32_t ret = -1; gf_cli_req cli_req = {{0,}}; dict_t *dict = NULL; glusterd_op_t cli_op = GD_OP_QUOTA; char *volname = NULL; int32_t type = 0; char msg[2048] = {0,}; xlator_t *this = NULL; glusterd_conf_t *conf = NULL; GF_ASSERT (req); this = THIS; GF_ASSERT (this); conf = this->private; GF_ASSERT (conf); ret = xdr_to_generic (req->msg[0], &cli_req, (xdrproc_t)xdr_gf_cli_req); if (ret < 0) { //failed to decode msg; req->rpc_err = GARBAGE_ARGS; goto out; } if (cli_req.dict.dict_len) { /* Unserialize the dictionary */ dict = dict_new (); ret = dict_unserialize (cli_req.dict.dict_val, cli_req.dict.dict_len, &dict); if (ret < 0) { gf_log (this->name, GF_LOG_ERROR, "failed to " "unserialize req-buffer to dictionary"); snprintf (msg, sizeof (msg), "Unable to decode the " "command"); goto out; } else { dict->extra_stdfree = cli_req.dict.dict_val; } } ret = dict_get_str (dict, "volname", &volname); if (ret) { snprintf (msg, sizeof (msg), "Unable to get volume name"); gf_log (this->name, GF_LOG_ERROR, "Unable to get volume name, " "while handling quota command"); goto out; } ret = dict_get_int32 (dict, "type", &type); if (ret) { snprintf (msg, sizeof (msg), "Unable to get type of command"); gf_log (this->name, GF_LOG_ERROR, "Unable to get type of cmd, " "while handling quota command"); goto out; } if ((conf->op_version == GD_OP_VERSION_MIN) && (type > GF_QUOTA_OPTION_TYPE_VERSION)) { snprintf (msg, sizeof (msg), "Cannot execute command. The " "cluster is operating at version 1. Executing command " "%s is disallowed in this state", gd_quota_op_list[type]); ret = -1; goto out; } ret = glusterd_op_begin_synctask (req, GD_OP_QUOTA, dict); out: if (ret) { if (msg[0] == '\0') snprintf (msg, sizeof (msg), "Operation failed"); ret = glusterd_op_send_cli_response (cli_op, ret, 0, req, dict, msg); } return ret; } int glusterd_handle_quota (rpcsvc_request_t *req) { return glusterd_big_locked_handler (req, __glusterd_handle_quota); } int32_t glusterd_check_if_quota_trans_enabled (glusterd_volinfo_t *volinfo) { int32_t ret = 0; int flag = _gf_false; flag = glusterd_volinfo_get_boolean (volinfo, VKEY_FEATURES_QUOTA); if (flag == -1) { gf_log ("", GF_LOG_ERROR, "failed to get the quota status"); ret = -1; goto out; } if (flag == _gf_false) { ret = -1; goto out; } ret = 0; out: return ret; } /* At the end of the function, the variable @found will be set * to true if the path to be removed was present in the limit-list, * else will be false. * * In addition, the function does the following things: * * a. places the path to be removed, if found, in @removed_path, * b. places the new limit list formed after removing @path's entry, in * @new_list. If @path is not found, the input limit string @quota_limits is * dup'd as is and placed in @new_list. */ int32_t _glusterd_quota_remove_limits (char *quota_limits, char *path, gf_boolean_t *found, char **new_list, char **removed_path) { int ret = 0; int i = 0; int size = 0; int len = 0; int pathlen = 0; int skiplen = 0; int flag = 0; char *limits = NULL; char *qlimits = NULL; char *rp = NULL; if (found != NULL) *found = _gf_false; if (quota_limits == NULL) return -1; qlimits = quota_limits; pathlen = strlen (path); len = strlen (qlimits); limits = GF_CALLOC (len + 1, sizeof (char), gf_gld_mt_char); if (!limits) return -1; while (i < len) { if (!memcmp ((void *) &qlimits [i], (void *)path, pathlen)) if (qlimits [i + pathlen] == ':') { flag = 1; if (found != NULL) *found = _gf_true; } while (qlimits [i + size] != ',' && qlimits [i + size] != '\0') size++; if (!flag) { memcpy ((void *) &limits [i], (void *) &qlimits [i], size + 1); } else { skiplen = size + 1; size = len - i - size; if (removed_path) { rp = GF_CALLOC (skiplen, sizeof (char), gf_gld_mt_char); if (!rp) { ret = -1; goto out; } strncpy (rp, &qlimits[i], skiplen - 1); *removed_path = rp; } memcpy ((void *) &limits [i], (void *) &qlimits [i + skiplen], size); break; } i += size + 1; size = 0; } len = strlen (limits); if (len == 0) goto out; if (limits[len - 1] == ',') { limits[len - 1] = '\0'; len --; } *new_list = GF_CALLOC (len + 1, sizeof (char), gf_gld_mt_char); if (!*new_list) { ret = -1; goto out; } memcpy ((void *) *new_list, (void *) limits, len + 1); ret = 0; out: GF_FREE (limits); if (ret != -1) ret = flag ? 0 : 1; return ret; } int32_t glusterd_quota_initiate_fs_crawl (glusterd_conf_t *priv, char *volname) { pid_t pid; int32_t ret = 0; int status = 0; char mountdir[] = "/tmp/mntXXXXXX"; runner_t runner = {0}; if (mkdtemp (mountdir) == NULL) { gf_log ("glusterd", GF_LOG_DEBUG, "failed to create a temporary mount directory"); ret = -1; goto out; } runinit (&runner); runner_add_args (&runner, SBIN_DIR"/glusterfs", "-s", "localhost", "--volfile-id", volname, "-l", DEFAULT_LOG_FILE_DIRECTORY"/quota-crawl.log", mountdir, NULL); synclock_unlock (&priv->big_lock); ret = runner_run_reuse (&runner); synclock_lock (&priv->big_lock); if (ret == -1) { runner_log (&runner, "glusterd", GF_LOG_DEBUG, "command failed"); runner_end (&runner); goto out; } runner_end (&runner); if ((pid = fork ()) < 0) { gf_log ("glusterd", GF_LOG_WARNING, "fork from parent failed"); ret = -1; goto out; } else if (pid == 0) {//first child /* fork one more to not hold back main process on * blocking call below */ pid = fork (); if (pid) _exit (pid > 0 ? EXIT_SUCCESS : EXIT_FAILURE); ret = chdir (mountdir); if (ret == -1) { gf_log ("glusterd", GF_LOG_WARNING, "chdir %s failed, " "reason: %s", mountdir, strerror (errno)); exit (EXIT_FAILURE); } runinit (&runner); runner_add_args (&runner, "/usr/bin/find", "find", ".", NULL); if (runner_start (&runner) == -1) _exit (EXIT_FAILURE); #ifndef GF_LINUX_HOST_OS runner_end (&runner); /* blocks in waitpid */ runcmd ("umount", mountdir, NULL); #else runcmd ("umount", "-l", mountdir, NULL); #endif rmdir (mountdir); _exit (EXIT_SUCCESS); } ret = (waitpid (pid, &status, 0) == pid && WIFEXITED (status) && WEXITSTATUS (status) == EXIT_SUCCESS) ? 0 : -1; out: return ret; } char * glusterd_quota_get_limit_value (char *quota_limits, char *path) { int32_t i, j, k, l, len; int32_t pat_len, diff; char *ret_str = NULL; len = strlen (quota_limits); pat_len = strlen (path); i = 0; j = 0; while (i < len) { j = i; k = 0; while (path [k] == quota_limits [j]) { j++; k++; } l = j; while (quota_limits [j] != ',' && quota_limits [j] != '\0') j++; if (quota_limits [l] == ':' && pat_len == (l - i)) { diff = j - i; ret_str = GF_CALLOC (diff + 1, sizeof (char), gf_gld_mt_char); strncpy (ret_str, "a_limits [i], diff); break; } i = ++j; //skip ',' } return ret_str; } char* _glusterd_quota_get_limit_usages (glusterd_volinfo_t *volinfo, char *path, char **op_errstr) { int32_t ret = 0; char *quota_limits = NULL; char *ret_str = NULL; if (volinfo == NULL) return NULL; ret = glusterd_volinfo_get (volinfo, VKEY_FEATURES_LIMIT_USAGE, "a_limits); if (ret) return NULL; if (quota_limits == NULL) { ret_str = NULL; *op_errstr = gf_strdup ("Limit not set on any directory"); } else if (path == NULL) ret_str = gf_strdup (quota_limits); else ret_str = glusterd_quota_get_limit_value (quota_limits, path); return ret_str; } int32_t glusterd_quota_get_limit_usages (glusterd_conf_t *priv, glusterd_volinfo_t *volinfo, char *volname, dict_t *dict, char **op_errstr, dict_t *rsp_dict) { int32_t i = 0; int32_t ret = 0; int32_t count = 0; int entry_count = 0; char *path = NULL; char cmd_str [1024] = {0, }; char *ret_str = NULL; xlator_t *this = NULL; glusterd_conf_t *conf = NULL; char *default_limit = NULL; char *val = NULL; if (rsp_dict == NULL) return 0; this = THIS; GF_ASSERT (this); conf = this->private; GF_ASSERT (conf); ret = dict_get_int32 (dict, "count", &count); if (ret < 0) goto out; if (count == 0) { ret_str = _glusterd_quota_get_limit_usages (volinfo, NULL, op_errstr); ret = dict_set_dynstr (rsp_dict, "limit_list", ret_str); if (ret) goto out; } else { i = 0; while (count--) { snprintf (cmd_str, sizeof (cmd_str), "path%d", i++); ret = dict_get_str (dict, cmd_str, &path); if (ret < 0) goto out; ret = gf_canonicalize_path (path); if (ret) { goto out; } ret_str = _glusterd_quota_get_limit_usages (volinfo, path, op_errstr); /* Despite quota limits being absent on @path, we go * ahead and place it in @rsp_dict with * value = "Not set". This is because after commit op, * as part of aggregation of @rsp_dict with @op_ctx, * when we copy the rsp_dict into op_ctx, op_ctx would * still be containing the old key (same as @cmd_str) * with the old value. In order to overwrite the old * value, we replace it with "Not set", something that * the cli can easily interpret as a case of quota * limits not having been set on the given path. */ if (!ret_str) { ret = dict_set_str (rsp_dict, cmd_str, "Not set"); } else { ret = dict_set_dynstr (rsp_dict, cmd_str, ret_str); entry_count = entry_count + 1; } } } ret = dict_set_int32 (rsp_dict, "entry-count", entry_count); if (ret) goto out; ret = dict_set_uint32 (rsp_dict, "op-version", conf->op_version); ret = glusterd_volinfo_get (volinfo, "features.default-soft-limit", &default_limit); if (default_limit) val = gf_strdup (default_limit); else val = gf_strdup ("90%"); ret = dict_set_dynstr (rsp_dict, "default-soft-limit", val); out: return ret; } int32_t glusterd_quota_enable (glusterd_volinfo_t *volinfo, char **op_errstr, gf_boolean_t *crawl) { int32_t ret = -1; char *quota_status = NULL; xlator_t *this = NULL; this = THIS; GF_ASSERT (this); GF_VALIDATE_OR_GOTO (this->name, volinfo, out); GF_VALIDATE_OR_GOTO (this->name, crawl, out); GF_VALIDATE_OR_GOTO (this->name, op_errstr, out); if (glusterd_is_volume_started (volinfo) == 0) { *op_errstr = gf_strdup ("Volume is stopped, start volume " "to enable quota."); goto out; } ret = glusterd_check_if_quota_trans_enabled (volinfo); if (ret == 0) { *op_errstr = gf_strdup ("Quota is already enabled"); goto out; } quota_status = gf_strdup ("on"); if (!quota_status) { gf_log (this->name, GF_LOG_ERROR, "memory allocation failed"); ret = -1; goto out; } ret = dict_set_dynstr (volinfo->dict, VKEY_FEATURES_QUOTA, quota_status); if (ret) { gf_log (this->name, GF_LOG_ERROR, "dict set failed"); goto out; } *op_errstr = gf_strdup ("Enabling quota has been successful"); *crawl = _gf_true; ret = 0; out: if (ret && op_errstr && !*op_errstr) gf_asprintf (op_errstr, "Enabling quota on volume %s has been " "unsuccessful", volinfo->volname); return ret; } int32_t glusterd_quota_disable (glusterd_volinfo_t *volinfo, char **op_errstr) { int32_t ret = -1; int i = 0; char *quota_status = NULL; char *value = NULL; xlator_t *this = NULL; glusterd_conf_t *conf = NULL; char *quota_options[] = {VKEY_FEATURES_LIMIT_USAGE, "features.soft-timeout", "features.hard-timeout", "features.alert-time", "features.default-soft-limit", NULL}; this = THIS; GF_ASSERT (this); conf = this->private; GF_ASSERT (conf); GF_VALIDATE_OR_GOTO (this->name, volinfo, out); GF_VALIDATE_OR_GOTO (this->name, op_errstr, out); ret = glusterd_check_if_quota_trans_enabled (volinfo); if (ret == -1) { *op_errstr = gf_strdup ("Quota is already disabled"); goto out; } quota_status = gf_strdup ("off"); if (!quota_status) { gf_log (this->name, GF_LOG_ERROR, "memory allocation failed"); ret = -1; goto out; } ret = dict_set_dynstr (volinfo->dict, VKEY_FEATURES_QUOTA, quota_status); if (ret) { gf_log (this->name, GF_LOG_ERROR, "dict set failed"); goto out; } for (i = 0; quota_options [i]; i++) { ret = glusterd_volinfo_get (volinfo, quota_options[i], &value); if (ret) { gf_log (this->name, GF_LOG_INFO, "failed to get option" " %s", quota_options[i]); } else { dict_del (volinfo->dict, quota_options[i]); } if ((i == 0) && (conf->op_version == GD_OP_VERSION_MIN)) break; } *op_errstr = gf_strdup ("Disabling quota has been successful"); ret = 0; out: if (ret && op_errstr && !*op_errstr) gf_asprintf (op_errstr, "Disabling quota on volume %s has been " "unsuccessful", volinfo->volname); return ret; } static void gd_quota_get_formatted_limit_list (char **value, char *existing_list, char *path, char *hard_limit, char *soft_limit) { if (!existing_list) { if (!soft_limit) gf_asprintf (value, "%s:%s", path, hard_limit); else gf_asprintf (value, "%s:%s:%s", path, hard_limit, soft_limit); } else { if (!soft_limit) gf_asprintf (value, "%s,%s:%s", existing_list, path, hard_limit); else gf_asprintf (value, "%s,%s:%s:%s", existing_list, path, hard_limit, soft_limit); } } int32_t glusterd_quota_limit_usage (glusterd_volinfo_t *volinfo, dict_t *dict, char **op_errstr) { int32_t ret = -1; char *path = NULL; char *limit = NULL; char *value = NULL; char *sl = NULL; char msg[5120] = {0,}; char *quota_limits = NULL; char *new_list = NULL; xlator_t *this = NULL; glusterd_conf_t *priv = NULL; char *removed_path = NULL; this = THIS; GF_ASSERT (this); priv = this->private; GF_ASSERT (priv); GF_VALIDATE_OR_GOTO (this->name, dict, out); GF_VALIDATE_OR_GOTO (this->name, volinfo, out); GF_VALIDATE_OR_GOTO (this->name, op_errstr, out); ret = glusterd_check_if_quota_trans_enabled (volinfo); if (ret == -1) { *op_errstr = gf_strdup ("Quota is disabled, please enable " "quota"); goto out; } ret = glusterd_volinfo_get (volinfo, VKEY_FEATURES_LIMIT_USAGE, "a_limits); if (ret) { gf_log (this->name, GF_LOG_ERROR, "failed to get quota limits"); goto out; } ret = dict_get_str (dict, "path", &path); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Unable to fetch path"); goto out; } ret = gf_canonicalize_path (path); if (ret) goto out; ret = dict_get_str (dict, "limit", &limit); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Unable to fetch limit"); goto out; } if (quota_limits == NULL) { ; //do nothing and go past the else block } else { ret = _glusterd_quota_remove_limits (quota_limits, path, NULL, &new_list, &removed_path); if (ret == -1) { gf_log (this->name, GF_LOG_ERROR, "Unable to remove " "limit"); goto out; } if (removed_path && (priv->op_version > GD_OP_VERSION_MIN)) { ret = gf_get_soft_limit (removed_path, &sl); if (ret == -1) goto out; } } gd_quota_get_formatted_limit_list (&value, new_list, path, limit, sl); ret = dict_set_str (volinfo->dict, VKEY_FEATURES_LIMIT_USAGE, value); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Unable to set quota limits"); goto out; } snprintf (msg, sizeof (msg), "hard limit set on %s", path); *op_errstr = gf_strdup (msg); ret = 0; out: GF_FREE (sl); GF_FREE (removed_path); GF_FREE (new_list); if (ret && op_errstr && !*op_errstr) gf_asprintf (op_errstr, "Failed to set hard limit on path %s " "for volume %s", path, volinfo->volname); return ret; } int glusterd_quota_soft_limit (glusterd_volinfo_t *volinfo, dict_t *dict, char **op_errstr) { int ret = 0; char *path = NULL; char *limit = NULL; char *quota_limits = NULL; char *new_list = NULL; char *removed_path = NULL; char msg[1024] = {0,}; char *hl = NULL; char *value = NULL; xlator_t *this = NULL; this = THIS; GF_ASSERT (this); if (!volinfo || !dict || !op_errstr) { ret = -1; goto out; } ret = glusterd_check_if_quota_trans_enabled (volinfo); if (ret == -1) { *op_errstr = gf_strdup ("Quota is disabled, please enable " "quota"); goto out; } ret = glusterd_volinfo_get (volinfo, VKEY_FEATURES_LIMIT_USAGE, "a_limits); if (ret) { gf_log (this->name, GF_LOG_ERROR, "failed to get quota limits"); goto out; } ret = dict_get_str (dict, "path", &path); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Unable to fetch path"); goto out; } ret = gf_canonicalize_path (path); if (ret) goto out; ret = dict_get_str (dict, "limit", &limit); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Unable to fetch limit"); goto out; } if (quota_limits == NULL) { gf_asprintf (op_errstr, "Soft-limit cannot be set on path %s, " "without setting hard-limit on it first.", path); ret = -1; goto out; } else { ret = _glusterd_quota_remove_limits (quota_limits, path, NULL, &new_list, &removed_path); if (ret == -1) { gf_log (this->name, GF_LOG_ERROR, "Failed to remove " "limit"); goto out; } if (!removed_path) { gf_asprintf (op_errstr, "Soft-limit cannot be set on " "path %s without setting hard-limit on it " "first.", path); ret = -1; goto out; } else { ret = gf_get_hard_limit (removed_path, &hl); if (ret) goto out; } } gd_quota_get_formatted_limit_list (&value, new_list, path, hl, limit); ret = dict_set_str (volinfo->dict, VKEY_FEATURES_LIMIT_USAGE, value); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Unable to set quota limits"); goto out; } snprintf (msg, sizeof (msg), "soft limit set on %s", path); *op_errstr = gf_strdup (msg); ret = 0; out: GF_FREE (hl); GF_FREE (removed_path); GF_FREE (new_list); if (ret && op_errstr && !*op_errstr) gf_asprintf (op_errstr, "Failed to set soft limit for path %s " "on volume %s", path, volinfo->volname); return ret; } int32_t glusterd_quota_remove_limits (glusterd_volinfo_t *volinfo, dict_t *dict, char **op_errstr) { int32_t ret = -1; char str [PATH_MAX + 1024] = {0,}; char *quota_limits = NULL; char *new_list = NULL; char *value = NULL; char *path = NULL; gf_boolean_t flag = _gf_false; xlator_t *this = NULL; this = THIS; GF_ASSERT (this); GF_VALIDATE_OR_GOTO (this->name, dict, out); GF_VALIDATE_OR_GOTO (this->name, volinfo, out); GF_VALIDATE_OR_GOTO (this->name, op_errstr, out); ret = glusterd_check_if_quota_trans_enabled (volinfo); if (ret == -1) { *op_errstr = gf_strdup ("Quota is disabled, please enable " "quota"); goto out; } ret = glusterd_volinfo_get (volinfo, VKEY_FEATURES_LIMIT_USAGE, "a_limits); if (ret) { gf_log (this->name, GF_LOG_ERROR, "failed to get quota limits"); goto out; } ret = dict_get_str (dict, "path", &path); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Unable to fetch path"); goto out; } ret = gf_canonicalize_path (path); if (ret) goto out; ret = _glusterd_quota_remove_limits (quota_limits, path, &flag, &new_list, NULL); if (ret == -1) { if (flag == _gf_true) snprintf (str, sizeof (str), "Removing limit on %s has " "been unsuccessful", path); else snprintf (str, sizeof (str), "%s has no limit set", path); *op_errstr = gf_strdup (str); goto out; } else { if (flag == _gf_true) snprintf (str, sizeof (str), "Removed quota limit on " "%s", path); else snprintf (str, sizeof (str), "no limit set on %s", path); *op_errstr = gf_strdup (str); } if (new_list) { value = gf_strdup (new_list); ret = dict_set_str (volinfo->dict, VKEY_FEATURES_LIMIT_USAGE, value); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Unable to set quota " "limits"); goto out; } } else { dict_del (volinfo->dict, VKEY_FEATURES_LIMIT_USAGE); } ret = 0; out: GF_FREE (new_list); return ret; } int glusterd_set_quota_option (glusterd_volinfo_t *volinfo, dict_t *dict, char *key, char **op_errstr) { int ret = 0; char *value = NULL; xlator_t *this = NULL; char *option = NULL; this = THIS; GF_ASSERT (this); ret = glusterd_check_if_quota_trans_enabled (volinfo); if (ret == -1) { gf_asprintf (op_errstr, "Cannot set %s. Quota on volume %s is " "disabled", key, volinfo->volname); return -1; } ret = dict_get_str (dict, "value", &value); if(ret) { gf_log (this->name, GF_LOG_ERROR, "Option value absent."); return -1; } option = gf_strdup (value); ret = dict_set_dynstr (volinfo->dict, key, option); if(ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set option %s", key); return -1; } gf_asprintf (op_errstr, "%s on volume %s set", key, volinfo->volname); return 0; } int glusterd_op_quota (dict_t *dict, char **op_errstr, dict_t *rsp_dict) { glusterd_volinfo_t *volinfo = NULL; int32_t ret = -1; char *volname = NULL; int type = -1; gf_boolean_t start_crawl = _gf_false; glusterd_conf_t *priv = NULL; xlator_t *this = NULL; GF_ASSERT (dict); GF_ASSERT (op_errstr); this = THIS; GF_ASSERT (this); priv = this->private; GF_ASSERT (priv); ret = dict_get_str (dict, "volname", &volname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Unable to get volume name"); goto out; } ret = glusterd_volinfo_find (volname, &volinfo); if (ret) { gf_asprintf (op_errstr, FMTSTR_CHECK_VOL_EXISTS, volname); goto out; } ret = dict_get_int32 (dict, "type", &type); if ((priv->op_version == GD_OP_VERSION_MIN) && (type > GF_QUOTA_OPTION_TYPE_VERSION)) { gf_asprintf (op_errstr, "Volume quota failed. The cluster is " "operating at version %d. Option %s " "is disallowed in this state.", priv->op_version, gd_quota_op_list[type]); ret = -1; goto out; } if (type == GF_QUOTA_OPTION_TYPE_ENABLE) { ret = glusterd_quota_enable (volinfo, op_errstr, &start_crawl); if (ret < 0) goto out; goto create_vol; } if (type == GF_QUOTA_OPTION_TYPE_DISABLE) { ret = glusterd_quota_disable (volinfo, op_errstr); if (ret < 0) goto out; goto create_vol; } if (type == GF_QUOTA_OPTION_TYPE_LIMIT_USAGE) { ret = glusterd_quota_limit_usage (volinfo, dict, op_errstr); if (ret < 0) goto out; goto create_vol; } if (type == GF_QUOTA_OPTION_TYPE_REMOVE) { ret = glusterd_quota_remove_limits (volinfo, dict, op_errstr); if (ret < 0) goto out; goto create_vol; } if (type == GF_QUOTA_OPTION_TYPE_LIST) { ret = glusterd_check_if_quota_trans_enabled (volinfo); if (ret == -1) { *op_errstr = gf_strdup ("cannot list the limits, " "quota is disabled"); goto out; } ret = glusterd_quota_get_limit_usages (priv, volinfo, volname, dict, op_errstr, rsp_dict); goto out; } if (type == GF_QUOTA_OPTION_TYPE_SOFT_LIMIT) { ret = glusterd_quota_soft_limit (volinfo, dict, op_errstr); if (ret < 0) goto out; goto create_vol; } if (type == GF_QUOTA_OPTION_TYPE_SOFT_TIMEOUT) { ret = glusterd_set_quota_option (volinfo, dict, "features.soft-timeout", op_errstr); if (ret) goto out; goto create_vol; } if (type == GF_QUOTA_OPTION_TYPE_HARD_TIMEOUT) { ret = glusterd_set_quota_option (volinfo, dict, "features.hard-timeout", op_errstr); if (ret) goto out; goto create_vol; } if (type == GF_QUOTA_OPTION_TYPE_ALERT_TIME) { ret = glusterd_set_quota_option (volinfo, dict, "features.alert-time", op_errstr); if (ret) goto out; goto create_vol; } if (type == GF_QUOTA_OPTION_TYPE_DEFAULT_SOFT_LIMIT) { ret = glusterd_set_quota_option (volinfo, dict, "features.default-soft-limit", op_errstr); if (ret) goto out; goto create_vol; } create_vol: ret = glusterd_create_volfiles_and_notify_services (volinfo); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Unable to re-create " "volfiles"); ret = -1; goto out; } ret = glusterd_store_volinfo (volinfo, GLUSTERD_VOLINFO_VER_AC_INCREMENT); if (ret) goto out; if (GLUSTERD_STATUS_STARTED == volinfo->status) { if (priv->op_version == GD_OP_VERSION_MIN) ret = glusterd_check_generate_start_nfs (); } ret = 0; out: if (rsp_dict && start_crawl == _gf_true) glusterd_quota_initiate_fs_crawl (priv, volname); if (priv->op_version > GD_OP_VERSION_MIN && is_origin_glusterd ()) { if (type != GF_QUOTA_OPTION_TYPE_LIST) { if (glusterd_all_volumes_with_quota_stopped ()) ret = glusterd_quotad_stop (); else ret = glusterd_check_generate_start_quotad (); } } if (rsp_dict && *op_errstr) { ret = dict_set_dynstr (rsp_dict, "errstr", *op_errstr); if (ret) { GF_FREE (*op_errstr); gf_log (this->name, GF_LOG_DEBUG, "failed to set error message in ctx"); } *op_errstr = NULL; } return ret; } int glusterd_op_stage_quota (dict_t *dict, char **op_errstr) { int ret = 0; char *volname = NULL; gf_boolean_t exists = _gf_false; int type = 0; dict_t *ctx = NULL; xlator_t *this = NULL; glusterd_conf_t *priv = NULL; this = THIS; GF_ASSERT (this); priv = this->private; GF_ASSERT (priv); GF_ASSERT (dict); GF_ASSERT (op_errstr); ret = dict_get_str (dict, "volname", &volname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Unable to get volume name"); goto out; } exists = glusterd_check_volume_exists (volname); if (!exists) { gf_asprintf (op_errstr, FMTSTR_CHECK_VOL_EXISTS, volname); ret = -1; goto out; } ret = dict_get_int32 (dict, "type", &type); if (ret) { *op_errstr = gf_strdup ("Volume quota failed, internal error, " "unable to get type of operation"); goto out; } if ((priv->op_version == GD_OP_VERSION_MIN) && (type > GF_QUOTA_OPTION_TYPE_VERSION)) { gf_asprintf (op_errstr, "Volume quota failed. The cluster is " "operating at version %d. Option %s " "is disallowed in this state.", priv->op_version, gd_quota_op_list[type]); ret = -1; goto out; } ctx = glusterd_op_get_ctx(); if (ctx && (type == GF_QUOTA_OPTION_TYPE_ENABLE || type == GF_QUOTA_OPTION_TYPE_LIST)) { /* Fuse mount req. only for enable & list-usage options*/ if (!glusterd_is_fuse_available ()) { *op_errstr = gf_strdup ("Fuse unavailable"); ret = -1; goto out; } } out: if (ret && op_errstr && *op_errstr) gf_log (this->name, GF_LOG_ERROR, "%s", *op_errstr); gf_log (this->name, GF_LOG_DEBUG, "Returning %d", ret); return ret; }