vitastor/src/cli_pool_cfg.cpp

474 lines
15 KiB
C++

/*
=========================================================================
Copyright (c) 2023 MIND Software LLC. All Rights Reserved.
This file is part of the Software-Defined Storage MIND UStor Project.
For more information about this product, please visit https://mindsw.io
or contact us directly at info@mindsw.io
=========================================================================
*/
#include "cli_pool_cfg.h"
bool pool_configurator_t::is_valid_scheme_string(std::string scheme_str)
{
if (scheme_str != "replicated" && scheme_str != "xor" && scheme_str != "ec")
{
error = "Coding scheme should be one of \"xor\", \"replicated\", \"ec\" or \"jerasure\"";
return false;
}
return true;
}
bool pool_configurator_t::is_valid_immediate_commit_string(std::string immediate_commit_str)
{
if (immediate_commit != "" && immediate_commit != "all" && immediate_commit != "small" && immediate_commit != "none")
{
error = "Immediate Commit should be one of \"all\", \"small\", or \"none\"";
return false;
}
return true;
}
std::string pool_configurator_t::get_error_string()
{
return error;
}
bool pool_configurator_t::parse(json11::Json cfg, bool new_pool)
{
if (new_pool) // New pool configuration
{
// Pool name (req)
name = cfg["name"].string_value();
if (name == "")
{
error = "Pool name must be given";
return false;
}
// Exclusive ec shortcut check
if (!cfg["ec"].is_null() &&
(!cfg["scheme"].is_null() || !cfg["size"].is_null() || !cfg["pg_size"].is_null() || !cfg["parity_chunks"].is_null()))
{
error = "You cannot use 'ec' shortcut together with PG size, parity chunks and scheme arguments";
return false;
}
// ec = N+K (opt)
if (cfg["ec"].is_string())
{
scheme = "ec";
// pg_size = N+K
// parity_chunks = K
int ret = sscanf(cfg["ec"].string_value().c_str(), "%lu+%lu", &pg_size, &parity_chunks);
if (ret != 2)
{
error = "Shortcut for 'ec' scheme has an invalid value. Format: --ec <N>+<K>";
return false;
}
if (!pg_size || !parity_chunks)
{
error = "<N>+<K> values for 'ec' scheme cannot be 0";
return false;
}
pg_size += parity_chunks;
}
// scheme (opt) + pg_size (req) + parity_chunks (req)
else
{
scheme = cfg["scheme"].is_string() ?
(cfg["scheme"].string_value() == "jerasure" ? "ec" : cfg["scheme"].string_value()) : "replicated";
if (!is_valid_scheme_string(scheme))
{
return false;
}
if (!cfg["size"].is_null() && !cfg["pg_size"].is_null() ||
!cfg["size"].is_null() && !cfg["size"].uint64_value() ||
!cfg["pg_size"].is_null() && !cfg["pg_size"].uint64_value())
{
error = "PG size has an invalid value";
return false;
}
pg_size = !cfg["size"].is_null() ? cfg["size"].uint64_value() : cfg["pg_size"].uint64_value();
if (!pg_size)
{
error = "PG size must be given with value >= 1";
return false;
}
if (!cfg["parity_chunks"].is_null() && !cfg["parity_chunks"].uint64_value())
{
error = "Parity chunks has an invalid value";
return false;
}
parity_chunks = cfg["parity_chunks"].uint64_value();
if (scheme == "xor" && !parity_chunks)
{
parity_chunks = 1;
}
if (!parity_chunks)
{
error = "Parity Chunks must be given with value >= 1";
return false;
}
}
// pg_minsize (opt)
if (cfg["pg_minsize"].uint64_value())
{
pg_minsize = cfg["pg_minsize"].uint64_value();
}
else
{
if (!cfg["pg_minsize"].is_null())
{
error = "PG minsize has an invalid value";
return false;
}
if (scheme == "replicated")
{
// pg_minsize = (N+K > 2) ? 2 : 1
pg_minsize = pg_size > 2 ? 2 : 1;
}
else // ec or xor
{
// pg_minsize = (K > 1) ? N + 1 : N
pg_minsize = pg_size - parity_chunks + (parity_chunks > 1 ? 1 : 0);
}
}
if (!pg_minsize)
{
error = "PG minsize must be given with value >= 1";
return false;
}
// pg_count (req)
if (!cfg["count"].is_null() && !cfg["pg_count"].is_null() ||
!cfg["count"].is_null() && !cfg["count"].uint64_value() ||
!cfg["pg_count"].is_null() && !cfg["pg_count"].uint64_value())
{
error = "PG count has an invalid value";
return false;
}
pg_count = !cfg["count"].is_null() ? cfg["count"].uint64_value() : cfg["pg_count"].uint64_value();
if (!pg_count)
{
error = "PG count must be given with value >= 1";
return false;
}
// Optional params
failure_domain = cfg["failure_domain"].string_value();
if (!cfg["max_osd_combinations"].is_null() && !cfg["max_osd_combinations"].uint64_value())
{
error = "Max OSD combinations has an invalid value";
return false;
}
max_osd_combinations = cfg["max_osd_combinations"].uint64_value();
if (!cfg["block_size"].is_null() && !cfg["block_size"].uint64_value())
{
error = "Block size has an invalid value";
return false;
}
block_size = cfg["block_size"].uint64_value();
if (!cfg["bitmap_granularity"].is_null() && !cfg["bitmap_granularity"].uint64_value())
{
error = "Bitmap granularity has an invalid value";
return false;
}
bitmap_granularity = cfg["bitmap_granularity"].uint64_value();
if (!is_valid_immediate_commit_string(cfg["immediate_commit"].string_value()))
{
return false;
}
immediate_commit = cfg["immediate_commit"].string_value();
if (!cfg["pg_stripe_size"].is_null() && !cfg["pg_stripe_size"].uint64_value())
{
error = "PG stripe size has an invalid value";
return false;
}
pg_stripe_size = cfg["pg_stripe_size"].uint64_value();
root_node = cfg["root_node"].string_value();
osd_tags = cfg["osd_tags"].string_value();
primary_affinity_tags = cfg["primary_affinity_tags"].string_value();
scrub_interval = cfg["scrub_interval"].string_value();
}
else // Modified pool configuration
{
bool has_changes = false;
// Unsupported parameters
if (!cfg["scheme"].is_null() || !cfg["parity_chunks"].is_null() || !cfg["ec"].is_null() || !cfg["bitmap_granularity"].is_null())
{
error = "Scheme, parity_chunks and bitmap_granularity parameters cannot be modified";
return false;
}
// Supported parameters
if (!cfg["name"].is_null())
{
name = cfg["name"].string_value();
has_changes = true;
}
if (!cfg["size"].is_null() || !cfg["pg_size"].is_null())
{
if (!cfg["size"].is_null() && !cfg["pg_size"].is_null())
{
error = "Cannot use both size and pg_size parameters at the same time.";
return false;
}
else if (!cfg["size"].is_null() && !cfg["size"].uint64_value() ||
!cfg["pg_size"].is_null() && !cfg["pg_size"].uint64_value())
{
error = "PG size has an invalid value";
return false;
}
pg_size = !cfg["size"].is_null() ? cfg["size"].uint64_value() : cfg["pg_size"].uint64_value();
has_changes = true;
}
if (!cfg["pg_minsize"].is_null())
{
if (!cfg["pg_minsize"].uint64_value())
{
error = "PG minsize has an invalid value";
return false;
}
pg_minsize = cfg["pg_minsize"].uint64_value();
has_changes = true;
}
if (!cfg["count"].is_null() || !cfg["pg_count"].is_null())
{
if (!cfg["count"].is_null() && !cfg["pg_count"].is_null())
{
error = "Cannot use both count and pg_count parameters at the same time.";
return false;
}
else if (!cfg["count"].is_null() && !cfg["count"].uint64_value() ||
!cfg["pg_count"].is_null() && !cfg["pg_count"].uint64_value())
{
error = "PG count has an invalid value";
return false;
}
pg_count = !cfg["count"].is_null() ? cfg["count"].uint64_value() : cfg["pg_count"].uint64_value();
has_changes = true;
}
if (!cfg["failure_domain"].is_null())
{
failure_domain = cfg["failure_domain"].string_value();
has_changes = true;
}
if (!cfg["max_osd_combinations"].is_null())
{
if (!cfg["max_osd_combinations"].uint64_value())
{
error = "Max OSD combinations has an invalid value";
return false;
}
max_osd_combinations = cfg["max_osd_combinations"].uint64_value();
has_changes = true;
}
if (!cfg["block_size"].is_null())
{
if (!cfg["block_size"].uint64_value())
{
error = "Block size has an invalid value";
return false;
}
block_size = cfg["block_size"].uint64_value();
has_changes = true;
}
if (!cfg["immediate_commit"].is_null())
{
if (!is_valid_immediate_commit_string(cfg["immediate_commit"].string_value()))
{
return false;
}
immediate_commit = cfg["immediate_commit"].string_value();
has_changes = true;
}
if (!cfg["pg_stripe_size"].is_null())
{
if (!cfg["pg_stripe_size"].uint64_value())
{
error = "PG stripe size has an invalid value";
return false;
}
pg_stripe_size = cfg["pg_stripe_size"].uint64_value();
has_changes = true;
}
if (!cfg["root_node"].is_null())
{
root_node = cfg["root_node"].string_value();
has_changes = true;
}
if (!cfg["osd_tags"].is_null())
{
osd_tags = cfg["osd_tags"].string_value();
has_changes = true;
}
if (!cfg["primary_affinity_tags"].is_null())
{
primary_affinity_tags = cfg["primary_affinity_tags"].string_value();
has_changes = true;
}
if (!cfg["scrub_interval"].is_null())
{
scrub_interval = cfg["scrub_interval"].string_value();
has_changes = true;
}
if (!has_changes)
{
error = "No changes were provided to modify pool";
return false;
}
}
return true;
}
bool pool_configurator_t::validate(etcd_state_client_t &st_cli, pool_config_t *pool_config, bool strict)
{
// Validate pool parameters
// Scheme
uint64_t p_scheme = (scheme != "" ?
(scheme == "xor" ? POOL_SCHEME_XOR : (scheme == "ec" ? POOL_SCHEME_EC : POOL_SCHEME_REPLICATED)) :
(pool_config ? pool_config->scheme : 0));
// PG size
uint64_t p_pg_size = (pg_size ? pg_size : (pool_config ? pool_config->pg_size : 0));
if (p_pg_size)
{
// Min PG size
if ((p_scheme == POOL_SCHEME_XOR || p_scheme == POOL_SCHEME_EC) && p_pg_size < 3)
{
error = "PG size cannot be less than 3 for XOR/EC pool";
return false;
}
// Max PG size
else if (p_pg_size > 256)
{
error = "PG size cannot be greater than 256";
return false;
}
}
// Parity Chunks
uint64_t p_parity_chunks = (parity_chunks ? parity_chunks : (pool_config ? pool_config->parity_chunks : 0));
if (p_parity_chunks)
{
if (p_scheme == POOL_SCHEME_XOR && p_parity_chunks > 1)
{
error = "Parity Chunks must be 1 for XOR pool";
return false;
}
if (p_scheme == POOL_SCHEME_EC && (p_parity_chunks < 1 || p_parity_chunks > p_pg_size-2))
{
error = "Parity Chunks must be between 1 and pg_size-2 for EC pool";
return false;
}
}
// PG minsize
uint64_t p_pg_minsize = (pg_minsize ? pg_minsize : (pool_config ? pool_config->pg_minsize : 0));
if (p_pg_minsize)
{
// Max PG minsize relative to PG size
if (p_pg_minsize > p_pg_size)
{
error = "PG minsize cannot be greater than "+std::to_string(p_pg_size)+" (PG size)";
return false;
}
// PG minsize relative to PG size and Parity Chunks
else if ((p_scheme == POOL_SCHEME_XOR || p_scheme == POOL_SCHEME_EC) && p_pg_minsize < (p_pg_size - p_parity_chunks))
{
error =
"PG minsize cannot be less than "+std::to_string(p_pg_size - p_parity_chunks)+" "
"(PG size - Parity Chunks) for XOR/EC pool";
return false;
}
}
// Max OSD Combinations (optional)
if (max_osd_combinations > 0 && max_osd_combinations < 100)
{
error = "Max OSD Combinations must be at least 100";
return false;
}
// Scrub interval (optional)
if (scrub_interval != "")
{
bool ok;
parse_time(scrub_interval, &ok);
if (!ok)
{
error = "Failed to parse scrub interval. Format: number + unit s/m/h/d/M/y";
return false;
}
}
// Additional checks (only if strict)
if (strict)
{
uint64_t p_block_size = block_size ? block_size :
(pool_config ? pool_config->data_block_size : st_cli.global_block_size);
uint64_t p_bitmap_granularity = bitmap_granularity ? bitmap_granularity :
(pool_config ? pool_config->bitmap_granularity : st_cli.global_bitmap_granularity);
// Block size value and range
if ((p_block_size & (p_block_size-1)) || p_block_size < MIN_DATA_BLOCK_SIZE || p_block_size > MAX_DATA_BLOCK_SIZE)
{
error =
"Data block size must be a power of two between "+std::to_string(MIN_DATA_BLOCK_SIZE)+" "
"and "+std::to_string(MAX_DATA_BLOCK_SIZE);
return false;
}
// Block size relative to bitmap granularity
if (p_block_size % p_bitmap_granularity)
{
error = "Data block size must be devisible by "+std::to_string(p_bitmap_granularity)+" (Bitmap Granularity)";
return false;
}
}
return true;
}