/* 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 "glusterd-volgen.h" #include "glusterd-utils.h" static int get_tier_freq_threshold (glusterd_volinfo_t *volinfo, char *threshold_key) { int threshold = 0; char *str_thresold = NULL; int ret = -1; xlator_t *this = NULL; this = THIS; GF_ASSERT (this); glusterd_volinfo_get (volinfo, threshold_key, &str_thresold); if (str_thresold) { ret = gf_string2int (str_thresold, &threshold); if (ret == -1) { threshold = ret; gf_msg (this->name, GF_LOG_ERROR, EINVAL, GD_MSG_INCOMPATIBLE_VALUE, "Failed to convert " "string to integer"); } } return threshold; } /* * Validation function for record-counters * if write-freq-threshold and read-freq-threshold both have non-zero values * record-counters cannot be set to off * if record-counters is set to on * check if both the frequency thresholds are zero, then pop * a note, but volume set is not failed. * */ static int validate_tier_counters (glusterd_volinfo_t *volinfo, dict_t *dict, char *key, char *value, char **op_errstr) { char errstr[2048] = ""; int ret = -1; xlator_t *this = NULL; gf_boolean_t origin_val = -1; int current_wt = 0; int current_rt = 0; this = THIS; GF_ASSERT (this); if (volinfo->type != GF_CLUSTER_TYPE_TIER) { snprintf (errstr, sizeof (errstr), "Volume %s is not a tier " "volume. Option %s is only valid for tier volume.", volinfo->volname, key); goto out; } ret = gf_string2boolean (value, &origin_val); if (ret) { snprintf (errstr, sizeof (errstr), "%s is not a compatible " "value. %s expects an boolean value", value, key); goto out; } current_rt = get_tier_freq_threshold (volinfo, "cluster.read-freq-threshold"); if (current_rt == -1) { snprintf (errstr, sizeof (errstr), " Failed to retrive value of" "cluster.read-freq-threshold"); goto out; } current_wt = get_tier_freq_threshold (volinfo, "cluster.write-freq-threshold"); if (current_wt == -1) { snprintf (errstr, sizeof (errstr), " Failed to retrive value of" "cluster.write-freq-threshold"); goto out; } /* If record-counters is set to off */ if (!origin_val) { /* Both the thresholds should be zero to set * record-counters to off*/ if (current_rt || current_wt) { snprintf (errstr, sizeof (errstr), "Cannot set features.record-counters to \"%s\"" " as cluster.write-freq-threshold is %d" " and cluster.read-freq-threshold is %d. Please" " set both cluster.write-freq-threshold and " " cluster.read-freq-threshold to 0, to set " " features.record-counters to \"%s\".", value, current_wt, current_rt, value); ret = -1; goto out; } } /* TODO give a warning message to the user. errstr without re = -1 will * not result in a warning on cli for now. else { if (!current_rt && !current_wt) { snprintf (errstr, sizeof (errstr), " Note : cluster.write-freq-threshold is %d" " and cluster.read-freq-threshold is %d. Please" " set both cluster.write-freq-threshold and " " cluster.read-freq-threshold to" " appropriate positive values.", current_wt, current_rt); } }*/ 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; } /* Validation for tiering frequency thresholds * If any of the frequency thresholds are set to a non-zero value, * switch record-counters on, if not already on * If both the frequency thresholds are set to zero, * switch record-counters off, if not already off * */ static int validate_tier_thresholds (glusterd_volinfo_t *volinfo, dict_t *dict, char *key, char *value, char **op_errstr) { char errstr[2048] = ""; int ret = -1; xlator_t *this = NULL; int origin_val = -1; gf_boolean_t current_rc = _gf_false; char *str_current_rc = NULL; int current_wt = 0; int current_rt = 0; char *str_current_wt = NULL; char *str_current_rt = NULL; gf_boolean_t is_set_rc = _gf_false; char *proposed_rc = NULL; gf_boolean_t is_set_wrt_thsd = _gf_false; this = THIS; GF_ASSERT (this); if (volinfo->type != GF_CLUSTER_TYPE_TIER) { snprintf (errstr, sizeof (errstr), "Volume %s is not a tier " "volume. Option %s is only valid for tier volume.", volinfo->volname, key); goto out; } 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); goto out; } if (origin_val < 0) { snprintf (errstr, sizeof (errstr), "%s is not a " "compatible value. %s expects a positive" "integer value.", value, key); goto out; } /* Get the record-counters value */ ret = glusterd_volinfo_get_boolean (volinfo, "features.record-counters"); if (ret == -1) { snprintf (errstr, sizeof (errstr), "Failed to retrive value of" "features.record-counters from volume info"); goto out; } current_rc = ret; /* if any of the thresholds are set to a non-zero value * switch record-counters on, if not already on*/ if (origin_val > 0) { if (!current_rc) { is_set_rc = _gf_true; current_rc = _gf_true; } } else { /* if the set is for write-freq-threshold */ if (strstr (key, "write-freq-threshold")) { current_rt = get_tier_freq_threshold (volinfo, "cluster.read-freq-threshold"); if (current_rt == -1) { snprintf (errstr, sizeof (errstr), " Failed to retrive value of" "cluster.read-freq-threshold"); goto out; } current_wt = origin_val; } /* else it should be read-freq-threshold */ else { current_wt = get_tier_freq_threshold (volinfo, "cluster.write-freq-threshold"); if (current_wt == -1) { snprintf (errstr, sizeof (errstr), " Failed to retrive value of" "cluster.write-freq-threshold"); goto out; } current_rt = origin_val; } /* Since both the thresholds are zero, set record-counters * to off, if not already off */ if (current_rt == 0 && current_wt == 0) { if (current_rc) { is_set_rc = _gf_true; current_rc = _gf_false; } } } /* if record-counter has to be set to proposed value */ if (is_set_rc) { if (current_rc) { ret = gf_asprintf (&proposed_rc, "on"); } else { ret = gf_asprintf (&proposed_rc, "off"); } if (ret < 0) { gf_msg (this->name, GF_LOG_ERROR, EINVAL, GD_MSG_INCOMPATIBLE_VALUE, "Failed to allocate memory to dict_value"); goto error; } ret = dict_set_str (volinfo->dict, "features.record-counters", proposed_rc); error: if (ret) { snprintf (errstr, sizeof (errstr), "Failed to set features.record-counters" "to \"%s\" automatically." "Please try to set features.record-counters " "\"%s\" manually. The options " "cluster.write-freq-threshold and " "cluster.read-freq-threshold can only " "be set to a non zero value, if " "features.record-counters is " "set to \"on\".", proposed_rc, proposed_rc); 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); if (proposed_rc) GF_FREE (proposed_rc); } return ret; } static int validate_tier (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 origin_val = -1; char *current_wm_hi = NULL; char *current_wm_low = NULL; uint64_t wm_hi = 0; uint64_t wm_low = 0; this = THIS; GF_ASSERT (this); if (volinfo->type != GF_CLUSTER_TYPE_TIER) { snprintf (errstr, sizeof (errstr), "Volume %s is not a tier " "volume. Option %s is only valid for tier volume.", volinfo->volname, key); gf_msg (this->name, GF_LOG_ERROR, EINVAL, GD_MSG_INCOMPATIBLE_VALUE, "%s", errstr); *op_errstr = gf_strdup (errstr); ret = -1; goto out; } if (strstr (key, "cluster.tier-mode")) { if (strcmp(value, "test") && strcmp(value, "cache")) { ret = -1; goto out; } goto out; } else if (strstr (key, "tier-pause")) { if (strcmp(value, "off") && strcmp(value, "on")) { ret = -1; goto out; } goto out; } /* * Rest of the volume set options for tier are expecting a positive * Integer. Change the function accordingly if this constraint is * changed. */ 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); gf_msg (this->name, GF_LOG_ERROR, EINVAL, GD_MSG_INCOMPATIBLE_VALUE, "%s", errstr); *op_errstr = gf_strdup (errstr); ret = -1; goto out; } if (strstr (key, "watermark-hi") || strstr (key, "watermark-low")) { if ((origin_val < 1) || (origin_val > 99)) { snprintf (errstr, sizeof (errstr), "%s is not a " "compatible value. %s expects a " "percentage from 1-99.", value, key); gf_msg (this->name, GF_LOG_ERROR, EINVAL, GD_MSG_INCOMPATIBLE_VALUE, "%s", errstr); *op_errstr = gf_strdup (errstr); ret = -1; goto out; } if (strstr (key, "watermark-hi")) { wm_hi = origin_val; } else { glusterd_volinfo_get (volinfo, "cluster.watermark-hi", ¤t_wm_hi); gf_string2bytesize_uint64 (current_wm_hi, &wm_hi); } if (strstr (key, "watermark-low")) { wm_low = origin_val; } else { glusterd_volinfo_get (volinfo, "cluster.watermark-low", ¤t_wm_low); gf_string2bytesize_uint64 (current_wm_low, &wm_low); } if (wm_low > wm_hi) { snprintf (errstr, sizeof (errstr), "lower watermark" " cannot exceed upper watermark."); gf_msg (this->name, GF_LOG_ERROR, EINVAL, GD_MSG_INCOMPATIBLE_VALUE, "%s", errstr); *op_errstr = gf_strdup (errstr); ret = -1; goto out; } } else if (strstr (key, "tier-promote-frequency") || strstr (key, "tier-max-mb") || strstr (key, "tier-max-files") || strstr (key, "tier-demote-frequency")) { if (origin_val < 1) { snprintf (errstr, sizeof (errstr), "%s is not a " " compatible value. %s expects a positive " "integer value greater than 0.", value, key); gf_msg (this->name, GF_LOG_ERROR, EINVAL, GD_MSG_INCOMPATIBLE_VALUE, "%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_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] = ""; glusterd_conf_t *priv = NULL; int ret = 0; xlator_t *this = NULL; this = THIS; GF_ASSERT (this); if (!strcasecmp (value, "lazy") || !strcasecmp (value, "normal") || !strcasecmp (value, "aggressive")) { ret = 0; } else { ret = -1; snprintf (errstr, sizeof (errstr), "%s should be " "{lazy|normal|aggressive}", key); 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_stripe (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); if (volinfo->stripe_count == 1) { snprintf (errstr, sizeof (errstr), "Cannot set %s for a non-stripe volume.", key); gf_msg (this->name, GF_LOG_ERROR, 0, GD_MSG_NON_STRIPE_VOL, "%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 (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_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_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_TIER) { if (volinfo->tier_info.cold_type != GF_CLUSTER_TYPE_DISPERSE && volinfo->tier_info.hot_type != GF_CLUSTER_TYPE_DISPERSE) { gf_asprintf (op_errstr, "Volume %s is not containing " "disperse type", volinfo->volname); return -1; } else return 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; } /* 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