170 lines
6.0 KiB
JavaScript
170 lines
6.0 KiB
JavaScript
// Copyright (c) Vitaliy Filippov, 2019+
|
|
// License: VNPL-1.1 (see README.md for details)
|
|
|
|
const { parse_level_indexes, parse_pg_dsl } = require('./lp_optimizer/dsl_pgs.js');
|
|
|
|
function validate_pool_cfg(pool_id, pool_cfg, placement_levels, warn)
|
|
{
|
|
pool_cfg.pg_size = Math.floor(pool_cfg.pg_size);
|
|
pool_cfg.pg_minsize = Math.floor(pool_cfg.pg_minsize);
|
|
pool_cfg.parity_chunks = Math.floor(pool_cfg.parity_chunks) || undefined;
|
|
pool_cfg.pg_count = Math.floor(pool_cfg.pg_count);
|
|
pool_cfg.max_osd_combinations = Math.floor(pool_cfg.max_osd_combinations) || 10000;
|
|
if (!/^[1-9]\d*$/.exec(''+pool_id))
|
|
{
|
|
if (warn)
|
|
console.log('Pool ID '+pool_id+' is invalid');
|
|
return false;
|
|
}
|
|
if (pool_cfg.scheme !== 'xor' && pool_cfg.scheme !== 'replicated' &&
|
|
pool_cfg.scheme !== 'ec' && pool_cfg.scheme !== 'jerasure')
|
|
{
|
|
if (warn)
|
|
console.log('Pool '+pool_id+' has invalid coding scheme (one of "xor", "replicated", "ec" and "jerasure" required)');
|
|
return false;
|
|
}
|
|
if (!pool_cfg.pg_size || pool_cfg.pg_size < 1 || pool_cfg.pg_size > 256 ||
|
|
pool_cfg.scheme !== 'replicated' && pool_cfg.pg_size < 3)
|
|
{
|
|
if (warn)
|
|
console.log('Pool '+pool_id+' has invalid pg_size');
|
|
return false;
|
|
}
|
|
if (!pool_cfg.pg_minsize || pool_cfg.pg_minsize < 1 || pool_cfg.pg_minsize > pool_cfg.pg_size ||
|
|
pool_cfg.scheme === 'xor' && pool_cfg.pg_minsize < (pool_cfg.pg_size - 1))
|
|
{
|
|
if (warn)
|
|
console.log('Pool '+pool_id+' has invalid pg_minsize');
|
|
return false;
|
|
}
|
|
if (pool_cfg.scheme === 'xor' && pool_cfg.parity_chunks != 0 && pool_cfg.parity_chunks != 1)
|
|
{
|
|
if (warn)
|
|
console.log('Pool '+pool_id+' has invalid parity_chunks (must be 1)');
|
|
return false;
|
|
}
|
|
if ((pool_cfg.scheme === 'ec' || pool_cfg.scheme === 'jerasure') &&
|
|
(pool_cfg.parity_chunks < 1 || pool_cfg.parity_chunks > pool_cfg.pg_size-2))
|
|
{
|
|
if (warn)
|
|
console.log('Pool '+pool_id+' has invalid parity_chunks (must be between 1 and pg_size-2)');
|
|
return false;
|
|
}
|
|
if (!pool_cfg.pg_count || pool_cfg.pg_count < 1)
|
|
{
|
|
if (warn)
|
|
console.log('Pool '+pool_id+' has invalid pg_count');
|
|
return false;
|
|
}
|
|
if (!pool_cfg.name)
|
|
{
|
|
if (warn)
|
|
console.log('Pool '+pool_id+' has empty name');
|
|
return false;
|
|
}
|
|
if (pool_cfg.max_osd_combinations < 100)
|
|
{
|
|
if (warn)
|
|
console.log('Pool '+pool_id+' has invalid max_osd_combinations (must be at least 100)');
|
|
return false;
|
|
}
|
|
if (pool_cfg.root_node && typeof(pool_cfg.root_node) != 'string')
|
|
{
|
|
if (warn)
|
|
console.log('Pool '+pool_id+' has invalid root_node (must be a string)');
|
|
return false;
|
|
}
|
|
if (pool_cfg.osd_tags && typeof(pool_cfg.osd_tags) != 'string' &&
|
|
(!(pool_cfg.osd_tags instanceof Array) || pool_cfg.osd_tags.filter(t => typeof t != 'string').length > 0))
|
|
{
|
|
if (warn)
|
|
console.log('Pool '+pool_id+' has invalid osd_tags (must be a string or array of strings)');
|
|
return false;
|
|
}
|
|
if (pool_cfg.primary_affinity_tags && typeof(pool_cfg.primary_affinity_tags) != 'string' &&
|
|
(!(pool_cfg.primary_affinity_tags instanceof Array) || pool_cfg.primary_affinity_tags.filter(t => typeof t != 'string').length > 0))
|
|
{
|
|
if (warn)
|
|
console.log('Pool '+pool_id+' has invalid primary_affinity_tags (must be a string or array of strings)');
|
|
return false;
|
|
}
|
|
if (!get_pg_rules(pool_id, pool_cfg, placement_levels, true))
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function get_pg_rules(pool_id, pool_cfg, placement_levels, warn)
|
|
{
|
|
if (pool_cfg.level_placement)
|
|
{
|
|
const pg_size = (0|pool_cfg.pg_size);
|
|
let rules = pool_cfg.level_placement;
|
|
if (typeof rules === 'string')
|
|
{
|
|
rules = rules.split(/\s+/).map(s => s.split(/=/, 2)).reduce((a, c) => { a[c[0]] = c[1]; return a; }, {});
|
|
}
|
|
else
|
|
{
|
|
rules = { ...rules };
|
|
}
|
|
// Always add failure_domain to prevent rules from being totally incorrect
|
|
const all_diff = [];
|
|
for (let i = 1; i <= pg_size; i++)
|
|
{
|
|
all_diff.push(i);
|
|
}
|
|
rules[pool_cfg.failure_domain || 'host'] = all_diff;
|
|
placement_levels = placement_levels||{};
|
|
placement_levels.host = placement_levels.host || 100;
|
|
placement_levels.osd = placement_levels.osd || 101;
|
|
for (const k in rules)
|
|
{
|
|
if (!placement_levels[k] || typeof rules[k] !== 'string' &&
|
|
(!(rules[k] instanceof Array) ||
|
|
rules[k].filter(s => typeof s !== 'string' && typeof s !== 'number').length > 0))
|
|
{
|
|
if (warn)
|
|
console.log('Pool '+pool_id+' configuration is invalid: level_placement should be { [level]: string | (string|number)[] }');
|
|
return null;
|
|
}
|
|
else if (rules[k].length != pg_size)
|
|
{
|
|
if (warn)
|
|
console.log('Pool '+pool_id+' configuration is invalid: values in level_placement should contain exactly pg_size ('+pg_size+') items');
|
|
return null;
|
|
}
|
|
}
|
|
return parse_level_indexes(rules);
|
|
}
|
|
else if (typeof pool_cfg.raw_placement === 'string')
|
|
{
|
|
try
|
|
{
|
|
return parse_pg_dsl(pool_cfg.raw_placement);
|
|
}
|
|
catch (e)
|
|
{
|
|
if (warn)
|
|
console.log('Pool '+pool_id+' configuration is invalid: invalid raw_placement: '+e.message);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
let rules = [ [] ];
|
|
let prev = [ 1 ];
|
|
for (let i = 1; i < pool_cfg.pg_size; i++)
|
|
{
|
|
rules.push([ [ pool_cfg.failure_domain||'host', '!=', prev ] ]);
|
|
prev = [ ...prev, i+1 ];
|
|
}
|
|
return rules;
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
validate_pool_cfg,
|
|
get_pg_rules,
|
|
};
|