Merge remote-tracking branch 'xo-server-backup-reports/master'
This commit is contained in:
commit
56b583fc99
65
packages/xo-server-backup-reports/.editorconfig
Normal file
65
packages/xo-server-backup-reports/.editorconfig
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
# http://EditorConfig.org
|
||||||
|
#
|
||||||
|
# Julien Fontanet's configuration
|
||||||
|
# https://gist.github.com/julien-f/8096213
|
||||||
|
|
||||||
|
# Top-most EditorConfig file.
|
||||||
|
root = true
|
||||||
|
|
||||||
|
# Common config.
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespaces = true
|
||||||
|
|
||||||
|
# CoffeeScript
|
||||||
|
#
|
||||||
|
# https://github.com/polarmobile/coffeescript-style-guide/blob/master/README.md
|
||||||
|
[*.{,lit}coffee]
|
||||||
|
indent_size = 2
|
||||||
|
indent_style = space
|
||||||
|
|
||||||
|
# Markdown
|
||||||
|
[*.{md,mdwn,mdown,markdown}]
|
||||||
|
indent_size = 4
|
||||||
|
indent_style = space
|
||||||
|
|
||||||
|
# Package.json
|
||||||
|
#
|
||||||
|
# This indentation style is the one used by npm.
|
||||||
|
[/package.json]
|
||||||
|
indent_size = 2
|
||||||
|
indent_style = space
|
||||||
|
|
||||||
|
# Jade
|
||||||
|
[*.jade]
|
||||||
|
indent_size = 2
|
||||||
|
indent_style = space
|
||||||
|
|
||||||
|
# JavaScript
|
||||||
|
#
|
||||||
|
# Two spaces seems to be the standard most common style, at least in
|
||||||
|
# Node.js (http://nodeguide.com/style.html#tabs-vs-spaces).
|
||||||
|
[*.js]
|
||||||
|
indent_size = 2
|
||||||
|
indent_style = space
|
||||||
|
|
||||||
|
# Less
|
||||||
|
[*.less]
|
||||||
|
indent_size = 2
|
||||||
|
indent_style = space
|
||||||
|
|
||||||
|
# Sass
|
||||||
|
#
|
||||||
|
# Style used for http://libsass.com
|
||||||
|
[*.s[ac]ss]
|
||||||
|
indent_size = 2
|
||||||
|
indent_style = space
|
||||||
|
|
||||||
|
# YAML
|
||||||
|
#
|
||||||
|
# Only spaces are allowed.
|
||||||
|
[*.yaml]
|
||||||
|
indent_size = 2
|
||||||
|
indent_style = space
|
7
packages/xo-server-backup-reports/.gitignore
vendored
Normal file
7
packages/xo-server-backup-reports/.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
/dist/
|
||||||
|
/node_modules/
|
||||||
|
|
||||||
|
npm-debug.log
|
||||||
|
npm-debug.log.*
|
||||||
|
pnpm-debug.log
|
||||||
|
pnpm-debug.log.*
|
10
packages/xo-server-backup-reports/.npmignore
Normal file
10
packages/xo-server-backup-reports/.npmignore
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
/examples/
|
||||||
|
example.js
|
||||||
|
example.js.map
|
||||||
|
*.example.js
|
||||||
|
*.example.js.map
|
||||||
|
|
||||||
|
/test/
|
||||||
|
/tests/
|
||||||
|
*.spec.js
|
||||||
|
*.spec.js.map
|
9
packages/xo-server-backup-reports/.travis.yml
Normal file
9
packages/xo-server-backup-reports/.travis.yml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
language: node_js
|
||||||
|
node_js:
|
||||||
|
- stable
|
||||||
|
- 6
|
||||||
|
- 4
|
||||||
|
|
||||||
|
# Use containers.
|
||||||
|
# http://docs.travis-ci.com/user/workers/container-based-infrastructure/
|
||||||
|
sudo: false
|
52
packages/xo-server-backup-reports/README.md
Normal file
52
packages/xo-server-backup-reports/README.md
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# xo-server-backup-reports [](https://travis-ci.org/vatesfr/xo-server-backup-reports)
|
||||||
|
|
||||||
|
> Backup reports plugin for XO-Server
|
||||||
|
|
||||||
|
XO-Server plugin which sends email reports and Xmpp messages when backup jobs are done.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
Installation of the [npm package](https://npmjs.org/package/xo-server-backup-reports):
|
||||||
|
|
||||||
|
```
|
||||||
|
> npm install --global xo-server-backup-reports
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Like all other xo-server plugins, it can be configured directly via
|
||||||
|
the web iterface, see [the plugin documentation](https://xen-orchestra.com/docs/plugins.html).
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
```
|
||||||
|
# Install dependencies
|
||||||
|
> npm install
|
||||||
|
|
||||||
|
# Run the tests
|
||||||
|
> npm test
|
||||||
|
|
||||||
|
# Continuously compile
|
||||||
|
> npm run dev
|
||||||
|
|
||||||
|
# Continuously run the tests
|
||||||
|
> npm run dev-test
|
||||||
|
|
||||||
|
# Build for production (automatically called by npm install)
|
||||||
|
> npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contributions
|
||||||
|
|
||||||
|
Contributions are *very* welcomed, either on the documentation or on
|
||||||
|
the code.
|
||||||
|
|
||||||
|
You may:
|
||||||
|
|
||||||
|
- report any [issue](https://github.com/vatesfr/xo-server-backup-reports/issues)
|
||||||
|
you've encountered;
|
||||||
|
- fork and create a pull request.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
AGPL3 © [Vates SAS](http://vates.fr)
|
91
packages/xo-server-backup-reports/package.json
Normal file
91
packages/xo-server-backup-reports/package.json
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
{
|
||||||
|
"name": "xo-server-backup-reports",
|
||||||
|
"version": "0.6.0",
|
||||||
|
"license": "AGPL-3.0",
|
||||||
|
"description": "Backup reports plugin for XO-Server",
|
||||||
|
"keywords": [
|
||||||
|
"backup",
|
||||||
|
"email",
|
||||||
|
"mail",
|
||||||
|
"orchestra",
|
||||||
|
"plugin",
|
||||||
|
"report",
|
||||||
|
"reports",
|
||||||
|
"xen",
|
||||||
|
"xen-orchestra",
|
||||||
|
"xo-server"
|
||||||
|
],
|
||||||
|
"homepage": "https://github.com/vatesfr/xo-server-backup-reports",
|
||||||
|
"bugs": "https://github.com/vatesfr/xo-server-backup-reports/issues",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/vatesfr/xo-server-backup-reports"
|
||||||
|
},
|
||||||
|
"author": {
|
||||||
|
"name": "Julien Fontanet",
|
||||||
|
"email": "julien.fontanet@isonoe.net"
|
||||||
|
},
|
||||||
|
"preferGlobal": false,
|
||||||
|
"main": "dist/",
|
||||||
|
"bin": {},
|
||||||
|
"files": [
|
||||||
|
"dist/"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"lodash": "^4.13.1",
|
||||||
|
"moment": "^2.13.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"babel-cli": "^6.18.0",
|
||||||
|
"babel-eslint": "^7.1.1",
|
||||||
|
"babel-plugin-lodash": "^3.2.10",
|
||||||
|
"babel-preset-env": "^1.0.0",
|
||||||
|
"babel-preset-stage-0": "^6.16.0",
|
||||||
|
"cross-env": "^3.1.3",
|
||||||
|
"dependency-check": "^2.6.0",
|
||||||
|
"ghooks": "^1.3.2",
|
||||||
|
"rimraf": "^2.5.4",
|
||||||
|
"standard": "^8.5.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||||
|
"clean": "rimraf dist/",
|
||||||
|
"depcheck": "dependency-check ./package.json",
|
||||||
|
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||||
|
"lint": "standard",
|
||||||
|
"posttest": "npm run lint && npm run depcheck",
|
||||||
|
"prebuild": "npm run clean",
|
||||||
|
"predev": "npm run clean",
|
||||||
|
"prepublish": "npm run build"
|
||||||
|
},
|
||||||
|
"babel": {
|
||||||
|
"plugins": [
|
||||||
|
"lodash"
|
||||||
|
],
|
||||||
|
"presets": [
|
||||||
|
[
|
||||||
|
"env",
|
||||||
|
{
|
||||||
|
"targets": {
|
||||||
|
"node": 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stage-0"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"standard": {
|
||||||
|
"ignore": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"parser": "babel-eslint"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"ghooks": {
|
||||||
|
"commit-msg": "npm test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
178
packages/xo-server-backup-reports/src/index.js
Normal file
178
packages/xo-server-backup-reports/src/index.js
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
import moment from 'moment'
|
||||||
|
import { forEach } from 'lodash'
|
||||||
|
|
||||||
|
export const configurationSchema = {
|
||||||
|
type: 'object',
|
||||||
|
|
||||||
|
properties: {
|
||||||
|
toMails: {
|
||||||
|
type: 'array',
|
||||||
|
title: 'mails',
|
||||||
|
description: 'an array of recipients (mails)',
|
||||||
|
|
||||||
|
items: {
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
minItems: 1
|
||||||
|
},
|
||||||
|
toXmpp: {
|
||||||
|
type: 'array',
|
||||||
|
title: 'xmpp address',
|
||||||
|
description: 'an array of recipients (xmpp)',
|
||||||
|
|
||||||
|
items: {
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
minItems: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
|
||||||
|
const logError = e => {
|
||||||
|
console.error('backup report error:', e)
|
||||||
|
}
|
||||||
|
|
||||||
|
class BackupReportsXoPlugin {
|
||||||
|
constructor (xo) {
|
||||||
|
this._xo = xo
|
||||||
|
this._report = ::this._wrapper
|
||||||
|
}
|
||||||
|
|
||||||
|
configure ({ toMails, toXmpp }) {
|
||||||
|
this._mailsReceivers = toMails
|
||||||
|
this._xmppReceivers = toXmpp
|
||||||
|
}
|
||||||
|
|
||||||
|
load () {
|
||||||
|
this._xo.on('job:terminated', this._report)
|
||||||
|
}
|
||||||
|
|
||||||
|
unload () {
|
||||||
|
this._xo.removeListener('job:terminated', this._report)
|
||||||
|
}
|
||||||
|
|
||||||
|
_wrapper (status) {
|
||||||
|
return new Promise(resolve => resolve(this._listener(status))).catch(logError)
|
||||||
|
}
|
||||||
|
|
||||||
|
_listener (status) {
|
||||||
|
let nSuccess = 0
|
||||||
|
let nCalls = 0
|
||||||
|
let reportWhen
|
||||||
|
|
||||||
|
const text = []
|
||||||
|
const nagiosText = []
|
||||||
|
|
||||||
|
forEach(status.calls, call => {
|
||||||
|
// Ignore call if it's not a Backup a Snapshot or a Disaster Recovery.
|
||||||
|
if (call.method !== 'vm.deltaCopy' &&
|
||||||
|
call.method !== 'vm.rollingDeltaBackup' &&
|
||||||
|
call.method !== 'vm.rollingDrCopy' &&
|
||||||
|
call.method !== 'vm.rollingSnapshot' &&
|
||||||
|
call.method !== 'vm.rollingBackup') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
reportWhen = call.params._reportWhen
|
||||||
|
|
||||||
|
if (reportWhen === 'never') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
nCalls++
|
||||||
|
if (!call.error) {
|
||||||
|
nSuccess++
|
||||||
|
}
|
||||||
|
|
||||||
|
let vm
|
||||||
|
|
||||||
|
try {
|
||||||
|
vm = this._xo.getObject(call.params.id || call.params.vm)
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
const start = moment(call.start)
|
||||||
|
const end = moment(call.end)
|
||||||
|
const duration = moment.duration(end - start).humanize()
|
||||||
|
|
||||||
|
text.push([
|
||||||
|
`### VM : ${vm ? vm.name_label : 'undefined'}`,
|
||||||
|
` - UUID: ${vm ? vm.uuid : 'undefined'}`,
|
||||||
|
call.error
|
||||||
|
? ` - Status: Failure\n - Error: ${call.error.message}`
|
||||||
|
: ' - Status: Success',
|
||||||
|
` - Start time: ${String(start)}`,
|
||||||
|
` - End time: ${String(end)}`,
|
||||||
|
` - Duration: ${duration}`
|
||||||
|
].join('\n'))
|
||||||
|
|
||||||
|
if (call.error) {
|
||||||
|
nagiosText.push(
|
||||||
|
`[ ${vm ? vm.name_label : 'undefined'} : ${call.error.message} ]`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// No backup calls.
|
||||||
|
if (nCalls === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const globalSuccess = nSuccess === nCalls
|
||||||
|
if (globalSuccess && (
|
||||||
|
reportWhen === 'fail' || // xo-web < 5
|
||||||
|
reportWhen === 'failure' // xo-web >= 5
|
||||||
|
)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const start = moment(status.start)
|
||||||
|
const end = moment(status.end)
|
||||||
|
const duration = moment.duration(end - start).humanize()
|
||||||
|
let method = status.calls[Object.keys(status.calls)[0]].method
|
||||||
|
method = method.slice(method.indexOf('.') + 1)
|
||||||
|
.replace(/([A-Z])/g, ' $1').replace(/^./, letter => letter.toUpperCase()) // humanize
|
||||||
|
const tag = status.calls[Object.keys(status.calls)[0]].params.tag
|
||||||
|
|
||||||
|
// Global status.
|
||||||
|
text.unshift([
|
||||||
|
`## Global status for "${tag}" (${method}): ${globalSuccess ? 'Success' : 'Fail'}`,
|
||||||
|
` - Start time: ${String(start)}`,
|
||||||
|
` - End time: ${String(end)}`,
|
||||||
|
` - Duration: ${duration}`,
|
||||||
|
` - Successful backed up VM number: ${nSuccess}`,
|
||||||
|
` - Failed backed up VM: ${nCalls - nSuccess}`,
|
||||||
|
''
|
||||||
|
].join('\n'))
|
||||||
|
|
||||||
|
const markdown = text.join('\n')
|
||||||
|
const markdownNagios = nagiosText.join(' ')
|
||||||
|
|
||||||
|
// TODO : Handle errors when `sendEmail` isn't present. (Plugin dependencies)
|
||||||
|
|
||||||
|
const xo = this._xo
|
||||||
|
return Promise.all([
|
||||||
|
xo.sendEmail && xo.sendEmail({
|
||||||
|
to: this._mailsReceivers,
|
||||||
|
subject: `[Xen Orchestra][${globalSuccess ? 'Success' : 'Failure'}] Backup report for ${tag}`,
|
||||||
|
markdown
|
||||||
|
}),
|
||||||
|
xo.sendToXmppClient && xo.sendToXmppClient({
|
||||||
|
to: this._xmppReceivers,
|
||||||
|
message: markdown
|
||||||
|
}),
|
||||||
|
xo.sendSlackMessage && xo.sendSlackMessage({
|
||||||
|
message: markdown
|
||||||
|
}),
|
||||||
|
xo.sendPassiveCheck && xo.sendPassiveCheck({
|
||||||
|
status: globalSuccess ? 0 : 2,
|
||||||
|
message: globalSuccess ? `[Xen Orchestra] [Success] Backup report for ${tag}` : `[Xen Orchestra] [Failure] Backup report for ${tag} - VMs : ${markdownNagios}`
|
||||||
|
})
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
|
||||||
|
export default ({ xo }) => new BackupReportsXoPlugin(xo)
|
Loading…
Reference in New Issue
Block a user