Make code modular.

This commit is contained in:
wescoeur 2016-03-24 09:46:14 +01:00
parent 3ddb4d2b23
commit 06f60b7d92
5 changed files with 609 additions and 583 deletions

View File

@ -0,0 +1,207 @@
import filter from 'lodash.filter'
import clone from 'lodash.clonedeep'
import { default as mapToArray } from 'lodash.map'
import Plan from './plan'
import {
debug
} from './utils'
// ===================================================================
export default class DensityPlan extends Plan {
constructor (xo, name, poolIds, options) {
super(xo, name, poolIds, options)
}
_checkRessourcesThresholds (objects, averages) {
return filter(objects, object =>
averages[object.id].memoryFree > this._thresholds.memoryFree.low
)
}
async execute () {
const results = await this._findHostsToOptimize()
if (!results) {
return
}
const {
hosts,
toOptimize
} = results
let {
averages: hostsAverages
} = results
const pools = await this._getPlanPools()
let optimizationsCount = 0
for (const hostToOptimize of toOptimize) {
const {
id: hostId,
$poolId: poolId
} = hostToOptimize
const {
master: masterId
} = pools[poolId]
// Avoid master optimization.
if (masterId === hostId) {
continue
}
let poolMaster // Pool master.
const poolHosts = [] // Without master.
const masters = [] // Without the master of this loop.
const otherHosts = []
for (const dest of hosts) {
const {
id: destId,
$poolId: destPoolId
} = dest
// Destination host != Host to optimize!
if (destId === hostId) {
continue
}
if (destPoolId === poolId) {
if (destId === masterId) {
poolMaster = dest
} else {
poolHosts.push(dest)
}
} else if (destId === pools[destPoolId].master) {
masters.push(dest)
} else {
otherHosts.push(dest)
}
}
const simulResults = await this._simulate({
host: hostToOptimize,
destinations: [
[ poolMaster ],
poolHosts,
masters,
otherHosts
],
hostsAverages: clone(hostsAverages)
})
if (simulResults) {
// Update stats.
hostsAverages = simulResults.hostsAverages
// Migrate.
await this._migrate(simulResults.moves)
optimizationsCount++
}
}
debug(`Density mode: ${optimizationsCount} optimizations.`)
}
async _simulate ({ host, destinations, hostsAverages }) {
const { id: hostId } = host
debug(`Try to optimize Host (${hostId}).`)
const vms = await this._getVms(hostId)
const vmsAverages = await this._getVmsAverages(vms, host)
// Sort vms by amount of memory. (+ -> -)
vms.sort((a, b) =>
vmsAverages[b.id].memory - vmsAverages[a.id].memory
)
const simulResults = {
hostsAverages,
moves: []
}
// Try to find a destination for each VM.
for (const vm of vms) {
let move
// Simulate the VM move on a destinations set.
for (const subDestinations of destinations) {
move = this._testMigration({
vm,
destinations: subDestinations,
hostsAverages,
vmsAverages
})
// Destination found.
if (move) {
simulResults.moves.push(move)
break
}
}
// Unable to move a VM.
if (!move) {
return
}
}
// Done.
return simulResults
}
// Test if a VM migration on a destination (of a destinations set) is possible.
_testMigration ({ vm, destinations, hostsAverages, vmsAverages }) {
const {
_thresholds: {
critical: criticalThreshold
}
} = this
// Sort the destinations by available memory. (- -> +)
destinations.sort((a, b) =>
hostsAverages[a.id].memoryFree - hostsAverages[b.id].memoryFree
)
for (const destination of destinations) {
const destinationAverages = hostsAverages[destination.id]
const vmAverages = vmsAverages[vm.id]
// Unable to move the VM.
if (
destinationAverages.cpu + vmAverages.cpu >= criticalThreshold ||
destinationAverages.memoryFree - vmAverages.memory <= criticalThreshold
) {
continue
}
destinationAverages.cpu += vmAverages.cpu
destinationAverages.memoryFree -= vmAverages.memory
// Available movement.
return {
vm,
destination
}
}
}
async _migrate (moves) {
await Promise.all(
mapToArray(moves, move => {
const {
vm,
destination
} = move
const xapiSrc = this.xo.getXapi(destination)
debug(`Migrate VM (${vm.id}) to Host (${destination.id}) from Host (${vm.$container}).`)
// xapiSrc.migrateVm(vm._xapiId, this.xo.getXapi(destination), destination._xapiId)
})
)
}
}

View File

@ -1,50 +1,24 @@
import EventEmitter from 'events'
import clone from 'lodash.clonedeep'
import differenceBy from 'lodash.differenceby'
import eventToPromise from 'event-to-promise'
import filter from 'lodash.filter'
import includes from 'lodash.includes'
import intersection from 'lodash.intersection'
import uniq from 'lodash.uniq'
import { CronJob } from 'cron'
import { default as mapToArray } from 'lodash.map'
import DensityPlan from './density-plan'
import PerformancePlan from './performance-plan'
import {
EXECUTION_DELAY,
debug
} from './utils'
class Emitter extends EventEmitter {}
// ===================================================================
const noop = () => {}
const LOAD_BALANCER_DEBUG = 1
const debug = LOAD_BALANCER_DEBUG
? str => console.log(`[load-balancer]${str}`)
: noop
// ===================================================================
const PERFORMANCE_MODE = 0
const DENSITY_MODE = 1
// Delay between each ressources evaluation in minutes.
// Must be less than MINUTES_OF_HISTORICAL_DATA.
const EXECUTION_DELAY = 1
const MINUTES_OF_HISTORICAL_DATA = 30
// CPU threshold in percent.
const DEFAULT_CRITICAL_THRESHOLD_CPU = 90.0
// Memory threshold in MB.
const DEFAULT_CRITICAL_THRESHOLD_MEMORY_FREE = 64.0
// Thresholds factors.
const HIGH_THRESHOLD_FACTOR = 0.85
const LOW_THRESHOLD_FACTOR = 0.25
const HIGH_THRESHOLD_MEMORY_FREE_FACTOR = 1.25
const LOW_THRESHOLD_MEMORY_FREE_FACTOR = 20.0
// ===================================================================
export const configurationSchema = {
@ -145,556 +119,6 @@ const makeJob = (cronPattern, fn) => {
return job
}
// Compare a list of objects and give the best.
function searchObject (objects, fun) {
let object = objects[0]
for (let i = 1; i < objects.length; i++) {
if (fun(object, objects[i]) > 0) {
object = objects[i]
}
}
return object
}
// ===================================================================
// Averages.
// ===================================================================
function computeAverage (values, nPoints = values.length) {
let sum = 0
let tot = 0
const { length } = values
for (let i = length - nPoints; i < length; i++) {
const value = values[i]
sum += value || 0
if (value) {
tot += 1
}
}
return sum / tot
}
function computeRessourcesAverage (objects, objectsStats, nPoints) {
const averages = {}
for (const object of objects) {
const { id } = object
const { stats } = objectsStats[id]
averages[id] = {
cpu: computeAverage(
mapToArray(stats.cpus, cpu => computeAverage(cpu, nPoints))
),
nCpus: stats.cpus.length,
memoryFree: computeAverage(stats.memoryFree, nPoints),
memory: computeAverage(stats.memory, nPoints)
}
}
return averages
}
function computeRessourcesAverageWithWeight (averages1, averages2, ratio) {
const averages = {}
for (const id in averages1) {
const objectAverages = averages[id] = {}
for (const averageName in averages1[id]) {
objectAverages[averageName] = averages1[id][averageName] * ratio + averages2[id][averageName] * (1 - ratio)
}
}
return averages
}
function setRealCpuAverageOfVms (vms, vmsAverages, nCpus) {
for (const vm of vms) {
const averages = vmsAverages[vm.id]
averages.cpu *= averages.nCpus / nCpus
}
}
// ===================================================================
const numberOrDefault = (value, def) => (value >= 0) ? value : def
class Plan {
constructor (xo, name, poolIds, {
excludedHosts,
thresholds
} = {}) {
this.xo = xo
this._name = name
this._poolIds = poolIds
this._excludedHosts = excludedHosts
this._thresholds = {
cpu: {
critical: numberOrDefault(thresholds.cpu, DEFAULT_CRITICAL_THRESHOLD_CPU)
},
memoryFree: {
critical: numberOrDefault(thresholds.memoryFree, DEFAULT_CRITICAL_THRESHOLD_MEMORY_FREE) * 1024
}
}
for (const key in this._thresholds) {
const attr = this._thresholds[key]
const { critical } = attr
if (key === 'memoryFree') {
attr.high = critical * HIGH_THRESHOLD_MEMORY_FREE_FACTOR
attr.low = critical * LOW_THRESHOLD_MEMORY_FREE_FACTOR
} else {
attr.high = critical * HIGH_THRESHOLD_FACTOR
attr.low = critical * LOW_THRESHOLD_FACTOR
}
}
}
execute () {
throw new Error('Not implemented')
}
// ===================================================================
// Get hosts to optimize.
// ===================================================================
async _findHostsToOptimize () {
const hosts = this._getHosts()
const hostsStats = await this._getHostsStats(hosts, 'minutes')
// Check if a ressource's utilization exceeds threshold.
const avgNow = computeRessourcesAverage(hosts, hostsStats, EXECUTION_DELAY)
let toOptimize = this._checkRessourcesThresholds(hosts, avgNow)
// No ressource's utilization problem.
if (toOptimize.length === 0) {
debug('No hosts to optimize.')
return
}
// Check in the last 30 min interval with ratio.
const avgBefore = computeRessourcesAverage(hosts, hostsStats, MINUTES_OF_HISTORICAL_DATA)
const avgWithRatio = computeRessourcesAverageWithWeight(avgNow, avgBefore, 0.75)
toOptimize = this._checkRessourcesThresholds(toOptimize, avgWithRatio)
// No ressource's utilization problem.
if (toOptimize.length === 0) {
debug('No hosts to optimize.')
return
}
return {
toOptimize,
averages: avgWithRatio,
hosts
}
}
_checkRessourcesThresholds () {
throw new Error('Not implemented')
}
// ===================================================================
// Get objects.
// ===================================================================
_getPlanPools () {
const pools = {}
try {
for (const poolId of this._poolIds) {
pools[poolId] = this.xo.getObject(poolId)
}
} catch (_) {
return {}
}
return pools
}
// Compute hosts for each pool. They can change over time.
_getHosts () {
return differenceBy(
filter(this.xo.getObjects(), object =>
object.type === 'host' && includes(this._poolIds, object.$poolId)
),
this._excludedHosts,
val => val.id || val
)
}
async _getVms (hostId) {
return filter(this.xo.getObjects(), object =>
object.type === 'VM' &&
object.power_state === 'Running' &&
object.$container === hostId
)
}
// ===================================================================
// Get stats.
// ===================================================================
async _getHostsStats (hosts, granularity) {
const hostsStats = {}
await Promise.all(mapToArray(hosts, host =>
this.xo.getXapiHostStats(host, granularity).then(hostStats => {
hostsStats[host.id] = {
nPoints: hostStats.stats.cpus[0].length,
stats: hostStats.stats,
averages: {}
}
})
))
return hostsStats
}
async _getVmsStats (vms, granularity) {
const vmsStats = {}
await Promise.all(mapToArray(vms, vm =>
this.xo.getXapiVmStats(vm, granularity).then(vmStats => {
vmsStats[vm.id] = {
nPoints: vmStats.stats.cpus[0].length,
stats: vmStats.stats,
averages: {}
}
})
))
return vmsStats
}
async _getVmsAverages (vms, host) {
const vmsStats = await this._getVmsStats(vms, 'minutes')
const vmsAverages = computeRessourcesAverageWithWeight(
computeRessourcesAverage(vms, vmsStats, EXECUTION_DELAY),
computeRessourcesAverage(vms, vmsStats, MINUTES_OF_HISTORICAL_DATA),
0.75
)
// Compute real CPU usage. Virtuals cpus to reals cpus.
setRealCpuAverageOfVms(vms, vmsAverages, host.CPUs.cpu_count)
return vmsAverages
}
}
// ===================================================================
class PerformancePlan extends Plan {
constructor (xo, name, poolIds, options) {
super(xo, name, poolIds, options)
}
_checkRessourcesThresholds (objects, averages) {
return filter(objects, object => {
const objectAverages = averages[object.id]
return (
objectAverages.cpu >= this._thresholds.cpu.high ||
objectAverages.memoryFree <= this._thresholds.memoryFree.high
)
})
}
async execute () {
const results = await this._findHostsToOptimize()
if (!results) {
return
}
const {
averages,
toOptimize
} = results
let { hosts } = results
toOptimize.sort((a, b) => {
a = averages[a.id]
b = averages[b.id]
return (b.cpu - a.cpu) || (a.memoryFree - b.memoryFree)
})
for (const exceededHost of toOptimize) {
const { id } = exceededHost
debug(`Try to optimize Host (${exceededHost.id}).`)
hosts = filter(hosts, host => host.id !== id)
// Search bests combinations for the worst host.
await this._optimize({
exceededHost,
hosts,
hostsAverages: averages
})
}
}
async _optimize ({ exceededHost, hosts, hostsAverages }) {
const vms = await this._getVms(exceededHost.id)
const vmsAverages = await this._getVmsAverages(vms, exceededHost)
// Sort vms by cpu usage. (lower to higher)
vms.sort((a, b) =>
vmsAverages[b.id].cpu - vmsAverages[a.id].cpu
)
const exceededAverages = hostsAverages[exceededHost.id]
const promises = []
const xapiSrc = this.xo.getXapi(exceededHost)
let optimizationsCount = 0
for (const vm of vms) {
// Search host with lower cpu usage.
const destination = searchObject(hosts, (a, b) =>
hostsAverages[b.id].cpu - hostsAverages[a.id].cpu
)
const destinationAverages = hostsAverages[destination.id]
const vmAverages = vmsAverages[vm.id]
// Unable to move the vm.
if (
exceededAverages.cpu - vmAverages.cpu < destinationAverages.cpu + vmAverages.cpu ||
destinationAverages.memoryFree > vmAverages.memory
) {
continue
}
exceededAverages.cpu -= vmAverages.cpu
destinationAverages.cpu += vmAverages.cpu
exceededAverages.memoryFree += vmAverages.memory
destinationAverages.memoryFree -= vmAverages.memory
debug(`Migrate VM (${vm.id}) to Host (${destination.id}) from Host (${exceededHost.id}).`)
optimizationsCount++
// promises.push(
// xapiSrc.migrateVm(vm._xapiId, this.xo.getXapi(destination), destination._xapiId)
// )
}
await Promise.all(promises)
debug(`Performance mode: ${optimizationsCount} optimizations for Host (${exceededHost.id}).`)
return
}
}
// ===================================================================
class DensityPlan extends Plan {
constructor (xo, name, poolIds, options) {
super(xo, name, poolIds, options)
}
_checkRessourcesThresholds (objects, averages) {
return filter(objects, object =>
averages[object.id].memoryFree > this._thresholds.memoryFree.low
)
}
async execute () {
const results = await this._findHostsToOptimize()
if (!results) {
return
}
const {
hosts,
toOptimize
} = results
let {
averages: hostsAverages
} = results
const pools = await this._getPlanPools()
let optimizationsCount = 0
for (const hostToOptimize of toOptimize) {
const {
id: hostId,
$poolId: poolId
} = hostToOptimize
const {
master: masterId
} = pools[poolId]
// Avoid master optimization.
if (masterId === hostId) {
continue
}
let poolMaster // Pool master.
const poolHosts = [] // Without master.
const masters = [] // Without the master of this loop.
const otherHosts = []
for (const dest of hosts) {
const {
id: destId,
$poolId: destPoolId
} = dest
// Destination host != Host to optimize!
if (destId === hostId) {
continue
}
if (destPoolId === poolId) {
if (destId === masterId) {
poolMaster = dest
} else {
poolHosts.push(dest)
}
} else if (destId === pools[destPoolId].master) {
masters.push(dest)
} else {
otherHosts.push(dest)
}
}
const simulResults = await this._simulate({
host: hostToOptimize,
destinations: [
[ poolMaster ],
poolHosts,
masters,
otherHosts
],
hostsAverages: clone(hostsAverages)
})
if (simulResults) {
// Update stats.
hostsAverages = simulResults.hostsAverages
// Migrate.
await this._migrate(simulResults.moves)
optimizationsCount++
}
}
debug(`Density mode: ${optimizationsCount} optimizations.`)
}
async _simulate ({ host, destinations, hostsAverages }) {
const { id: hostId } = host
debug(`Try to optimize Host (${hostId}).`)
const vms = await this._getVms(hostId)
const vmsAverages = await this._getVmsAverages(vms, host)
// Sort vms by amount of memory. (+ -> -)
vms.sort((a, b) =>
vmsAverages[b.id].memory - vmsAverages[a.id].memory
)
const simulResults = {
hostsAverages,
moves: []
}
// Try to find a destination for each VM.
for (const vm of vms) {
let move
// Simulate the VM move on a destinations set.
for (const subDestinations of destinations) {
move = this._testMigration({
vm,
destinations: subDestinations,
hostsAverages,
vmsAverages
})
// Destination found.
if (move) {
simulResults.moves.push(move)
break
}
}
// Unable to move a VM.
if (!move) {
return
}
}
// Done.
return simulResults
}
// Test if a VM migration on a destination (of a destinations set) is possible.
_testMigration ({ vm, destinations, hostsAverages, vmsAverages }) {
const {
_thresholds: {
critical: criticalThreshold
}
} = this
// Sort the destinations by available memory. (- -> +)
destinations.sort((a, b) =>
hostsAverages[a.id].memoryFree - hostsAverages[b.id].memoryFree
)
for (const destination of destinations) {
const destinationAverages = hostsAverages[destination.id]
const vmAverages = vmsAverages[vm.id]
// Unable to move the VM.
if (
destinationAverages.cpu + vmAverages.cpu >= criticalThreshold ||
destinationAverages.memoryFree - vmAverages.memory <= criticalThreshold
) {
continue
}
destinationAverages.cpu += vmAverages.cpu
destinationAverages.memoryFree -= vmAverages.memory
// Available movement.
return {
vm,
destination
}
}
}
async _migrate (moves) {
await Promise.all(
mapToArray(moves, move => {
const {
vm,
destination
} = move
const xapiSrc = this.xo.getXapi(destination)
debug(`Migrate VM (${vm.id}) to Host (${destination.id}) from Host (${vm.$container}).`)
// xapiSrc.migrateVm(vm._xapiId, this.xo.getXapi(destination), destination._xapiId)
})
)
}
}
// ===================================================================
// ===================================================================

View File

@ -0,0 +1,112 @@
import filter from 'lodash.filter'
import Plan from './plan'
import {
debug,
searchObject
} from './utils'
// ===================================================================
export default class PerformancePlan extends Plan {
constructor (xo, name, poolIds, options) {
super(xo, name, poolIds, options)
}
_checkRessourcesThresholds (objects, averages) {
return filter(objects, object => {
const objectAverages = averages[object.id]
return (
objectAverages.cpu >= this._thresholds.cpu.high ||
objectAverages.memoryFree <= this._thresholds.memoryFree.high
)
})
}
async execute () {
const results = await this._findHostsToOptimize()
if (!results) {
return
}
const {
averages,
toOptimize
} = results
let { hosts } = results
toOptimize.sort((a, b) => {
a = averages[a.id]
b = averages[b.id]
return (b.cpu - a.cpu) || (a.memoryFree - b.memoryFree)
})
for (const exceededHost of toOptimize) {
const { id } = exceededHost
debug(`Try to optimize Host (${exceededHost.id}).`)
hosts = filter(hosts, host => host.id !== id)
// Search bests combinations for the worst host.
await this._optimize({
exceededHost,
hosts,
hostsAverages: averages
})
}
}
async _optimize ({ exceededHost, hosts, hostsAverages }) {
const vms = await this._getVms(exceededHost.id)
const vmsAverages = await this._getVmsAverages(vms, exceededHost)
// Sort vms by cpu usage. (lower to higher)
vms.sort((a, b) =>
vmsAverages[b.id].cpu - vmsAverages[a.id].cpu
)
const exceededAverages = hostsAverages[exceededHost.id]
const promises = []
const xapiSrc = this.xo.getXapi(exceededHost)
let optimizationsCount = 0
for (const vm of vms) {
// Search host with lower cpu usage.
const destination = searchObject(hosts, (a, b) =>
hostsAverages[b.id].cpu - hostsAverages[a.id].cpu
)
const destinationAverages = hostsAverages[destination.id]
const vmAverages = vmsAverages[vm.id]
// Unable to move the vm.
if (
exceededAverages.cpu - vmAverages.cpu < destinationAverages.cpu + vmAverages.cpu ||
destinationAverages.memoryFree > vmAverages.memory
) {
continue
}
exceededAverages.cpu -= vmAverages.cpu
destinationAverages.cpu += vmAverages.cpu
exceededAverages.memoryFree += vmAverages.memory
destinationAverages.memoryFree -= vmAverages.memory
debug(`Migrate VM (${vm.id}) to Host (${destination.id}) from Host (${exceededHost.id}).`)
optimizationsCount++
// promises.push(
// xapiSrc.migrateVm(vm._xapiId, this.xo.getXapi(destination), destination._xapiId)
// )
}
await Promise.all(promises)
debug(`Performance mode: ${optimizationsCount} optimizations for Host (${exceededHost.id}).`)
return
}
}

View File

@ -0,0 +1,257 @@
import differenceBy from 'lodash.differenceby'
import filter from 'lodash.filter'
import includes from 'lodash.includes'
import { default as mapToArray } from 'lodash.map'
import {
EXECUTION_DELAY,
debug
} from './utils'
const MINUTES_OF_HISTORICAL_DATA = 30
// CPU threshold in percent.
const DEFAULT_CRITICAL_THRESHOLD_CPU = 90.0
// Memory threshold in MB.
const DEFAULT_CRITICAL_THRESHOLD_MEMORY_FREE = 64.0
// Thresholds factors.
const HIGH_THRESHOLD_FACTOR = 0.85
const LOW_THRESHOLD_FACTOR = 0.25
const HIGH_THRESHOLD_MEMORY_FREE_FACTOR = 1.25
const LOW_THRESHOLD_MEMORY_FREE_FACTOR = 20.0
const numberOrDefault = (value, def) => (value >= 0) ? value : def
// ===================================================================
// Averages.
// ===================================================================
function computeAverage (values, nPoints = values.length) {
let sum = 0
let tot = 0
const { length } = values
for (let i = length - nPoints; i < length; i++) {
const value = values[i]
sum += value || 0
if (value) {
tot += 1
}
}
return sum / tot
}
function computeRessourcesAverage (objects, objectsStats, nPoints) {
const averages = {}
for (const object of objects) {
const { id } = object
const { stats } = objectsStats[id]
averages[id] = {
cpu: computeAverage(
mapToArray(stats.cpus, cpu => computeAverage(cpu, nPoints))
),
nCpus: stats.cpus.length,
memoryFree: computeAverage(stats.memoryFree, nPoints),
memory: computeAverage(stats.memory, nPoints)
}
}
return averages
}
function computeRessourcesAverageWithWeight (averages1, averages2, ratio) {
const averages = {}
for (const id in averages1) {
const objectAverages = averages[id] = {}
for (const averageName in averages1[id]) {
objectAverages[averageName] = averages1[id][averageName] * ratio + averages2[id][averageName] * (1 - ratio)
}
}
return averages
}
function setRealCpuAverageOfVms (vms, vmsAverages, nCpus) {
for (const vm of vms) {
const averages = vmsAverages[vm.id]
averages.cpu *= averages.nCpus / nCpus
}
}
// ===================================================================
export default class Plan {
constructor (xo, name, poolIds, {
excludedHosts,
thresholds
} = {}) {
this.xo = xo
this._name = name
this._poolIds = poolIds
this._excludedHosts = excludedHosts
this._thresholds = {
cpu: {
critical: numberOrDefault(thresholds.cpu, DEFAULT_CRITICAL_THRESHOLD_CPU)
},
memoryFree: {
critical: numberOrDefault(thresholds.memoryFree, DEFAULT_CRITICAL_THRESHOLD_MEMORY_FREE) * 1024
}
}
for (const key in this._thresholds) {
const attr = this._thresholds[key]
const { critical } = attr
if (key === 'memoryFree') {
attr.high = critical * HIGH_THRESHOLD_MEMORY_FREE_FACTOR
attr.low = critical * LOW_THRESHOLD_MEMORY_FREE_FACTOR
} else {
attr.high = critical * HIGH_THRESHOLD_FACTOR
attr.low = critical * LOW_THRESHOLD_FACTOR
}
}
}
execute () {
throw new Error('Not implemented')
}
// ===================================================================
// Get hosts to optimize.
// ===================================================================
async _findHostsToOptimize () {
const hosts = this._getHosts()
const hostsStats = await this._getHostsStats(hosts, 'minutes')
// Check if a ressource's utilization exceeds threshold.
const avgNow = computeRessourcesAverage(hosts, hostsStats, EXECUTION_DELAY)
let toOptimize = this._checkRessourcesThresholds(hosts, avgNow)
// No ressource's utilization problem.
if (toOptimize.length === 0) {
debug('No hosts to optimize.')
return
}
// Check in the last 30 min interval with ratio.
const avgBefore = computeRessourcesAverage(hosts, hostsStats, MINUTES_OF_HISTORICAL_DATA)
const avgWithRatio = computeRessourcesAverageWithWeight(avgNow, avgBefore, 0.75)
toOptimize = this._checkRessourcesThresholds(toOptimize, avgWithRatio)
// No ressource's utilization problem.
if (toOptimize.length === 0) {
debug('No hosts to optimize.')
return
}
return {
toOptimize,
averages: avgWithRatio,
hosts
}
}
_checkRessourcesThresholds () {
throw new Error('Not implemented')
}
// ===================================================================
// Get objects.
// ===================================================================
_getPlanPools () {
const pools = {}
try {
for (const poolId of this._poolIds) {
pools[poolId] = this.xo.getObject(poolId)
}
} catch (_) {
return {}
}
return pools
}
// Compute hosts for each pool. They can change over time.
_getHosts () {
return differenceBy(
filter(this.xo.getObjects(), object =>
object.type === 'host' && includes(this._poolIds, object.$poolId)
),
this._excludedHosts,
val => val.id || val
)
}
async _getVms (hostId) {
return filter(this.xo.getObjects(), object =>
object.type === 'VM' &&
object.power_state === 'Running' &&
object.$container === hostId
)
}
// ===================================================================
// Get stats.
// ===================================================================
async _getHostsStats (hosts, granularity) {
const hostsStats = {}
await Promise.all(mapToArray(hosts, host =>
this.xo.getXapiHostStats(host, granularity).then(hostStats => {
hostsStats[host.id] = {
nPoints: hostStats.stats.cpus[0].length,
stats: hostStats.stats,
averages: {}
}
})
))
return hostsStats
}
async _getVmsStats (vms, granularity) {
const vmsStats = {}
await Promise.all(mapToArray(vms, vm =>
this.xo.getXapiVmStats(vm, granularity).then(vmStats => {
vmsStats[vm.id] = {
nPoints: vmStats.stats.cpus[0].length,
stats: vmStats.stats,
averages: {}
}
})
))
return vmsStats
}
async _getVmsAverages (vms, host) {
const vmsStats = await this._getVmsStats(vms, 'minutes')
const vmsAverages = computeRessourcesAverageWithWeight(
computeRessourcesAverage(vms, vmsStats, EXECUTION_DELAY),
computeRessourcesAverage(vms, vmsStats, MINUTES_OF_HISTORICAL_DATA),
0.75
)
// Compute real CPU usage. Virtuals cpus to reals cpus.
setRealCpuAverageOfVms(vms, vmsAverages, host.CPUs.cpu_count)
return vmsAverages
}
}

View File

@ -0,0 +1,26 @@
const noop = () => {}
const LOAD_BALANCER_DEBUG = 1
// Delay between each ressources evaluation in minutes.
// Must be less than MINUTES_OF_HISTORICAL_DATA.
export const EXECUTION_DELAY = 1
// ===================================================================
export const debug = LOAD_BALANCER_DEBUG
? str => console.log(`[load-balancer]${str}`)
: noop
// Compare a list of objects and give the best.
export function searchObject (objects, fun) {
let object = objects[0]
for (let i = 1; i < objects.length; i++) {
if (fun(object, objects[i]) > 0) {
object = objects[i]
}
}
return object
}