From ab6bd56006d8bf2c65ce526ef988c04523cf51c4 Mon Sep 17 00:00:00 2001 From: wescoeur Date: Wed, 24 Feb 2016 10:05:20 +0100 Subject: [PATCH] Initial commit. --- packages/xo-server-load-balancer/.babelrc | 11 ++ .../xo-server-load-balancer/.editorconfig | 65 ++++++++ packages/xo-server-load-balancer/.gitignore | 9 ++ packages/xo-server-load-balancer/.mocha.js | 5 + packages/xo-server-load-balancer/.mocha.opts | 1 + packages/xo-server-load-balancer/.npmignore | 10 ++ packages/xo-server-load-balancer/.travis.yml | 10 ++ packages/xo-server-load-balancer/README.md | 58 ++++++++ packages/xo-server-load-balancer/package.json | 69 +++++++++ packages/xo-server-load-balancer/src/index.js | 139 ++++++++++++++++++ .../xo-server-load-balancer/src/index.spec.js | 13 ++ 11 files changed, 390 insertions(+) create mode 100644 packages/xo-server-load-balancer/.babelrc create mode 100644 packages/xo-server-load-balancer/.editorconfig create mode 100644 packages/xo-server-load-balancer/.gitignore create mode 100644 packages/xo-server-load-balancer/.mocha.js create mode 100644 packages/xo-server-load-balancer/.mocha.opts create mode 100644 packages/xo-server-load-balancer/.npmignore create mode 100644 packages/xo-server-load-balancer/.travis.yml create mode 100644 packages/xo-server-load-balancer/README.md create mode 100644 packages/xo-server-load-balancer/package.json create mode 100644 packages/xo-server-load-balancer/src/index.js create mode 100644 packages/xo-server-load-balancer/src/index.spec.js diff --git a/packages/xo-server-load-balancer/.babelrc b/packages/xo-server-load-balancer/.babelrc new file mode 100644 index 000000000..bc055f47f --- /dev/null +++ b/packages/xo-server-load-balancer/.babelrc @@ -0,0 +1,11 @@ +{ + "comments": false, + "compact": true, + "optional": [ + "es7.asyncFunctions", + "es7.decorators", + "es7.exportExtensions", + "es7.functionBind", + "runtime" + ] +} diff --git a/packages/xo-server-load-balancer/.editorconfig b/packages/xo-server-load-balancer/.editorconfig new file mode 100644 index 000000000..da21ef4c5 --- /dev/null +++ b/packages/xo-server-load-balancer/.editorconfig @@ -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 diff --git a/packages/xo-server-load-balancer/.gitignore b/packages/xo-server-load-balancer/.gitignore new file mode 100644 index 000000000..6959be1cf --- /dev/null +++ b/packages/xo-server-load-balancer/.gitignore @@ -0,0 +1,9 @@ +/.nyc_output/ +/bower_components/ +/dist/ + +npm-debug.log +npm-debug.log.* + +!node_modules/* +node_modules/*/ diff --git a/packages/xo-server-load-balancer/.mocha.js b/packages/xo-server-load-balancer/.mocha.js new file mode 100644 index 000000000..e6d84e403 --- /dev/null +++ b/packages/xo-server-load-balancer/.mocha.js @@ -0,0 +1,5 @@ +Error.stackTraceLimit = 100 + +try { require('trace') } catch (_) {} +try { require('clarify') } catch (_) {} +try { require('source-map-support/register') } catch (_) {} diff --git a/packages/xo-server-load-balancer/.mocha.opts b/packages/xo-server-load-balancer/.mocha.opts new file mode 100644 index 000000000..6cfd94898 --- /dev/null +++ b/packages/xo-server-load-balancer/.mocha.opts @@ -0,0 +1 @@ +--require ./.mocha.js diff --git a/packages/xo-server-load-balancer/.npmignore b/packages/xo-server-load-balancer/.npmignore new file mode 100644 index 000000000..c31ee82cb --- /dev/null +++ b/packages/xo-server-load-balancer/.npmignore @@ -0,0 +1,10 @@ +/examples/ +example.js +example.js.map +*.example.js +*.example.js.map + +/test/ +/tests/ +*.spec.js +*.spec.js.map diff --git a/packages/xo-server-load-balancer/.travis.yml b/packages/xo-server-load-balancer/.travis.yml new file mode 100644 index 000000000..502095fce --- /dev/null +++ b/packages/xo-server-load-balancer/.travis.yml @@ -0,0 +1,10 @@ +language: node_js +node_js: + - 'stable' + - '4' + - '0.12' + - '0.10' + +# Use containers. +# http://docs.travis-ci.com/user/workers/container-based-infrastructure/ +sudo: false diff --git a/packages/xo-server-load-balancer/README.md b/packages/xo-server-load-balancer/README.md new file mode 100644 index 000000000..53c7d2b94 --- /dev/null +++ b/packages/xo-server-load-balancer/README.md @@ -0,0 +1,58 @@ +# xo-server-load-balancer [![Build Status](https://travis-ci.org/vatesfr/xo-server-load-balancer.png?branch=master)](https://travis-ci.org/vatesfr/xo-server-load-balancer) + +XO-Server plugin that allows load balancing. + +## Install + +Go inside your `xo-server` folder and install it: + +``` +> npm install xo-server-load-balancer +``` + +## Usage + +Edit your `xo-server` configuration and add the plugin name in the `plugins` section. + +```yaml +plugins: + + xo-server-load-balancer: +``` + +## Development + +### Installing dependencies + +``` +> npm install +``` + +### Compilation + +The sources files are watched and automatically recompiled on changes. + +``` +> npm run dev +``` + +### Tests + +``` +> npm run test-dev +``` + +## Contributions + +Contributions are *very* welcomed, either on the documentation or on +the code. + +You may: + +- report any [issue](https://github.com/vatesfr/xo-server-load-balancer/issues) + you've encountered; +- fork and create a pull request. + +## License + +AGPL3 © [Vates SAS](http://vates.fr) diff --git a/packages/xo-server-load-balancer/package.json b/packages/xo-server-load-balancer/package.json new file mode 100644 index 000000000..c09840484 --- /dev/null +++ b/packages/xo-server-load-balancer/package.json @@ -0,0 +1,69 @@ +{ + "name": "xo-server-load-balancer", + "version": "0.0.1", + "license": "AGPL-3.0", + "description": "Load balancer for XO-Server", + "keywords": [ + "load", + "balancer", + "server", + "pool", + "host" + ], + "homepage": "https://github.com/vatesfr/xo-server-load-balancer", + "bugs": "https://github.com/vatesfr/xo-server-load-balancer/issues", + "repository": { + "type": "git", + "url": "https://github.com/vatesfr/xo-server-load-balancer" + }, + "author": { + "name": "Julien Fontanet", + "email": "julien.fontanet@isonoe.net" + }, + "preferGlobal": false, + "main": "dist/", + "bin": {}, + "files": [ + "dist/" + ], + "engines": { + "node": ">=0.12" + }, + "dependencies": { + "babel-runtime": "^5.8.34", + "event-to-promise": "^0.6.0", + "lodash.filter": "^4.2.0", + "lodash.intersection": "^4.1.0", + "lodash.map": "^4.2.0", + "lodash.uniq": "^4.2.0", + "node-xmpp-client": "^3.0.0" + }, + "devDependencies": { + "babel": "^5.8.34", + "babel-eslint": "^4.1.6", + "clarify": "^1.0.5", + "dependency-check": "^2.5.1", + "mocha": "^2.3.4", + "must": "^0.13.1", + "nyc": "^3.2.2", + "source-map-support": "^0.3.3", + "standard": "^5.4.1", + "trace": "^2.0.1" + }, + "scripts": { + "build": "babel --source-maps --out-dir=dist/ src/", + "dev": "babel --watch --source-maps --out-dir=dist/ src/", + "dev-test": "mocha --opts .mocha.opts --watch --reporter=min \"dist/**/*.spec.js\"", + "lint": "standard", + "depcheck": "dependency-check ./package.json", + "posttest": "npm run lint && npm run depcheck", + "prepublish": "npm run build", + "test": "nyc mocha --opts .mocha.opts \"dist/**/*.spec.js\"" + }, + "standard": { + "ignore": [ + "dist/**" + ], + "parser": "babel-eslint" + } +} diff --git a/packages/xo-server-load-balancer/src/index.js b/packages/xo-server-load-balancer/src/index.js new file mode 100644 index 000000000..a0e21d4d5 --- /dev/null +++ b/packages/xo-server-load-balancer/src/index.js @@ -0,0 +1,139 @@ +import * as mapToArray from 'lodash.map' +import filter from 'lodash.filter' +import intersection from 'lodash.intersection' +import uniq from 'lodash.uniq' +import { CronJob } from 'cron' + +export const configurationSchema = { + type: 'object', + + properties: { + + }, + + additionalProperties: false, + required: [] +} + +// =================================================================== + +const BALANCING_MODE_PERFORMANCE = 0 + +// Delay between each ressources evaluation in minutes. +// MIN: 1, MAX: 59. +const EXECUTION_DELAY = 1 + +export const makeCronJob = (cronPattern, fn) => { + let running + + const job = new CronJob(cronPattern, async () => { + if (running) { + return + } + + running = true + + try { + await fn() + } catch (error) { + console.error('[WARN] scheduled function:', error && error.stack || error) + } finally { + running = false + } + }) + + return job +} + +class Plan { + constructor (xo, poolUuids, { + mode = BALANCING_MODE_PERFORMANCE + } = {}) { + this.xo = xo + this._mode = mode + this._poolUuids = poolUuids + } + + async execute () { + const stats = await this._getStats( + this._getHosts() + ) + + stats // FIXME + } + + // Compute hosts for each pool. They can change over time. + _getHosts () { + const objects = filter(this.xo.getObjects(), { type: 'host' }) + const hosts = {} + + for (const poolUuid of this._objects) { + hosts[poolUuid] = filter(objects, { uuid: poolUuid }) + } + + return hosts + } + + async _getStats (hosts) { + const promises = [] + + for (const poolUuid of hosts) { + promises.push(Promise.all( + mapToArray(hosts[poolUuid], host => + this.xo.getXapiHostStats(host, 'seconds') + ) + )) + } + + return await Promise.all(promises) + } +} + +class LoadBalancerPlugin { + constructor (xo) { + this.xo = xo + this._plans = [] + this._poolUuids = [] // Used pools. + } + + configure ({...conf}) { + this._cronJob = makeCronJob(`*/${EXECUTION_DELAY} * * * *`, ::this._executePlans) + } + + load () { + this._cronJob.start() + } + + unload () { + this._cronJob.stop() + } + + addPlan (name, poolUuids, mode, behavior) { + poolUuids = uniq(poolUuids) + + // Check already used pools. + if (intersection(poolUuids, this._poolUuids) > 0) { + throw new Error(`Pool(s) already included in an other plan: ${poolUuids}`) + } + + const { xo } = this + + // Test if each pool exists. + for (const poolUuid of poolUuids) { + console.log(poolUuid) + xo.getObject(poolUuid) + } + + this._plans.push(new Plan(xo, poolUuids)) + } + + async _executePlans () { + await Promise.all( + mapToArray(this._plans, plan => plan.execute()) + ) + } +} + +// =================================================================== + +export default ({ xo }) => new LoadBalancerPlugin(xo) diff --git a/packages/xo-server-load-balancer/src/index.spec.js b/packages/xo-server-load-balancer/src/index.spec.js new file mode 100644 index 000000000..6e9c776d2 --- /dev/null +++ b/packages/xo-server-load-balancer/src/index.spec.js @@ -0,0 +1,13 @@ +/* eslint-env mocha */ + +import expect from 'must' + +// =================================================================== + +import myLib from './' + +// =================================================================== + +describe('myLib', () => { + // TODO +})