feat(xo-server-usage-report): add top 3 SRs by IOPS (#3508)

Fixes #3306
This commit is contained in:
badrAZ
2018-10-10 11:26:33 +02:00
committed by Julien Fontanet
parent c5aabbadc2
commit d7cd87a6e4
4 changed files with 92 additions and 47 deletions

View File

@@ -6,6 +6,7 @@
- [Usage Report] Add IOPS read/write/total per VM [#3309](https://github.com/vatesfr/xen-orchestra/issues/3309) (PR [#3455](https://github.com/vatesfr/xen-orchestra/pull/3455))
- [Self service] Sort resource sets by name (PR [#3507](https://github.com/vatesfr/xen-orchestra/pull/3507))
- [Usage Report] Add top 3 SRs which use the most IOPS read/write/total [#3306](https://github.com/vatesfr/xen-orchestra/issues/3306) (PR [#3508](https://github.com/vatesfr/xen-orchestra/pull/3508))
### Bug fixes

View File

@@ -34,6 +34,7 @@
"node": ">=6"
},
"dependencies": {
"@xen-orchestra/async-map": "^0.0.0",
"@xen-orchestra/cron": "^1.0.3",
"handlebars": "^4.0.6",
"html-minifier": "^3.5.8",

View File

@@ -363,19 +363,14 @@
<div class="page">
<div class="top">
<table>
<caption>Most used storages </caption>
<caption>Top SRs</caption>
<tr>
<th />
<th>UUID</th>
<th>Name</th>
<th>value</th>
</tr>
{{#each topSrs}}
<tr>
<td>{{shortUUID this.uuid}}</td>
<td>{{this.name}}</td>
<td>{{normaliseValue this.value}} GiB</td>
</tr>
{{/each}}
{{getTopSrs topSrs}}
</table>
<table>
<caption>Hosts missing patches</caption>

View File

@@ -1,3 +1,4 @@
import asyncMap from '@xen-orchestra/async-map'
import Handlebars from 'handlebars'
import humanFormat from 'human-format'
import { createSchedule } from '@xen-orchestra/cron'
@@ -22,6 +23,8 @@ import { readFile, writeFile } from 'fs'
// ===================================================================
const GRANULARITY = 'days'
const pReadFile = promisify(readFile)
const pWriteFile = promisify(writeFile)
@@ -88,6 +91,24 @@ export const configurationSchema = {
// ===================================================================
const shortUuid = uuid => {
if (typeof uuid === 'string') {
return uuid.split('-')[0]
}
}
const formatIops = value =>
isFinite(value)
? humanFormat(value, {
unit: 'IOPS',
decimals: 2,
})
: '-'
const normaliseValue = value => (isFinite(value) ? round(value, 2) : '-')
// ===================================================================
Handlebars.registerHelper('compare', function (
lvalue,
operator,
@@ -123,16 +144,9 @@ Handlebars.registerHelper('math', function (lvalue, operator, rvalue, options) {
return mathOperators[operator](+lvalue, +rvalue)
})
Handlebars.registerHelper('shortUUID', uuid => {
if (typeof uuid === 'string') {
return uuid.split('-')[0]
}
})
Handlebars.registerHelper('shortUUID', shortUuid)
Handlebars.registerHelper(
'normaliseValue',
value => (isFinite(value) ? round(value, 2) : '-')
)
Handlebars.registerHelper('normaliseValue', normaliseValue)
Handlebars.registerHelper(
'normaliseEvolution',
@@ -146,15 +160,35 @@ Handlebars.registerHelper(
)
)
Handlebars.registerHelper('formatIops', formatIops)
const getHeader = (label, size) => `
<tr>
<td rowspan='${size + 1}' class="tableHeader">${label}</td>
</tr>
`
const getBody = ({ uuid, name, value }, transformValue, unit) => `
<tr>
<td>${shortUuid(uuid)}</td>
<td>${name}</td>
<td>${transformValue(value)}${unit !== undefined ? ` ${unit}` : ''}</td>
</tr>
`
Handlebars.registerHelper(
'formatIops',
value =>
isFinite(value)
? humanFormat(value, {
unit: 'IOPS',
decimals: 2,
})
: '-'
'getTopSrs',
({ usedSpace, iopsRead, iopsWrite, iopsTotal }) =>
new Handlebars.SafeString(`
${getHeader('Used space', usedSpace.length)}
${usedSpace.map(obj => getBody(obj, normaliseValue, 'GiB')).join('')}
${getHeader('IOPS read', iopsRead.length)}
${iopsRead.map(obj => getBody(obj, formatIops)).join('')}
${getHeader('IOPS write', iopsWrite.length)}
${iopsWrite.map(obj => getBody(obj, formatIops)).join('')}
${getHeader('IOPS total', iopsTotal.length)}
${iopsTotal.map(obj => getBody(obj, formatIops)).join('')}
`)
)
// ===================================================================
@@ -244,7 +278,7 @@ async function getVmsStats ({ runningVms, xo }) {
return orderBy(
await Promise.all(
map(runningVms, async vm => {
const { stats } = await xo.getXapiVmStats(vm, 'days')
const { stats } = await xo.getXapiVmStats(vm, GRANULARITY)
const iopsRead = METRICS_MEAN.iops(get(stats.iops, 'r'))
const iopsWrite = METRICS_MEAN.iops(get(stats.iops, 'w'))
return {
@@ -271,7 +305,7 @@ async function getHostsStats ({ runningHosts, xo }) {
return orderBy(
await Promise.all(
map(runningHosts, async host => {
const { stats } = await xo.getXapiHostStats(host, 'days')
const { stats } = await xo.getXapiHostStats(host, GRANULARITY)
return {
uuid: host.uuid,
name: host.name_label,
@@ -288,24 +322,38 @@ async function getHostsStats ({ runningHosts, xo }) {
)
}
function getSrsStats (xoObjects) {
async function getSrsStats ({ xo, xoObjects }) {
return orderBy(
map(filter(xoObjects, obj => obj.type === 'SR' && obj.size > 0), sr => {
const total = sr.size / gibPower
const used = sr.physical_usage / gibPower
let name = sr.name_label
if (!sr.shared) {
name += ` (${find(xoObjects, { id: sr.$container }).name_label})`
await asyncMap(
filter(
xoObjects,
obj => obj.type === 'SR' && obj.size > 0 && obj.$PBDs.length > 0
),
async sr => {
const totalSpace = sr.size / gibPower
const usedSpace = sr.physical_usage / gibPower
let name = sr.name_label
if (!sr.shared) {
name += ` (${find(xoObjects, { id: sr.$container }).name_label})`
}
const { stats } = await xo.getXapiSrStats(sr.id, GRANULARITY)
const iopsRead = computeMean(get(stats.iops, 'r'))
const iopsWrite = computeMean(get(stats.iops, 'w'))
return {
uuid: sr.uuid,
name,
totalSpace,
usedSpace,
freeSpace: totalSpace - usedSpace,
iopsRead,
iopsWrite,
iopsTotal: iopsRead + iopsWrite,
}
}
return {
uuid: sr.uuid,
name,
total,
used,
free: total - used,
}
}),
'total',
),
'name',
'desc'
)
}
@@ -386,8 +434,8 @@ function getTopHosts ({ hostsStats, xo }) {
])
}
function getTopSrs ({ srsStats, xo }) {
return getTop(srsStats, ['used']).used
function getTopSrs (srsStats) {
return getTop(srsStats, ['usedSpace', 'iopsRead', 'iopsWrite', 'iopsTotal'])
}
async function getHostsMissingPatches ({ runningHosts, xo }) {
@@ -529,7 +577,7 @@ async function dataBuilder ({ xo, storedStatsPath, all }) {
xo.getAllUsers(),
getVmsStats({ xo, runningVms }),
getHostsStats({ xo, runningHosts }),
getSrsStats(xoObjects),
getSrsStats({ xo, xoObjects }),
getHostsMissingPatches({ xo, runningHosts }),
])
@@ -545,7 +593,7 @@ async function dataBuilder ({ xo, storedStatsPath, all }) {
computeGlobalHostsStats({ xo, hostsStats, haltedHosts }),
getTopVms({ xo, vmsStats }),
getTopHosts({ xo, hostsStats }),
getTopSrs({ xo, srsStats }),
getTopSrs(srsStats),
getAllUsersEmail(users),
])