diff --git a/packages/xo-server-transport-nagios/README.md b/packages/xo-server-transport-nagios/README.md index ccbf1ea8a..300fe1309 100644 --- a/packages/xo-server-transport-nagios/README.md +++ b/packages/xo-server-transport-nagios/README.md @@ -12,11 +12,18 @@ Installation of the [npm package](https://npmjs.org/package/xo-server-nagios): ## Usage -**TODO** +Like all other xo-server plugins, it can be configured directly via +the web interface, see [the plugin documentation](https://xen-orchestra.com/docs/plugins.html). ## Development -``` +### `Xo#sendPassiveCheck( { status, message }) ` + +This xo method is called to send a passive check to nagios and change the status of a service. +It has two parameters: +- status: it's the service status in Nagios (0: OK | 1: WARNING | 2: CRITICAL). +- message: it's the status information in Nagios. + # Install dependencies > npm install diff --git a/packages/xo-server-transport-nagios/package.json b/packages/xo-server-transport-nagios/package.json index 53275a2e0..8396d00c5 100644 --- a/packages/xo-server-transport-nagios/package.json +++ b/packages/xo-server-transport-nagios/package.json @@ -31,10 +31,14 @@ "engines": { "node": ">=4" }, - "dependencies": {}, + "dependencies": { + "babel-runtime": "^6.18.0", + "buffer-crc32": "^0.2.13" + }, "devDependencies": { "babel-cli": "^6.18.0", "babel-eslint": "^7.1.0", + "babel-plugin-transform-runtime": "^6.15.0", "babel-preset-latest": "^6.16.0", "babel-preset-stage-0": "^6.16.0", "cross-env": "^3.1.3", @@ -55,6 +59,9 @@ "prepublish": "npm run build" }, "babel": { + "plugins": [ + "transform-runtime" + ], "presets": [ "latest", "stage-0" diff --git a/packages/xo-server-transport-nagios/src/index.js b/packages/xo-server-transport-nagios/src/index.js index ef1e0aa54..98bd0e269 100644 --- a/packages/xo-server-transport-nagios/src/index.js +++ b/packages/xo-server-transport-nagios/src/index.js @@ -1,26 +1,164 @@ +import crc32 from 'buffer-crc32' +import net from 'net' +import { Buffer } from 'buffer' + +// =================================================================== + export const configurationSchema = { - foo: { - required: true, - title: 'Foo', - type: 'string' - } + type: 'object', + + properties: { + server: { + type: 'string', + description: 'The nagios server adress' + }, + port: { + type: 'integer', + description: 'The NSCA port' + }, + key: { + type: 'string', + description: 'The encryption key' + }, + host: { + type: 'string', + description: 'The host name in Nagios' + }, + service: { + type: 'string', + description: 'The service description in Nagios' + } + }, + additionalProperties: false, + required: ['server', 'port', 'key', 'host', 'service'] } +// =================================================================== + +function nscaPacketBuilder ({ + host, + iv, + message, + service, + status, + timestamp +}) { + // Building NSCA packet + const SIZE = 720 + const packet = new Buffer(SIZE) + packet.fill(0) + packet.writeInt16BE(VERSION, 0) + packet.fill('h', 2, 3) + packet.writeUInt32BE(0, 4) // initial 0 for CRC32 value + packet.writeUInt32BE(timestamp, 8) + packet.writeInt16BE(status, 12) + packet.write(host, 14, 77, ENCODING) + packet.write(service, 78, 206, ENCODING) + packet.write(message, 206, SIZE, ENCODING) + packet.writeUInt32BE(crc32.unsigned(packet), 4) + return packet +} + +function xor (data, mask) { + const dataSize = data.length + const maskSize = mask.length + const result = new Buffer(dataSize) + let j = 0 + for (let i = 0; i < dataSize; i++) { + if (j === maskSize) { + j = 0 + } + result[i] = data[i] ^ mask[j] + j++ + } + return result +} + +// =================================================================== + +export const OK = 0 +export const WARNING = 1 +export const CRITICAL = 2 + +const VERSION = 3 +const ENCODING = 'binary' + class XoServerNagios { + constructor ({ xo }) { - this._xo = xo + this._sendPassiveCheck = ::this._sendPassiveCheck + this._set = ::xo.defineProperty + this._unset = null + + // Defined in configure(). + this._conf = null + this._key = null } configure (configuration) { - throw new Error('not implemented') + this._conf = configuration + this._key = new Buffer(configuration.key, ENCODING) } load () { - throw new Error('not implemented') + this._unset = this._set('sendPassiveCheck', this._sendPassiveCheck) } unload () { - throw new Error('not implemented') + this._unset() + } + + test () { + return this._sendPassiveCheck({ + message: 'The server-nagios plugin for Xen Orchestra server seems to be working fine, nicely done :)', + status: OK + }) + } + + _sendPassiveCheck ({ + message, + status + }) { + return new Promise((resolve, reject) => { + if (/\r|\n/.test(message)) { + throw new Error('the message must not contain a line break') + } + + const client = new net.Socket() + + client.connect(this._conf.port, this._conf.server, () => { + console.log('Successful connection') + }) + + client.on('data', data => { + const timestamp = data.readInt32BE(128) + const iv = data.slice(0, 128) // initialization vector + const packet = nscaPacketBuilder({ + ...this._conf, + iv, + message, + status, + timestamp + }) + + // 1) Using xor between the NSCA packet and the initialization vector + // 2) Using xor between the result of the first operation and the encryption key + const xorPacketBuffer = xor( + xor( + packet, + iv + ), + this._key + ) + + client.write(xorPacketBuffer, res => { + client.destroy() + resolve(res) + }) + }) + + client.on('error', reject) + }) } }