216 lines
6.9 KiB
JavaScript
216 lines
6.9 KiB
JavaScript
// Copyright (c) Vitaliy Filippov, 2019+
|
|
// License: VNPL-1.1 (see README.md for details)
|
|
|
|
function get_osd_tree(global_config, state)
|
|
{
|
|
const levels = global_config.placement_levels||{};
|
|
levels.host = levels.host || 100;
|
|
levels.osd = levels.osd || 101;
|
|
const tree = {};
|
|
let up_osds = {};
|
|
// This requires monitor system time to be in sync with OSD system times (at least to some extent)
|
|
const down_time = Date.now()/1000 - global_config.osd_out_time;
|
|
for (const osd_num of Object.keys(state.osd.stats).sort((a, b) => a - b))
|
|
{
|
|
const stat = state.osd.stats[osd_num];
|
|
const osd_cfg = state.config.osd[osd_num];
|
|
let reweight = osd_cfg == null ? 1 : Number(osd_cfg.reweight);
|
|
if (reweight < 0 || isNaN(reweight))
|
|
reweight = 1;
|
|
if (stat && stat.size && reweight && (state.osd.state[osd_num] || Number(stat.time) >= down_time ||
|
|
osd_cfg && osd_cfg.noout))
|
|
{
|
|
// Numeric IDs are reserved for OSDs
|
|
if (state.osd.state[osd_num] && reweight > 0)
|
|
{
|
|
// React to down OSDs immediately
|
|
up_osds[osd_num] = true;
|
|
}
|
|
tree[osd_num] = tree[osd_num] || {};
|
|
tree[osd_num].id = osd_num;
|
|
tree[osd_num].parent = tree[osd_num].parent || stat.host;
|
|
tree[osd_num].level = 'osd';
|
|
tree[osd_num].size = reweight * stat.size / 1024 / 1024 / 1024 / 1024; // terabytes
|
|
if (osd_cfg && osd_cfg.tags)
|
|
{
|
|
tree[osd_num].tags = (osd_cfg.tags instanceof Array ? [ ...osd_cfg.tags ] : [ osd_cfg.tags ])
|
|
.reduce((a, c) => { a[c] = true; return a; }, {});
|
|
}
|
|
delete tree[osd_num].children;
|
|
if (!tree[stat.host])
|
|
{
|
|
tree[stat.host] = {
|
|
id: stat.host,
|
|
level: 'host',
|
|
parent: null,
|
|
children: [],
|
|
};
|
|
}
|
|
}
|
|
}
|
|
for (const node_id in state.config.node_placement||{})
|
|
{
|
|
const node_cfg = state.config.node_placement[node_id];
|
|
if (/^\d+$/.exec(node_id))
|
|
{
|
|
node_cfg.level = 'osd';
|
|
}
|
|
if (!node_id || !node_cfg.level || !levels[node_cfg.level] ||
|
|
node_cfg.level === 'osd' && !tree[node_id])
|
|
{
|
|
// All nodes must have non-empty IDs and valid levels
|
|
// OSDs have to actually exist
|
|
continue;
|
|
}
|
|
tree[node_id] = tree[node_id] || {};
|
|
tree[node_id].id = node_id;
|
|
tree[node_id].level = node_cfg.level;
|
|
tree[node_id].parent = node_cfg.parent;
|
|
if (node_cfg.level !== 'osd')
|
|
{
|
|
tree[node_id].children = [];
|
|
}
|
|
}
|
|
return { up_osds, levels, osd_tree: tree };
|
|
}
|
|
|
|
function make_hier_tree(global_config, tree)
|
|
{
|
|
const levels = global_config.placement_levels||{};
|
|
levels.host = levels.host || 100;
|
|
levels.osd = levels.osd || 101;
|
|
tree = { ...tree };
|
|
for (const node_id in tree)
|
|
{
|
|
tree[node_id] = { ...tree[node_id], children: [] };
|
|
}
|
|
tree[''] = { children: [] };
|
|
for (const node_id in tree)
|
|
{
|
|
if (node_id === '' || tree[node_id].level === 'osd' && (!tree[node_id].size || tree[node_id].size <= 0))
|
|
{
|
|
continue;
|
|
}
|
|
const node_cfg = tree[node_id];
|
|
const node_level = levels[node_cfg.level] || node_cfg.level;
|
|
let parent_level = node_cfg.parent && tree[node_cfg.parent] && tree[node_cfg.parent].children
|
|
&& tree[node_cfg.parent].level;
|
|
parent_level = parent_level ? (levels[parent_level] || parent_level) : null;
|
|
// Parent's level must be less than child's; OSDs must be leaves
|
|
const parent = parent_level && parent_level < node_level ? node_cfg.parent : '';
|
|
tree[parent].children.push(tree[node_id]);
|
|
}
|
|
// Delete empty nodes
|
|
let deleted = 0;
|
|
do
|
|
{
|
|
deleted = 0;
|
|
for (const node_id in tree)
|
|
{
|
|
if (tree[node_id].level !== 'osd' && (!tree[node_id].children || !tree[node_id].children.length))
|
|
{
|
|
const parent = tree[node_id].parent;
|
|
if (parent)
|
|
{
|
|
tree[parent].children = tree[parent].children.filter(c => c != tree[node_id]);
|
|
}
|
|
deleted++;
|
|
delete tree[node_id];
|
|
}
|
|
}
|
|
} while (deleted > 0);
|
|
return tree;
|
|
}
|
|
|
|
function filter_osds_by_root_node(global_config, pool_tree, root_node)
|
|
{
|
|
if (!root_node)
|
|
{
|
|
return;
|
|
}
|
|
let hier_tree = make_hier_tree(global_config, pool_tree);
|
|
let included = [ ...(hier_tree[root_node] || {}).children||[] ];
|
|
for (let i = 0; i < included.length; i++)
|
|
{
|
|
if (included[i].children)
|
|
{
|
|
included.splice(i+1, 0, ...included[i].children);
|
|
}
|
|
}
|
|
let cur = pool_tree[root_node] || {};
|
|
while (cur && cur.id)
|
|
{
|
|
included.unshift(cur);
|
|
cur = pool_tree[cur.parent||''];
|
|
}
|
|
included = included.reduce((a, c) => { a[c.id||''] = true; return a; }, {});
|
|
for (const item in pool_tree)
|
|
{
|
|
if (!included[item])
|
|
{
|
|
delete pool_tree[item];
|
|
}
|
|
}
|
|
}
|
|
|
|
function filter_osds_by_tags(orig_tree, tags)
|
|
{
|
|
if (!tags)
|
|
{
|
|
return;
|
|
}
|
|
for (const tag of (tags instanceof Array ? tags : [ tags ]))
|
|
{
|
|
for (const osd in orig_tree)
|
|
{
|
|
if (orig_tree[osd].level === 'osd' &&
|
|
(!orig_tree[osd].tags || !orig_tree[osd].tags[tag]))
|
|
{
|
|
delete orig_tree[osd];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function filter_osds_by_block_layout(orig_tree, osd_stats, block_size, bitmap_granularity, immediate_commit)
|
|
{
|
|
for (const osd in orig_tree)
|
|
{
|
|
if (orig_tree[osd].level === 'osd')
|
|
{
|
|
const osd_stat = osd_stats[osd];
|
|
if (osd_stat && (osd_stat.bs_block_size && osd_stat.bs_block_size != block_size ||
|
|
osd_stat.bitmap_granularity && osd_stat.bitmap_granularity != bitmap_granularity ||
|
|
osd_stat.immediate_commit == 'small' && immediate_commit == 'all' ||
|
|
osd_stat.immediate_commit == 'none' && immediate_commit != 'none'))
|
|
{
|
|
delete orig_tree[osd];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function get_affinity_osds(pool_cfg, up_osds, osd_tree)
|
|
{
|
|
let aff_osds = up_osds;
|
|
if (pool_cfg.primary_affinity_tags)
|
|
{
|
|
aff_osds = Object.keys(up_osds).reduce((a, c) => { a[c] = osd_tree[c]; return a; }, {});
|
|
filter_osds_by_tags(aff_osds, pool_cfg.primary_affinity_tags);
|
|
for (const osd in aff_osds)
|
|
{
|
|
aff_osds[osd] = true;
|
|
}
|
|
}
|
|
return aff_osds;
|
|
}
|
|
|
|
module.exports = {
|
|
get_osd_tree,
|
|
make_hier_tree,
|
|
filter_osds_by_root_node,
|
|
filter_osds_by_tags,
|
|
filter_osds_by_block_layout,
|
|
get_affinity_osds,
|
|
};
|