/* Copyright (c) 2013 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. */ #include #include "glusterd-volgen.h" #include "glusterd-utils.h" static int validate_cache_max_min_size(glusterd_volinfo_t *volinfo, dict_t *dict, char *key, char *value, char **op_errstr) { char *current_max_value = NULL; char *current_min_value = NULL; char errstr[2048] = ""; glusterd_conf_t *priv = NULL; int ret = 0; uint64_t max_value = 0; uint64_t min_value = 0; xlator_t *this = NULL; this = THIS; GF_ASSERT(this); priv = this->private; GF_ASSERT(priv); if ((!strcmp(key, "performance.cache-min-file-size")) || (!strcmp(key, "cache-min-file-size"))) { glusterd_volinfo_get(volinfo, "performance.cache-max-file-size", ¤t_max_value); if (current_max_value) { gf_string2bytesize_uint64(current_max_value, &max_value); gf_string2bytesize_uint64(value, &min_value); current_min_value = value; } } else if ((!strcmp(key, "performance.cache-max-file-size")) || (!strcmp(key, "cache-max-file-size"))) { glusterd_volinfo_get(volinfo, "performance.cache-min-file-size", ¤t_min_value); if (current_min_value) { gf_string2bytesize_uint64(current_min_value, &min_value); gf_string2bytesize_uint64(value, &max_value); current_max_value = value; } } if (min_value > max_value) { snprintf(errstr, sizeof(errstr), "cache-min-file-size (%s) is greater than " "cache-max-file-size (%s)", current_min_value, current_max_value); gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_CACHE_MINMAX_SIZE_INVALID, "%s", errstr); *op_errstr = gf_strdup(errstr); ret = -1; goto out; } out: gf_msg_debug(this->name, 0, "Returning %d", ret); return ret; } static int validate_defrag_throttle_option(glusterd_volinfo_t *volinfo, dict_t *dict, char *key, char *value, char **op_errstr) { char errstr[2048] = ""; int ret = 0; xlator_t *this = NULL; int thread_count = 0; long int cores_available = 0; this = THIS; GF_ASSERT(this); cores_available = sysconf(_SC_NPROCESSORS_ONLN); /* Throttle option should be one of lazy|normal|aggressive or a number * configured by user max up to the number of cores in the machine */ if (!strcasecmp(value, "lazy") || !strcasecmp(value, "normal") || !strcasecmp(value, "aggressive")) { ret = 0; } else if ((gf_string2int(value, &thread_count) == 0)) { if ((thread_count > 0) && (thread_count <= cores_available)) { ret = 0; } else { ret = -1; snprintf(errstr, sizeof(errstr), "%s should be within" " range of 0 and maximum number of cores " "available (cores available - %ld)", key, cores_available); gf_msg(this->name, GF_LOG_ERROR, EINVAL, GD_MSG_INVALID_ENTRY, "%s", errstr); *op_errstr = gf_strdup(errstr); } } else { ret = -1; snprintf(errstr, sizeof(errstr), "%s should be " "{lazy|normal|aggressive} or a number up to number of" " cores available (cores available - %ld)", key, cores_available); gf_msg(this->name, GF_LOG_ERROR, EINVAL, GD_MSG_INVALID_ENTRY, "%s", errstr); *op_errstr = gf_strdup(errstr); } gf_msg_debug(this->name, 0, "Returning %d", ret); return ret; } static int validate_quota(glusterd_volinfo_t *volinfo, dict_t *dict, char *key, char *value, char **op_errstr) { char errstr[2048] = ""; glusterd_conf_t *priv = NULL; int ret = 0; xlator_t *this = NULL; this = THIS; GF_ASSERT(this); priv = this->private; GF_ASSERT(priv); ret = glusterd_volinfo_get_boolean(volinfo, VKEY_FEATURES_QUOTA); if (ret == -1) { gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_QUOTA_GET_STAT_FAIL, "failed to get the quota status"); goto out; } if (ret == _gf_false) { snprintf(errstr, sizeof(errstr), "Cannot set %s. Enable quota first.", key); gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_QUOTA_DISABLED, "%s", errstr); *op_errstr = gf_strdup(errstr); ret = -1; goto out; } ret = 0; out: gf_msg_debug(this->name, 0, "Returning %d", ret); return ret; } static int validate_uss(glusterd_volinfo_t *volinfo, dict_t *dict, char *key, char *value, char **op_errstr) { char errstr[2048] = ""; int ret = 0; xlator_t *this = NULL; gf_boolean_t b = _gf_false; this = THIS; GF_ASSERT(this); ret = gf_string2boolean(value, &b); if (ret) { snprintf(errstr, sizeof(errstr), "%s is not a valid boolean " "value. %s expects a valid boolean value.", value, key); gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_INVALID_ENTRY, "%s", errstr); *op_errstr = gf_strdup(errstr); goto out; } out: gf_msg_debug(this->name, 0, "Returning %d", ret); return ret; } static int validate_uss_dir(glusterd_volinfo_t *volinfo, dict_t *dict, char *key, char *value, char **op_errstr) { char errstr[2048] = ""; int ret = -1; int i = 0; xlator_t *this = NULL; this = THIS; GF_ASSERT(this); i = strlen(value); if (i > NAME_MAX) { snprintf(errstr, sizeof(errstr), "value of %s exceedes %d " "characters", key, NAME_MAX); goto out; } else if (i < 2) { snprintf(errstr, sizeof(errstr), "value of %s too short, " "expects at least two characters", key); goto out; } if (value[0] != '.') { snprintf(errstr, sizeof(errstr), "%s expects value starting " "with '.' ", key); goto out; } for (i = 1; value[i]; i++) { if (isalnum(value[i]) || value[i] == '_' || value[i] == '-') continue; snprintf(errstr, sizeof(errstr), "%s expects value to" " contain only '0-9a-z-_'", key); goto out; } ret = 0; out: if (ret) { gf_msg(this->name, GF_LOG_ERROR, EINVAL, GD_MSG_INVALID_ENTRY, "%s", errstr); *op_errstr = gf_strdup(errstr); } gf_msg_debug(this->name, 0, "Returning %d", ret); return ret; } static int validate_server_options(glusterd_volinfo_t *volinfo, dict_t *dict, char *key, char *value, char **op_errstr) { char errstr[2048] = ""; xlator_t *this = NULL; int ret = -1; int origin_val = 0; this = THIS; GF_ASSERT(this); if (volinfo->status == GLUSTERD_STATUS_STARTED) { gf_msg(this->name, GF_LOG_INFO, 0, GD_MSG_VOL_SET_VALIDATION_INFO, "Please note that " "volume %s is started. This option will only get " "effected after a brick restart.", volinfo->volname); } ret = gf_string2int(value, &origin_val); if (ret) { snprintf(errstr, sizeof(errstr), "%s is not a compatible " "value. %s expects an integer value.", value, key); ret = -1; goto out; } if (origin_val < 0) { snprintf(errstr, sizeof(errstr), "%s is not a " "compatible value. %s expects a positive" "integer value.", value, key); ret = -1; goto out; } ret = 0; out: if (ret) { gf_msg(this->name, GF_LOG_ERROR, EINVAL, GD_MSG_INCOMPATIBLE_VALUE, "%s", errstr); *op_errstr = gf_strdup(errstr); } return ret; } static int validate_disperse(glusterd_volinfo_t *volinfo, dict_t *dict, char *key, char *value, char **op_errstr) { char errstr[2048] = ""; int ret = -1; xlator_t *this = NULL; this = THIS; GF_VALIDATE_OR_GOTO("glusterd", this, out); if (volinfo->type != GF_CLUSTER_TYPE_DISPERSE) { snprintf(errstr, sizeof(errstr), "Cannot set %s for a non-disperse volume.", key); gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_VOL_NOT_DISPERSE, "%s", errstr); *op_errstr = gf_strdup(errstr); ret = -1; goto out; } ret = 0; out: gf_msg_debug(ret == 0 ? THIS->name : "glusterd", 0, "Returning %d", ret); return ret; } static int validate_replica(glusterd_volinfo_t *volinfo, dict_t *dict, char *key, char *value, char **op_errstr) { char errstr[2048] = ""; int ret = 0; xlator_t *this = NULL; this = THIS; GF_ASSERT(this); if (volinfo->replica_count == 1) { snprintf(errstr, sizeof(errstr), "Cannot set %s for a non-replicate volume.", key); gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_VOL_NOT_REPLICA, "%s", errstr); *op_errstr = gf_strdup(errstr); ret = -1; goto out; } out: gf_msg_debug(this->name, 0, "Returning %d", ret); return ret; } static int validate_quorum_count(glusterd_volinfo_t *volinfo, dict_t *dict, char *key, char *value, char **op_errstr) { int ret = 0; xlator_t *this = NULL; int q_count = 0; this = THIS; GF_ASSERT(this); ret = gf_string2int(value, &q_count); if (ret) { gf_asprintf(op_errstr, "%s is not an integer. %s expects a " "valid integer value.", value, key); goto out; } if (q_count < 1 || q_count > volinfo->replica_count) { gf_asprintf(op_errstr, "%d in %s %d is out of range [1 - %d]", q_count, key, q_count, volinfo->replica_count); ret = -1; } out: if (ret) { gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_INVALID_ENTRY, "%s", *op_errstr); } gf_msg_debug(this->name, 0, "Returning %d", ret); return ret; } static int validate_subvols_per_directory(glusterd_volinfo_t *volinfo, dict_t *dict, char *key, char *value, char **op_errstr) { char errstr[2048] = ""; glusterd_conf_t *priv = NULL; int ret = 0; int subvols = 0; xlator_t *this = NULL; this = THIS; GF_ASSERT(this); priv = this->private; GF_ASSERT(priv); subvols = atoi(value); /* Checking if the subvols-per-directory exceed the total number of subvolumes. */ if (subvols > volinfo->subvol_count) { snprintf(errstr, sizeof(errstr), "subvols-per-directory(%d) is greater " "than the number of subvolumes(%d).", subvols, volinfo->subvol_count); gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_SUBVOLUMES_EXCEED, "%s.", errstr); *op_errstr = gf_strdup(errstr); ret = -1; goto out; } out: gf_msg_debug(this->name, 0, "Returning %d", ret); return ret; } static int validate_replica_heal_enable_disable(glusterd_volinfo_t *volinfo, dict_t *dict, char *key, char *value, char **op_errstr) { int ret = 0; if (!glusterd_is_volume_replicate(volinfo)) { gf_asprintf(op_errstr, "Volume %s is not of replicate type", volinfo->volname); ret = -1; } return ret; } static int validate_mandatory_locking(glusterd_volinfo_t *volinfo, dict_t *dict, char *key, char *value, char **op_errstr) { char errstr[2048] = ""; int ret = 0; xlator_t *this = NULL; this = THIS; GF_ASSERT(this); if (strcmp(value, "off") != 0 && strcmp(value, "file") != 0 && strcmp(value, "forced") != 0 && strcmp(value, "optimal") != 0) { snprintf(errstr, sizeof(errstr), "Invalid option value '%s':" " Available options are 'off', 'file', " "'forced' or 'optimal'", value); gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_INVALID_ENTRY, "%s", errstr); *op_errstr = gf_strdup(errstr); ret = -1; goto out; } out: gf_msg_debug(this->name, 0, "Returning %d", ret); return ret; } static int validate_disperse_heal_enable_disable(glusterd_volinfo_t *volinfo, dict_t *dict, char *key, char *value, char **op_errstr) { int ret = 0; if (volinfo->type != GF_CLUSTER_TYPE_DISPERSE) { gf_asprintf(op_errstr, "Volume %s is not of disperse type", volinfo->volname); ret = -1; } return ret; } static int validate_lock_migration_option(glusterd_volinfo_t *volinfo, dict_t *dict, char *key, char *value, char **op_errstr) { char errstr[2048] = ""; int ret = 0; xlator_t *this = NULL; gf_boolean_t b = _gf_false; this = THIS; GF_ASSERT(this); if (volinfo->replica_count > 1 || volinfo->disperse_count) { snprintf(errstr, sizeof(errstr), "Lock migration is " "a experimental feature. Currently works with" " pure distribute volume only"); ret = -1; gf_msg(this->name, GF_LOG_ERROR, EINVAL, GD_MSG_INVALID_ENTRY, "%s", errstr); *op_errstr = gf_strdup(errstr); goto out; } ret = gf_string2boolean(value, &b); if (ret) { snprintf(errstr, sizeof(errstr), "Invalid value" " for volume set command. Use on/off only."); ret = -1; gf_msg(this->name, GF_LOG_ERROR, EINVAL, GD_MSG_INVALID_ENTRY, "%s", errstr); *op_errstr = gf_strdup(errstr); goto out; } gf_msg_debug(this->name, 0, "Returning %d", ret); out: return ret; } static int validate_mux_limit(glusterd_volinfo_t *volinfo, dict_t *dict, char *key, char *value, char **op_errstr) { xlator_t *this = NULL; uint val = 0; int ret = -1; this = THIS; GF_VALIDATE_OR_GOTO("glusterd", this, out); if (!is_brick_mx_enabled()) { gf_asprintf(op_errstr, "Brick-multiplexing is not enabled. " "Please enable brick multiplexing before trying " "to set this option."); gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_WRONG_OPTS_SETTING, "%s", *op_errstr); goto out; } ret = gf_string2uint(value, &val); if (ret) { gf_asprintf(op_errstr, "%s is not a valid count. " "%s expects an unsigned integer.", value, key); gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_INVALID_ENTRY, "%s", *op_errstr); } if (val == 1) { gf_asprintf(op_errstr, "Brick-multiplexing is enabled. " "Please set this option to a value other than 1 " "to make use of the brick-multiplexing feature."); ret = -1; goto out; } out: gf_msg_debug("glusterd", 0, "Returning %d", ret); return ret; } static int validate_volume_per_thread_limit(glusterd_volinfo_t *volinfo, dict_t *dict, char *key, char *value, char **op_errstr) { xlator_t *this = NULL; uint val = 0; int ret = -1; this = THIS; GF_VALIDATE_OR_GOTO("glusterd", this, out); if (!is_brick_mx_enabled()) { gf_asprintf(op_errstr, "Brick-multiplexing is not enabled. " "Please enable brick multiplexing before trying " "to set this option."); gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_WRONG_OPTS_SETTING, "%s", *op_errstr); goto out; } ret = gf_string2uint(value, &val); if (ret) { gf_asprintf(op_errstr, "%s is not a valid count. " "%s expects an unsigned integer.", value, key); gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_INVALID_ENTRY, "%s", *op_errstr); } if ((val < 5) || (val > 200)) { gf_asprintf( op_errstr, "Please set this option to a value between 5 and 200 to" "optimize processing large numbers of volumes in parallel."); ret = -1; goto out; } out: gf_msg_debug("glusterd", 0, "Returning %d", ret); return ret; } static int validate_boolean(glusterd_volinfo_t *volinfo, dict_t *dict, char *key, char *value, char **op_errstr) { xlator_t *this = NULL; gf_boolean_t b = _gf_false; int ret = -1; this = THIS; GF_VALIDATE_OR_GOTO("glusterd", this, out); ret = gf_string2boolean(value, &b); if (ret) { gf_asprintf(op_errstr, "%s is not a valid boolean value. %s " "expects a valid boolean value.", value, key); gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_INVALID_ENTRY, "%s", *op_errstr); } out: gf_msg_debug("glusterd", 0, "Returning %d", ret); return ret; } static int validate_disperse_quorum_count(glusterd_volinfo_t *volinfo, dict_t *dict, char *key, char *value, char **op_errstr) { int ret = -1; int quorum_count = 0; int data_count = 0; ret = gf_string2int(value, &quorum_count); if (ret) { gf_asprintf(op_errstr, "%s is not an integer. %s expects a " "valid integer value.", value, key); goto out; } if (volinfo->type != GF_CLUSTER_TYPE_DISPERSE) { gf_asprintf(op_errstr, "Cannot set %s for a non-disperse volume.", key); ret = -1; goto out; } data_count = volinfo->disperse_count - volinfo->redundancy_count; if (quorum_count < data_count || quorum_count > volinfo->disperse_count) { gf_asprintf(op_errstr, "%d for %s is out of range [%d - %d]", quorum_count, key, data_count, volinfo->disperse_count); ret = -1; goto out; } ret = 0; out: return ret; } static int validate_parallel_readdir(glusterd_volinfo_t *volinfo, dict_t *dict, char *key, char *value, char **op_errstr) { int ret = -1; ret = validate_boolean(volinfo, dict, key, value, op_errstr); if (ret) goto out; ret = glusterd_is_defrag_on(volinfo); if (ret) { gf_asprintf(op_errstr, "%s option should be set " "after rebalance is complete", key); gf_msg("glusterd", GF_LOG_ERROR, 0, GD_MSG_INVALID_ENTRY, "%s", *op_errstr); } out: gf_msg_debug("glusterd", 0, "Returning %d", ret); return ret; } static int validate_rda_cache_limit(glusterd_volinfo_t *volinfo, dict_t *dict, char *key, char *value, char **op_errstr) { int ret = 0; uint64_t rda_cache_size = 0; ret = gf_string2bytesize_uint64(value, &rda_cache_size); if (ret < 0) goto out; if (rda_cache_size <= (1 * GF_UNIT_GB)) goto out; /* With release 3.11 the max value of rda_cache_limit is changed from * 1GB to INFINITY. If there are clients older than 3.11 and the value * of rda-cache-limit is set to > 1GB, the older clients will stop * working. Hence if a user is setting rda-cache-limit to > 1GB * ensure that all the clients are 3.11 or greater. */ ret = glusterd_check_client_op_version_support( volinfo->volname, GD_OP_VERSION_3_11_0, op_errstr); out: gf_msg_debug("glusterd", 0, "Returning %d", ret); return ret; } static int validate_worm_period(glusterd_volinfo_t *volinfo, dict_t *dict, char *key, char *value, char **op_errstr) { xlator_t *this = NULL; uint64_t period = -1; int ret = -1; this = THIS; GF_VALIDATE_OR_GOTO("glusterd", this, out); ret = gf_string2uint64(value, &period); if (ret) { gf_asprintf(op_errstr, "%s is not a valid uint64_t value." " %s expects a valid uint64_t value.", value, key); gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_INVALID_ENTRY, "%s", *op_errstr); } out: gf_msg_debug("glusterd", 0, "Returning %d", ret); return ret; } static int validate_reten_mode(glusterd_volinfo_t *volinfo, dict_t *dict, char *key, char *value, char **op_errstr) { xlator_t *this = NULL; int ret = -1; this = THIS; GF_VALIDATE_OR_GOTO("glusterd", this, out); if ((strcmp(value, "relax") && strcmp(value, "enterprise"))) { gf_asprintf(op_errstr, "The value of retention mode should be " "either relax or enterprise. But the value" " of %s is %s", key, value); gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_INVALID_ENTRY, "%s", *op_errstr); ret = -1; goto out; } ret = 0; out: gf_msg_debug("glusterd", 0, "Returning %d", ret); return ret; } static int is_directory(const char *path) { struct stat statbuf; if (sys_stat(path, &statbuf) != 0) return 0; return S_ISDIR(statbuf.st_mode); } static int validate_statedump_path(glusterd_volinfo_t *volinfo, dict_t *dict, char *key, char *value, char **op_errstr) { xlator_t *this = NULL; this = THIS; GF_ASSERT(this); int ret = 0; if (!is_directory(value)) { gf_asprintf(op_errstr, "Failed: %s is not a directory", value); ret = -1; gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_INVALID_ENTRY, "%s", *op_errstr); } return ret; } /* dispatch table for VOLUME SET * ----------------------------- * * Format of entries: * * First field is the , for the purpose of looking it up * in volume dictionary. Each is of the format ".". * * Second field is . * * Third field is