feat(usage-report): include CSV raw data files to sent email (#4979)
Fixes #4970
This commit is contained in:
parent
4f8e48b7d4
commit
f9886d52da
@ -10,6 +10,7 @@
|
|||||||
- [VM] Move boot order setting from Disk tab to Advanced tab [#1523](https://github.com/vatesfr/xen-orchestra/issues/1523#issuecomment-563141573) (PR [#4975](https://github.com/vatesfr/xen-orchestra/pull/4975))
|
- [VM] Move boot order setting from Disk tab to Advanced tab [#1523](https://github.com/vatesfr/xen-orchestra/issues/1523#issuecomment-563141573) (PR [#4975](https://github.com/vatesfr/xen-orchestra/pull/4975))
|
||||||
- [XOA/licenses] Display proxy licenses (PR [#4944](https://github.com/vatesfr/xen-orchestra/pull/4944))
|
- [XOA/licenses] Display proxy licenses (PR [#4944](https://github.com/vatesfr/xen-orchestra/pull/4944))
|
||||||
- [Network selector] Display pool's name [#4885](https://github.com/vatesfr/xen-orchestra/issues/4885) (PR [#4990](https://github.com/vatesfr/xen-orchestra/pull/4990))
|
- [Network selector] Display pool's name [#4885](https://github.com/vatesfr/xen-orchestra/issues/4885) (PR [#4990](https://github.com/vatesfr/xen-orchestra/pull/4990))
|
||||||
|
- [Usage report] Include CSV raw data files to the sent email [#4970](https://github.com/vatesfr/xen-orchestra/issues/4970) (PR [#4979](https://github.com/vatesfr/xen-orchestra/pull/4979))
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
|
|
||||||
@ -35,6 +36,7 @@
|
|||||||
>
|
>
|
||||||
> In case of conflict, the highest (lowest in previous list) `$version` wins.
|
> In case of conflict, the highest (lowest in previous list) `$version` wins.
|
||||||
|
|
||||||
|
- xo-server-usage-report minor
|
||||||
- @xen-orchestra/fs patch
|
- @xen-orchestra/fs patch
|
||||||
- xo-server patch
|
- xo-server patch
|
||||||
- xo-web minor
|
- xo-web minor
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
"@xen-orchestra/async-map": "^0.0.0",
|
"@xen-orchestra/async-map": "^0.0.0",
|
||||||
"@xen-orchestra/cron": "^1.0.6",
|
"@xen-orchestra/cron": "^1.0.6",
|
||||||
"@xen-orchestra/log": "^0.2.0",
|
"@xen-orchestra/log": "^0.2.0",
|
||||||
|
"csv-stringify": "^5.5.0",
|
||||||
"handlebars": "^4.0.6",
|
"handlebars": "^4.0.6",
|
||||||
"html-minifier": "^4.0.0",
|
"html-minifier": "^4.0.0",
|
||||||
"human-format": "^0.10.0",
|
"human-format": "^0.10.0",
|
||||||
|
@ -2,6 +2,7 @@ import asyncMap from '@xen-orchestra/async-map'
|
|||||||
import createLogger from '@xen-orchestra/log'
|
import createLogger from '@xen-orchestra/log'
|
||||||
import Handlebars from 'handlebars'
|
import Handlebars from 'handlebars'
|
||||||
import humanFormat from 'human-format'
|
import humanFormat from 'human-format'
|
||||||
|
import stringify from 'csv-stringify'
|
||||||
import { createSchedule } from '@xen-orchestra/cron'
|
import { createSchedule } from '@xen-orchestra/cron'
|
||||||
import { minify } from 'html-minifier'
|
import { minify } from 'html-minifier'
|
||||||
import {
|
import {
|
||||||
@ -703,6 +704,68 @@ const CRON_BY_PERIODICITY = {
|
|||||||
daily: '0 6 * * *',
|
daily: '0 6 * * *',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// let field empty in case of "NaN" and "NONE"
|
||||||
|
const CSV_CAST = {
|
||||||
|
number: value => (Number.isNaN(value) ? undefined : String(value)),
|
||||||
|
string: value => (value === 'NONE' ? undefined : value),
|
||||||
|
}
|
||||||
|
|
||||||
|
const CSV_COLUMNS = {
|
||||||
|
cpu: { key: 'cpu', header: 'CPU (%)' },
|
||||||
|
cpuEvolution: { key: 'evolution.cpu', header: 'CPU evolution (%)' },
|
||||||
|
diskRead: { key: 'diskRead', header: 'Disk read (MiB)' },
|
||||||
|
diskReadEvolution: {
|
||||||
|
key: 'evolution.diskRead',
|
||||||
|
header: 'Disk read evolution (%)',
|
||||||
|
},
|
||||||
|
diskWrite: { key: 'diskWrite', header: 'Disk write (MiB)' },
|
||||||
|
diskWriteEvolution: {
|
||||||
|
key: 'evolution.diskWrite',
|
||||||
|
header: 'Disk write evolution (%)',
|
||||||
|
},
|
||||||
|
iopsRead: { key: 'iopsRead', header: 'IOPS read' },
|
||||||
|
iopsReadEvolution: {
|
||||||
|
key: 'evolution.iopsRead',
|
||||||
|
header: 'IOPS read evolution (%)',
|
||||||
|
},
|
||||||
|
iopsTotal: { key: 'iopsTotal', header: 'IOPS total' },
|
||||||
|
iopsTotalEvolution: {
|
||||||
|
key: 'evolution.iopsTotal',
|
||||||
|
header: 'IOPS total evolution (%)',
|
||||||
|
},
|
||||||
|
iopsWrite: { key: 'iopsWrite', header: 'IOPS write' },
|
||||||
|
iopsWriteEvolution: {
|
||||||
|
key: 'evolution.iopsWrite',
|
||||||
|
header: 'IOPS write evolution (%)',
|
||||||
|
},
|
||||||
|
load: { key: 'load', header: 'Load average' },
|
||||||
|
loadEvolution: {
|
||||||
|
key: 'evolution.load',
|
||||||
|
header: 'Load average evolution (%)',
|
||||||
|
},
|
||||||
|
name: { key: 'name', header: 'Name' },
|
||||||
|
netReception: { key: 'netReception', header: 'Network RX (KiB)' },
|
||||||
|
netReceptionEvolution: {
|
||||||
|
key: 'evolution.netReception',
|
||||||
|
header: 'Network RX evolution (%)',
|
||||||
|
},
|
||||||
|
netTransmission: { key: 'netTransmission', header: 'Network TX (KiB)' },
|
||||||
|
netTransmissionEvolution: {
|
||||||
|
key: 'evolution.netTransmission',
|
||||||
|
header: 'Network TX evolution (%)',
|
||||||
|
},
|
||||||
|
ram: { key: 'ram', header: 'RAM (GiB)' },
|
||||||
|
ramEvolution: { key: 'evolution.ram', header: 'RAM evolution (%)' },
|
||||||
|
spaceFree: { key: 'freeSpace', header: 'Free space (GiB)' },
|
||||||
|
spaceTotal: { key: 'total', header: 'Total space (GiB)' },
|
||||||
|
spaceTotalEvolution: {
|
||||||
|
key: 'evolution.total',
|
||||||
|
header: 'Total space evolution (%)',
|
||||||
|
},
|
||||||
|
spaceUsed: { key: 'usedSpace', header: 'Used space (GiB)' },
|
||||||
|
uuid: { key: 'uuid', header: 'UUID' },
|
||||||
|
}
|
||||||
|
|
||||||
class UsageReportPlugin {
|
class UsageReportPlugin {
|
||||||
constructor({ xo, getDataDir }) {
|
constructor({ xo, getDataDir }) {
|
||||||
this._xo = xo
|
this._xo = xo
|
||||||
@ -772,6 +835,83 @@ class UsageReportPlugin {
|
|||||||
all: this._conf.all,
|
all: this._conf.all,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const attachments = [
|
||||||
|
{
|
||||||
|
filename: `xoReport_${currDate}.html`,
|
||||||
|
content: template(data),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
if (data.allResources !== undefined) {
|
||||||
|
attachments.push(
|
||||||
|
{
|
||||||
|
filename: `xoReport_${currDate}_vms.csv`,
|
||||||
|
content: stringify(data.allResources.vms, {
|
||||||
|
cast: CSV_CAST,
|
||||||
|
header: true,
|
||||||
|
columns: [
|
||||||
|
CSV_COLUMNS.uuid,
|
||||||
|
CSV_COLUMNS.name,
|
||||||
|
CSV_COLUMNS.cpu,
|
||||||
|
CSV_COLUMNS.cpuEvolution,
|
||||||
|
CSV_COLUMNS.ram,
|
||||||
|
CSV_COLUMNS.ramEvolution,
|
||||||
|
CSV_COLUMNS.diskRead,
|
||||||
|
CSV_COLUMNS.diskReadEvolution,
|
||||||
|
CSV_COLUMNS.diskWrite,
|
||||||
|
CSV_COLUMNS.diskWriteEvolution,
|
||||||
|
CSV_COLUMNS.iopsRead,
|
||||||
|
CSV_COLUMNS.iopsReadEvolution,
|
||||||
|
CSV_COLUMNS.iopsWrite,
|
||||||
|
CSV_COLUMNS.iopsWriteEvolution,
|
||||||
|
CSV_COLUMNS.iopsTotal,
|
||||||
|
CSV_COLUMNS.iopsTotalEvolution,
|
||||||
|
CSV_COLUMNS.netReception,
|
||||||
|
CSV_COLUMNS.netReceptionEvolution,
|
||||||
|
CSV_COLUMNS.netTransmission,
|
||||||
|
CSV_COLUMNS.netTransmissionEvolution,
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filename: `xoReport_${currDate}_hosts.csv`,
|
||||||
|
content: stringify(data.allResources.hosts, {
|
||||||
|
cast: CSV_CAST,
|
||||||
|
header: true,
|
||||||
|
columns: [
|
||||||
|
CSV_COLUMNS.uuid,
|
||||||
|
CSV_COLUMNS.name,
|
||||||
|
CSV_COLUMNS.cpu,
|
||||||
|
CSV_COLUMNS.cpuEvolution,
|
||||||
|
CSV_COLUMNS.ram,
|
||||||
|
CSV_COLUMNS.ramEvolution,
|
||||||
|
CSV_COLUMNS.load,
|
||||||
|
CSV_COLUMNS.loadEvolution,
|
||||||
|
CSV_COLUMNS.netReception,
|
||||||
|
CSV_COLUMNS.netReceptionEvolution,
|
||||||
|
CSV_COLUMNS.netTransmission,
|
||||||
|
CSV_COLUMNS.netTransmissionEvolution,
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filename: `xoReport_${currDate}_srs.csv`,
|
||||||
|
content: stringify(data.allResources.srs, {
|
||||||
|
cast: CSV_CAST,
|
||||||
|
header: true,
|
||||||
|
columns: [
|
||||||
|
CSV_COLUMNS.uuid,
|
||||||
|
CSV_COLUMNS.name,
|
||||||
|
CSV_COLUMNS.spaceTotal,
|
||||||
|
CSV_COLUMNS.spaceTotalEvolution,
|
||||||
|
CSV_COLUMNS.spaceUsed,
|
||||||
|
CSV_COLUMNS.spaceFree,
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
xo.sendEmail({
|
xo.sendEmail({
|
||||||
to: this._conf.emails,
|
to: this._conf.emails,
|
||||||
@ -782,12 +922,7 @@ class UsageReportPlugin {
|
|||||||
Please, find the attached report.
|
Please, find the attached report.
|
||||||
|
|
||||||
best regards.`,
|
best regards.`,
|
||||||
attachments: [
|
attachments,
|
||||||
{
|
|
||||||
filename: `xoReport_${currDate}.html`,
|
|
||||||
content: template(data),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
}),
|
||||||
storeData &&
|
storeData &&
|
||||||
storeStats({
|
storeStats({
|
||||||
|
@ -5088,6 +5088,11 @@ csv-parser@^2.1.0:
|
|||||||
minimist "^1.2.0"
|
minimist "^1.2.0"
|
||||||
ndjson "^1.4.0"
|
ndjson "^1.4.0"
|
||||||
|
|
||||||
|
csv-stringify@^5.5.0:
|
||||||
|
version "5.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/csv-stringify/-/csv-stringify-5.5.0.tgz#0bdeaaf60d6e15b89c752a0eceb4b4c2c8af5a8a"
|
||||||
|
integrity sha512-G05575DSO/9vFzQxZN+Srh30cNyHk0SM0ePyiTChMD5WVt7GMTVPBQf4rtgMF6mqhNCJUPw4pN8LDe8MF9EYOA==
|
||||||
|
|
||||||
cuint@^0.2.2:
|
cuint@^0.2.2:
|
||||||
version "0.2.2"
|
version "0.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/cuint/-/cuint-0.2.2.tgz#408086d409550c2631155619e9fa7bcadc3b991b"
|
resolved "https://registry.yarnpkg.com/cuint/-/cuint-0.2.2.tgz#408086d409550c2631155619e9fa7bcadc3b991b"
|
||||||
|
Loading…
Reference in New Issue
Block a user