diff --git a/mon/lp-optimizer.js b/mon/lp-optimizer.js index 780b4c8d..1ea455d4 100644 --- a/mon/lp-optimizer.js +++ b/mon/lp-optimizer.js @@ -505,6 +505,7 @@ function put_aligned_pgs(aligned_pgs, int_pgs, prev_int_pgs, keygen) // Convert multi-level osd_tree = { level: number|string, id?: string, size?: number, children?: osd_tree }[] // levels = { string: number } // to a two-level osd_tree suitable for all_combinations() +// FIXME: Replace with extract_tree_levels({ level: -Infinity, children: osd_tree }, [ failure_domain_level, osd_level ], levels) function flatten_tree(osd_tree, levels, failure_domain_level, osd_level, domains = {}, i = { i: 1 }) { osd_level = levels[osd_level] || osd_level; @@ -539,6 +540,49 @@ function extract_osds(osd_tree, levels, osd_level, osds = {}) return osds; } +// Convert multi-level tree_node = { level: number|string, id?: string, size?: number, children?: tree_node[] } +// levels = { string: number } +// to a multi-level OSD tree suitable for random_hier_combinations() +// (or in case of just 2 levels - for all_combinations() / random_combinations()) +// +// Example: +// tree_node = { level: 'dc', children: [ { level: 'rack', children: [ { level: 'host', children: [ { level: 'osd', size: 10 } ] } ] } ] } +// extract_levels = [ 'rack', 'osd' ] +// level_defs = { dc: 1, rack: 2, host: 3, osd: 4 } +// +// Result: +// { rack0: { osd1: 10 } } +function extract_tree_levels(tree_node, extract_levels, level_defs, new_tree = { idx: 1, items: {} }) +{ + const next_level = Number(level_defs[extract_levels[0]] || extract_levels[0]) || 0; + const level_name = level_defs[extract_levels[0]] ? extract_levels[0] : 'l'+extract_levels[0]+'_'; + const is_leaf = extract_levels.length == 1; + if ((level_defs[tree_node.level] || tree_node.level) >= next_level) + { + if (!is_leaf) + { + // Insert a (possibly fake) level + const nt = { idx: 1, items: {} }; + new_tree.items[level_name+(new_tree.idx++)] = nt.items; + extract_tree_levels(tree_node, extract_levels.slice(1), level_defs, nt); + } + else + { + // Insert a leaf node + const leaf_id = tree_node.id || (level_name+(new_tree.idx++)); + new_tree.items[leaf_id] = tree_node.size; + } + } + else + { + for (const child_node of tree_node.children||[]) + { + extract_tree_levels(child_node, extract_levels, level_defs, new_tree); + } + } + return new_tree.items; +} + // generate random PGs with hierarchical failure domains, i.e. for example 3 DC each with 2 HOSTS // osd_tree = { level3_id: { level2_id: { level1_id: scalar_value } }, ... } // osd_tree may contain arbitrary number of levels, but level count must be the same across the whole tree @@ -853,6 +897,7 @@ module.exports = { pg_list_space_efficiency, pg_per_osd_space_efficiency, flatten_tree, + extract_tree_levels, lp_solve, make_int_pgs, diff --git a/mon/test-random-hier.js b/mon/test-random-hier.js new file mode 100644 index 00000000..3ed3f54d --- /dev/null +++ b/mon/test-random-hier.js @@ -0,0 +1,46 @@ +// Copyright (c) Vitaliy Filippov, 2019+ +// License: VNPL-1.1 (see README.md for details) + +const LPOptimizer = require('./lp-optimizer.js'); + +const osd_tree = { + 100: { 110: { 111: 1, 112: 1 }, 120: { 121: 1, 122: 1 } }, + 200: { 210: { 211: 1, 212: 1 }, 220: { 221: 1, 222: 1 } }, + 300: { 310: { 311: 1, 312: 1 }, 320: { 321: 1, 322: 1 } }, + 400: { 410: { 411: 1, 412: 1 }, 420: { 421: 1, 422: 1 } }, + 500: { 510: { 511: 1, 512: 1 }, 520: { 521: 1, 522: 1 } }, +}; + +const osd_tree2 = { + 100: { 111: 1, 112: 1, 121: 1, 122: 1 }, + 200: { 211: 1, 212: 1, 221: 1, 222: 1 }, + 300: { 311: 1, 312: 1, 321: 1, 322: 1 }, + 400: { 411: 1, 412: 1, 421: 1, 422: 1 }, + 500: { 511: 1, 512: 1, 521: 1, 522: 1 }, +}; + +async function run() +{ + let r; + console.log(r = LPOptimizer.random_hier_combinations(osd_tree, [ 3, 2, 1 ], 10000, false, true)); + console.log(r = LPOptimizer.random_hier_combinations(osd_tree2, [ 3, 2 ], 0, false, true)); + // Will contain 'Z': + console.log(r = LPOptimizer.random_combinations(osd_tree2, 6, 0, true)); + console.log(r = LPOptimizer.extract_tree_levels( + { level: 'dc', children: [ + { level: 'rack', children: [ + { level: 'host', children: [ + { level: 'osd', id: 'OSD5', size: 10 }, + ] }, + ] }, + { level: 'osd', id: 'OSD10', size: 10 }, + ] }, + [ 'rack', 'osd' ], + { dc: 1, rack: 2, host: 3, osd: 4 } + )); + if (JSON.stringify(r) != '{"rack1":{"OSD5":10},"rack2":{"OSD10":10}}') + throw new Error('extract_tree_levels failed'); + console.log('OK'); +} + +run().catch(console.error);