MXS-1300: Make the MaxCtrl core a Node.js module

The core is now a module that is loaded by the command line client. This
allows the core library to be reused for testing.
This commit is contained in:
Markus Mäkelä
2017-07-17 16:03:49 +03:00
parent 9a6e281456
commit 9e688772cc
22 changed files with 25 additions and 14 deletions

View File

@ -0,0 +1,264 @@
/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2020-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
var request = require('request-promise-native');
var colors = require('colors/safe');
var Table = require('cli-table');
var consoleLib = require('console')
var fs = require('fs')
module.exports = function() {
this._ = require('lodash-getpath')
this.logger = console
// The main entry point into the library. This function is used to do
// cluster health checks and to propagate the commands to multiple
// servers.
this.maxctrl = function(argv, cb) {
if (argv.quiet) {
this.logger = new consoleLib.Console(fs.createWriteStream('/dev/null'),
fs.createWriteStream('/dev/null'))
}
this.argv = argv
return pingCluster(argv.hosts)
.then(function() {
var promises = []
argv.hosts.forEach(function(i) {
promises.push(cb(i))
})
return Promise.all(promises)
}, function(err) {
// One of the HTTP request pings to the cluster failed, log the error
logError(JSON.stringify(err.error, null, 4))
})
}
// Request a resource collection and format it as a table
this.getCollection = function (host, resource, fields) {
return doRequest(host, resource, function(res) {
var header = []
fields.forEach(function(i) {
header.push(Object.keys(i))
})
var table = getTable(header)
res.data.forEach(function(i) {
row = []
fields.forEach(function(p) {
var v = _.getPath(i, p[Object.keys(p)[0]], '')
if (Array.isArray(v)) {
v = v.join(', ')
}
row.push(v)
})
table.push(row)
})
logger.log(table.toString())
})
}
// Request a part of a resource as a collection
this.getSubCollection = function (host, resource, subres, fields) {
return doRequest(host, resource, function(res) {
var header = []
fields.forEach(function(i) {
header.push(Object.keys(i))
})
var table = getTable(header)
_.getPath(res.data, subres, []).forEach(function(i) {
row = []
fields.forEach(function(p) {
var v = _.getPath(i, p[Object.keys(p)[0]], '')
if (Array.isArray(v) && typeof(v[0]) != 'object') {
v = v.join(', ')
} else if (typeof(v) == 'object') {
v = JSON.stringify(v, null, 4)
}
row.push(v)
})
table.push(row)
})
logger.log(table.toString())
})
}
// Request a single resource and format it as a key-value list
this.getResource = function (host, resource, fields) {
return doRequest(host, resource, function(res) {
var table = getList()
fields.forEach(function(i) {
var k = Object.keys(i)[0]
var path = i[k]
var v = _.getPath(res.data, path, '')
if (Array.isArray(v) && typeof(v[0]) != 'object') {
v = v.join(', ')
} else if (typeof(v) == 'object') {
v = JSON.stringify(v, null, 4)
}
var o = {}
o[k] = v
table.push(o)
})
logger.log(table.toString())
})
}
this.updateValue = function(host, resource, key, value) {
var body = {}
_.set(body, key, value)
return doRequest(host, resource, null, { method: 'PATCH', body: body })
}
// Helper for converting endpoints to acutal URLs
this.getUri = function(host, secure, endpoint) {
var base = 'http://'
if (secure) {
base = 'https://'
}
return base + argv.user + ':' + argv.password + '@' + host + '/v1/' + endpoint
}
// Helper for executing requests and handling their responses, returns a
// promise that is fulfilled when all requests successfully complete. The
// promise is rejected if any of the requests fails.
this.doAsyncRequest = function(host, resource, cb, obj) {
args = obj || {}
args.uri = getUri(host, this.argv.secure, resource)
args.json = true
args.timeout = this.argv.timeout
return request(args)
.then(function(res) {
if (res && cb) {
// Request OK, returns data
return cb(res)
} else {
// Request OK, no data or data is ignored
logger.log(colors.green('OK'))
return Promise.resolve()
}
}, function(err) {
if (err.response && err.response.body) {
logError(JSON.stringify(err.response.body, null, 4))
} else if (err.statusCode) {
logError('Server responded with ' + err.statusCode)
} else {
logError('Undefined error: ' + JSON.stringify(err, null, 4))
}
return Promise.reject()
})
}
this.doRequest = function(host, resource, cb, obj) {
return doAsyncRequest(host, resource, cb, obj)
.then(this.argv.resolve, this.argv.reject)
}
this.logError = function(err) {
this.logger.error(colors.red('Error:'), err)
}
this.error = function(err) {
logger.log(colors.red('Error:'), err)
this.argv.reject()
}
}
var tsvopts = {
chars: {
'top': '' , 'top-mid': '' , 'top-left': '' , 'top-right': ''
, 'bottom': '' , 'bottom-mid': '' , 'bottom-left': '' , 'bottom-right': ''
, 'left': '' , 'left-mid': '' , 'mid': '' , 'mid-mid': ''
, 'right': '' , 'right-mid': '' , 'middle': ' '
},
style: {
'padding-left': 0,
'padding-right': 0,
compact: true
},
}
function getList() {
var opts = {
style: { head: ['cyan'] }
}
if (this.argv.tsv)
{
opts = _.assign(opts, tsvopts)
}
return new Table(opts)
}
// Creates a table-like array for output. The parameter is an array of header names
function getTable(headobj) {
for (i = 0; i < headobj.length; i++) {
headobj[i] = colors.cyan(headobj[i])
}
var opts
if (this.argv.tsv)
{
opts = _.assign(opts, tsvopts)
} else {
opts = {
head: headobj
}
}
return new Table(opts)
}
function pingCluster(hosts) {
var promises = []
hosts.forEach(function(i) {
promises.push(request('http://' + i + '/v1'))
})
return Promise.all(promises)
}

View File

@ -0,0 +1,97 @@
/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2020-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
var fs = require('fs')
var program = require('yargs');
const maxctrl_version = '1.0.0';
program
.version(maxctrl_version)
.group(['u', 'p', 'h', 's', 't', 'q', 'tsv'], 'Global Options:')
.option('u', {
alias:'user',
global: true,
default: 'admin',
describe: 'Username to use',
type: 'string'
})
.option('p', {
alias: 'password',
describe: 'Password for the user',
default: 'mariadb',
type: 'string'
})
.option('h', {
alias: 'hosts',
describe: 'List of MaxScale hosts. The hosts must be in ' +
'HOST:PORT format and each value must be separated by spaces.',
default: 'localhost:8989',
type: 'array'
})
.option('s', {
alias: 'secure',
describe: 'Enable HTTPS requests',
default: 'false',
type: 'boolean'
})
.option('t', {
alias: 'timeout',
describe: 'Request timeout in milliseconds',
default: '10000',
type: 'number'
})
.option('q', {
alias: 'quiet',
describe: 'Silence all output',
default: 'false',
type: 'boolean'
})
.option('tsv', {
describe: 'Print tab separated output',
default: 'false',
type: 'boolean'
})
.command(require('./lib/list.js'))
.command(require('./lib/show.js'))
.command(require('./lib/set.js'))
.command(require('./lib/clear.js'))
.command(require('./lib/enable.js'))
.command(require('./lib/disable.js'))
.command(require('./lib/create.js'))
.command(require('./lib/destroy.js'))
.command(require('./lib/link.js'))
.command(require('./lib/unlink.js'))
.command(require('./lib/start.js'))
.command(require('./lib/stop.js'))
.command(require('./lib/alter.js'))
.command(require('./lib/rotate.js'))
.command(require('./lib/call.js'))
.help()
.demandCommand(1, 'At least one command is required')
.command('*', 'the default command', {}, () => {
console.log('Unknown command. See output of `help` for a list of commands.')
})
module.exports.execute = function(argv, opts) {
if (opts && opts.extra_args) {
// Add extra options to the end of the argument list
argv = argv.concat(opts.extra_args)
}
return new Promise(function(resolve, reject) {
program
.parse(argv, {resolve: resolve, reject: reject})
})
}

View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2020-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
require('../common.js')()
exports.command = 'alter <command>'
exports.desc = 'Alter objects'
exports.handler = function() {}
exports.builder = function(yargs) {
yargs
.command('server <server> <key> <value>', 'Alter server parameters', {}, function(argv) {
maxctrl(argv, function(host) {
return updateValue(host, 'servers/' + argv.server, 'data.attributes.parameters.' + argv.key, argv.value)
})
})
.command('monitor <monitor> <key> <value>', 'Alter monitor parameters', {}, function(argv) {
maxctrl(argv, function(host) {
return updateValue(host, 'monitors/' + argv.monitor, 'data.attributes.parameters.' + argv.key, argv.value)
})
})
.command('service <service> <key> <value>', 'Alter service parameters', {}, function(argv) {
maxctrl(argv, function(host) {
return updateValue(host, 'services/' + argv.service, 'data.attributes.parameters.' + argv.key, argv.value)
})
})
.command('logging <key> <value>', 'Alter logging parameters', {}, function(argv) {
maxctrl(argv, function(host) {
return updateValue(host, 'maxscale/logs', 'attributes.parameters.' + argv.key, argv.value)
})
})
.command('maxscale <key> <value>', 'Alter MaxScale parameters', {}, function(argv) {
maxctrl(argv, function(host) {
return updateValue(host, 'maxscale', 'attributes.parameters.' + argv.key, argv.value)
})
})
.usage('Usage: alter <command>')
.help()
.command('*', 'the default command', {}, () => {
logger.log('Unknown command. See output of `help alter` for a list of commands.')
})
}

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2020-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
require('../common.js')()
exports.command = 'call <command>'
exports.desc = 'Call module commands'
exports.handler = function() {}
exports.builder = function(yargs) {
yargs
.command('command <module> <command> [parameters...]', 'Call a module command', {}, function(argv) {
// First we have to find the correct method to use
maxctrl(argv, function(host) {
return doRequest(host, 'maxscale/modules/' + argv.module + '/', function(resp) {
// A GET request will return the correct error if the command is not found
var verb = 'GET'
resp.data.attributes.commands.forEach(function(i) {
if (i.id == argv.command) {
verb = i.attributes.method;
}
})
return doAsyncRequest(host, 'maxscale/modules/' + argv.module + '/' + argv.command + '?' + argv.parameters.join('&'),
function(resp) {
logger.log(JSON.stringify(resp, null, 4))
}, { method: verb })
})
})
})
.usage('Usage: call <command>')
.help()
.command('*', 'the default command', {}, () => {
logger.log('Unknown command. See output of `help call` for a list of commands.')
})
}

View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2020-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
require('../common.js')()
exports.command = 'clear <command>'
exports.desc = 'Clear object state'
exports.handler = function() {}
exports.builder = function(yargs) {
yargs
.command('server <server> <state>', 'Clear server state', {}, function(argv) {
var target = 'servers/' + argv.server + '/clear?state=' + argv.state
maxctrl(argv, function(host) {
return doRequest(host, target, null, {method: 'PUT'})
})
})
.usage('Usage: clear <command>')
.help()
.command('*', 'the default command', {}, () => {
logger.log('Unknown command. See output of `help clear` for a list of commands.')
})
}

View File

@ -0,0 +1,190 @@
/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2020-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
require('../common.js')()
exports.command = 'create <command>'
exports.desc = 'Create objects'
exports.handler = function() {}
exports.builder = function(yargs) {
yargs
// Common options
.group(['protocol', 'authenticator', 'authenticator-options'], 'Common create options:')
.option('protocol', {
describe: 'Protocol module name',
type: 'string'
})
.option('authenticator', {
describe: 'Authenticator module name',
type: 'string'
})
.option('authenticator-options', {
describe: 'Option string for the authenticator',
type: 'string'
})
// Create server
.group(['services', 'monitors'], 'Create server options:')
.option('services', {
describe: 'Link the created server to these services',
type: 'array'
})
.option('monitors', {
describe: 'Link the created server to these monitors',
type: 'array'
})
.command('server <name> <host> <port>', 'Create a new server', {}, function(argv) {
var server = {
'data': {
'id': argv.name,
'type': 'servers',
'attributes': {
'parameters': {
'address': argv.host,
'port': argv.port,
'protocol': argv.protocol,
'authenticator': argv.authenticator,
'authenticator_options': argv.auth_options
}
}
}
}
if (argv.services) {
for (i = 0; i < argv.services.length; i++) {
_.set(server, 'data.relationships.services.data[' + i + ']', {id: argv.services[i], type: 'services'})
}
}
if (argv.monitors) {
for (i = 0; i < argv.monitors.length; i++) {
_.set(server, 'data.relationships.monitors.data[' + i + ']', {id: argv.monitors[i], type: 'monitors'})
}
}
maxctrl(argv, function(host) {
return doRequest(host, 'servers', null, {method: 'POST', body: server})
})
})
// Create monitor
.group(['servers'], 'Create monitor options:')
.option('servers', {
describe: 'Link the created monitor to these servers',
type: 'array'
})
.command('monitor <name> <module>', 'Create a new server', {}, function(argv) {
var monitor = {
'data': {
'id': argv.name,
'attributes': {
'module': argv.module
}
}
}
if (argv.servers) {
for (i = 0; i < argv.servers.length; i++) {
_.set(server, 'data.relationships.servers.data[' + i + ']', {id: argv.monitors[i], type: 'servers'})
}
}
maxctrl(argv, function(host) {
return doRequest(host, 'monitors', null, {method: 'POST', body: monitor})
})
})
// Create listener
.group(['interface', 'tls-key', 'tls-cert', 'tls-ca-cert', 'tls-version', 'tls-cert-verify-depth'], 'Create listener options:')
.option('interface', {
describe: 'Interface to listen on',
type: 'string',
default: '::'
})
// Should these have ssl as a prefix even though SSL isn't supported?
.option('tls-key', {
describe: 'Path to TLS key',
type: 'string'
})
.option('tls-cert', {
describe: 'Path to TLS certificate',
type: 'string'
})
.option('tls-ca-cert', {
describe: 'Path to TLS CA certificate',
type: 'string'
})
.option('tls-version', {
describe: 'TLS version to use',
type: 'string'
})
.option('tls-cert-verify-depth', {
describe: 'TLS certificate verification depth',
type: 'string'
})
.command('listener <service> <name> <port>', 'Create a new server', {}, function(argv) {
var listener = {
'data': {
'id': argv.name,
'type': 'listeners',
'attributes': {
'parameters': {
'port': argv.port,
'address': argv.interface,
'protocol': argv.protocol,
'authenticator': argv.authenticator,
'authenticator_options': argv.auth_options,
'ssl_key': argv['tls-key'],
'ssl_cert': argv['tls-cert'],
'ssl_ca_cert': argv['tls-ca-cert'],
'ssl_version': argv['tls-version'],
'ssl_cert_verify_depth': argv['tls-cert-verify-depth'],
}
}
}
}
if (argv.servers) {
for (i = 0; i < argv.servers.length; i++) {
_.set(server, 'data.relationships.servers.data[' + i + ']', {id: argv.monitors[i], type: 'servers'})
}
}
maxctrl(argv, function(host) {
return doRequest(host, 'services/' + argv.service + '/listeners', null, {method: 'POST', body: listener})
})
})
.command('user <name> <password>', 'Create a new network user', {}, function(argv) {
var user = {
'data': {
'id': argv.name,
'type': 'inet',
'attributes': {
'password': argv.password
}
}
}
maxctrl(argv, function(host) {
return doRequest(host, 'users/inet', null, {method: 'POST', body: user})
})
})
.usage('Usage: create <command>')
.help()
.command('*', 'the default command', {}, () => {
logger.log('Unknown command. See output of `help create` for a list of commands.')
})
}

View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2020-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
require('../common.js')()
exports.command = 'destroy <command>'
exports.desc = 'Destroy objects'
exports.handler = function() {}
exports.builder = function(yargs) {
yargs
.command('server <name>', 'Destroy an unused server', {}, function(argv) {
maxctrl(argv, function(host) {
return doRequest(host, 'servers/' + argv.name, null, {method: 'DELETE'})
})
})
.command('monitor <name>', 'Destroy an unused monitor', {}, function(argv) {
maxctrl(argv, function(host) {
return doRequest(host, 'monitors/' + argv.name, null, {method: 'DELETE'})
})
})
.command('listener <service> <name>', 'Destroy an unused listener', {}, function(argv) {
maxctrl(argv, function(host) {
return doRequest(host, 'services/' + argv.service + '/listeners/' + argv.name, null, {method: 'DELETE'})
})
})
.command('user <name>', 'Remove a network user', {}, function(argv) {
maxctrl(argv, function(host) {
return doRequest(host, 'users/inet/' + argv.name, null, {method: 'DELETE'})
})
})
.usage('Usage: destroy <command>')
.help()
.command('*', 'the default command', {}, () => {
logger.log('Unknown command. See output of `help destroy` for a list of commands.')
})
}

View File

@ -0,0 +1,59 @@
/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2020-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
require('../common.js')()
const log_levels = [
'debug',
'info',
'notice',
'warning'
]
exports.command = 'disable <command>'
exports.desc = 'Disable functionality'
exports.handler = function() {}
exports.builder = function(yargs) {
yargs
.command('log-priority <log>', 'Disable log priority [warning|notice|info|debug]', {}, function(argv) {
if (log_levels.indexOf(argv.log) != -1) {
maxctrl(argv, function(host) {
return updateValue(host, 'maxscale/logs', 'data.attributes.parameters.log_' + argv.log, false)
})
} else {
maxctrl(argv, function() {
error('Invalid log priority: ' + argv.log)
return Promise.reject()
})
}
})
.command('maxlog', 'Disable MaxScale logging', {}, function(argv) {
maxctrl(argv, function(host) {
return updateValue(host, 'maxscale/logs', 'data.attributes.parameters.maxlog', false)
})
})
.command('syslog', 'Disable syslog logging', {}, function(argv) {
maxctrl(argv, function(host) {
return updateValue(host, 'maxscale/logs', 'data.attributes.parameters.syslog', false)
})
})
.command('account <name>', 'Disable a Linux user account from administrative use', {}, function(argv) {
maxctrl(argv, function(host) {
return doRequest(host, 'users/unix/' + argv.name, null, { method: 'DELETE'})
})
})
.usage('Usage: disable <command>')
.help()
.command('*', 'the default command', {}, () => {
logger.log('Unknown command. See output of `help disable` for a list of commands.')
})
}

View File

@ -0,0 +1,65 @@
/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2020-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
require('../common.js')()
const log_levels = [
'debug',
'info',
'notice',
'warning'
]
exports.command = 'enable <command>'
exports.desc = 'Enable functionality'
exports.handler = function() {}
exports.builder = function(yargs) {
yargs
.command('log-priority <log>', 'Enable log priority [warning|notice|info|debug]', {}, function(argv) {
if (log_levels.indexOf(argv.log) != -1) {
maxctrl(argv, function(host) {
return updateValue(host, 'maxscale/logs', 'data.attributes.parameters.log_' + argv.log, true)
})
} else {
maxctrl(argv, function() {
error('Invalid log priority: ' + argv.log)
return Promise.reject()
})
}
})
.command('maxlog', 'Enable MaxScale logging', {}, function(argv) {
maxctrl(argv, function(host) {
return updateValue(host, 'maxscale/logs', 'data.attributes.parameters.maxlog', true)
})
})
.command('syslog', 'Enable syslog logging', {}, function(argv) {
maxctrl(argv, function(host) {
return updateValue(host, 'maxscale/logs', 'data.attributes.parameters.syslog', true)
})
})
.command('account <name>', 'Activate a Linux user account for administrative use', {}, function(argv) {
var req_body = {
data: {
id: argv.name,
type: 'unix'
}
}
maxctrl(argv, function(host) {
return doRequest(host, 'users/unix', null, { method: 'POST', body: req_body})
})
})
.usage('Usage: enable <command>')
.help()
.command('*', 'the default command', {}, () => {
logger.log('Unknown command. See output of `help enable` for a list of commands.')
})
}

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2020-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
require('../common.js')()
function addServer(argv, path, targets) {
maxctrl(argv, function(host){
return doRequest(host, path, function(res) {
var servers =_.get(res, 'data.relationships.servers.data', [])
targets.forEach(function(i){
servers.push({id: i, type: 'servers'})
})
// Update relationships and remove unnecessary parts
_.set(res, 'data.relationships.servers.data', servers)
delete res.data.attributes
return doAsyncRequest(host, path, null, {method: 'PATCH', body: res})
})
})
}
exports.command = 'link <command>'
exports.desc = 'Link objects'
exports.handler = function() {}
exports.builder = function(yargs) {
yargs
.command('service <name> <server...>', 'Link servers to a service', {}, function(argv) {
addServer(argv, 'services/' + argv.name, argv.server)
})
.command('monitor <name> <server...>', 'Link servers to a monitor', {}, function(argv) {
addServer(argv, 'monitors/' + argv.name, argv.server)
})
.usage('Usage: link <command>')
.help()
.command('*', 'the default command', {}, () => {
logger.log('Unknown command. See output of `help link` for a list of commands.')
})
}

View File

@ -0,0 +1,109 @@
/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2020-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
require('../common.js')()
exports.command = 'list <command>'
exports.desc = 'List objects'
exports.handler = function() {}
exports.builder = function(yargs) {
yargs
.command('servers', 'List servers', {}, function(argv) {
maxctrl(argv, function(host) {
return getCollection(host, 'servers', [
{'Server': 'id'},
{'Address': 'attributes.parameters.address'},
{'Port': 'attributes.parameters.port'},
{'Connections': 'attributes.statistics.connections'},
{'State': 'attributes.state'}
])
})
})
.command('services', 'List services', {}, function(argv) {
maxctrl(argv, function(host) {
return getCollection(host, 'services',[
{'Service': 'id'},
{'Router': 'attributes.router'},
{'Connections': 'attributes.connections'},
{'Total Connections': 'attributes.total_connections'},
{'Servers': 'relationships.servers.data[].id'}
])
})
})
.command('listeners <service>', 'List listeners of a service', {}, function(argv) {
maxctrl(argv, function(host) {
return getSubCollection(host, 'services/' + argv.service, 'attributes.listeners', [
{'Name': 'id'},
{'Port': 'attributes.parameters.port'},
{'Host': 'attributes.parameters.host'}
])
})
})
.command('monitors', 'List monitors', {}, function(argv) {
maxctrl(argv, function(host) {
return getCollection(host, 'monitors', [
{'Monitor': 'id'},
{'State': 'attributes.state'},
{'Servers': 'relationships.servers.data[].id'}
])
})
})
.command('sessions', 'List sessions', {}, function(argv) {
maxctrl(argv, function(host) {
return getCollection(host, 'sessions',[
{'Id': 'id'},
{'Service': 'relationships.services.data[].id'},
{'User': 'attributes.user'},
{'Host': 'attributes.remote'}
])
})
})
.command('filters', 'List filters', {}, function(argv) {
maxctrl(argv, function(host) {
return getCollection(host, 'filters', [
{'Filter': 'id'},
{'Service': 'relationships.services.data[].id'},
{'Module': 'attributes.module'}
])
})
})
.command('modules', 'List loaded modules', {}, function(argv) {
maxctrl(argv, function(host) {
return getCollection(host, 'maxscale/modules',[
{'Module':'id'},
{'Type':'attributes.module_type'},
{'Version': 'attributes.version'}
])
})
})
.command('users', 'List created network users', {}, function(argv) {
maxctrl(argv, function(host) {
return getCollection(host, 'users/inet',[
{'Name':'id'}
])
})
})
.command('commands', 'List module commands', {}, function(argv) {
maxctrl(argv, function(host) {
return getCollection(host, 'maxscale/modules',[
{'Module':'id'},
{'Commands': 'attributes.commands[].id'}
])
})
})
.usage('Usage: list <command>')
.help()
.command('*', 'the default command', {}, () => {
logger.log('Unknown command. See output of `help list` for a list of commands.')
})
}

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2020-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
require('../common.js')()
exports.command = 'rotate <command>'
exports.desc = 'Rotate log files'
exports.handler = function() {}
exports.builder = function(yargs) {
yargs
.command('logs', 'Rotate log files by closing and reopening the files', {}, function(argv) {
maxctrl(argv, function(host){
return doRequest(host, 'maxscale/logs/flush/', null, {method: 'POST'})
})
})
.usage('Usage: rotate <command>')
.help()
.command('*', 'the default command', {}, () => {
logger.log('Unknown command. See output of `help rotate` for a list of commands.')
})
}

View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2020-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
require('../common.js')()
exports.command = 'set <command>'
exports.desc = 'Set object state'
exports.handler = function() {}
exports.builder = function(yargs) {
yargs
.command('server <server> <state>', 'Set server state', {}, function(argv) {
var target = 'servers/' + argv.server + '/set?state=' + argv.state
maxctrl(argv, function(host) {
return doRequest(host, target, null, {method: 'PUT'})
})
})
.usage('Usage: set <command>')
.help()
.command('*', 'the default command', {}, () => {
logger.log('Unknown command. See output of `help set` for a list of commands.')
})
}

View File

@ -0,0 +1,124 @@
/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2020-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
require('../common.js')()
exports.command = 'show <command>'
exports.desc = 'Show objects'
exports.handler = function() {}
exports.builder = function(yargs) {
yargs
.command('server <server>', 'Show server', {}, function(argv) {
maxctrl(argv, function(host) {
return getResource(host, 'servers/' + argv.server, [
{'Server': 'id'},
{'Address': 'attributes.parameters.address'},
{'Port': 'attributes.parameters.port'},
{'State': 'attributes.state'},
{'Services': 'relationships.services.data[].id'},
{'Monitors': 'relationships.monitors.data[].id'},
{'Master ID': 'attributes.master_id'},
{'Node ID': 'attributes.node_id'},
{'Slave Server IDs': 'attributes.slaves'},
{'Statistics': 'attributes.statistics'},
{'Parameters': 'attributes.parameters'}
])
})
})
.command('service <service>', 'Show service', {}, function(argv) {
maxctrl(argv, function(host) {
return getResource(host, 'services/' + argv.service, [
{'Service': 'id'},
{'Router': 'attributes.router'},
{'State': 'attributes.state'},
{'Started At': 'attributes.started'},
{'Current Connections': 'attributes.connections'},
{'Total Connections': 'attributes.total_connections'},
{'Servers': 'relationships.servers.data[].id'},
{'Parameters': 'attributes.parameters'},
{'Router Diagnostics': 'attributes.router_diagnostics'}
])
})
})
.command('monitor <monitor>', 'Show monitor', {}, function(argv) {
maxctrl(argv, function(host) {
return getResource(host, 'monitors/' + argv.monitor, [
{'Monitor': 'id'},
{'State': 'attributes.state'},
{'Servers': 'relationships.servers.data[].id'},
{'Parameters': 'attributes.parameters'},
{'Monitor Diagnostics': 'attributes.monitor_diagnostics'}
])
})
})
.command('session <session>', 'Show session', {}, function(argv) {
maxctrl(argv, function(host) {
return getResource(host, 'sessions/' + argv.session, [
{'Id': 'id'},
{'Service': 'relationships.services.data[].id'},
{'State': 'attributes.state'},
{'User': 'attributes.user'},
{'Host': 'attributes.remote'},
{'Connected': 'attributes.connected'},
{'Idle': 'attributes.idle'}
])
})
})
.command('filter <filter>', 'Show filter', {}, function(argv) {
maxctrl(argv, function(host) {
return getResource(host, 'filters/' + argv.filter, [
{'Filter': 'id'},
{'Module': 'attributes.module'},
{'Services': 'relationships.services.data[].id'},
{'Parameters': 'attributes.parameters'}
])
})
})
.command('module <module>', 'Show loaded module', {}, function(argv) {
maxctrl(argv, function(host) {
return getResource(host, 'maxscale/modules/' + argv.module, [
{'Module': 'id'},
{'Type': 'attributes.module_type'},
{'Version': 'attributes.version'},
{'Maturity': 'attributes.maturity'},
{'Description': 'attributes.description'},
{'Parameters': 'attributes.parameters'},
{'Commands': 'attributes.commands'}
])
})
})
.command('maxscale', 'Show MaxScale information', {}, function(argv) {
maxctrl(argv, function(host) {
return getResource(host, 'maxscale', [
{'Version': 'attributes.version'},
{'Commit': 'attributes.commit'},
{'Started At': 'attributes.started_at'},
{'Uptime': 'attributes.uptime'}
])
})
})
.command('commands <module>', 'Show module commands of a module', {}, function(argv) {
maxctrl(argv, function(host) {
return getSubCollection(host, 'maxscale/modules/' + argv.module, 'attributes.commands', [
{'Command': 'id'},
{'Parameters': 'attributes.parameters[].type'},
{'Descriptions': 'attributes.parameters[].description'}
])
})
})
.usage('Usage: show <command>')
.help()
.command('*', 'the default command', {}, () => {
logger.log('Unknown command. See output of `help show` for a list of commands.')
})
}

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2020-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
require('../common.js')()
exports.command = 'start <command>'
exports.desc = 'Start objects'
exports.handler = function() {}
exports.builder = function(yargs) {
yargs
.command('service <name>', 'Start a service', {}, function(argv) {
maxctrl(argv, function(host) {
return doRequest(host, 'services/' + argv.name + '/start', null, {method: 'PUT'})
})
})
.command('monitor <name>', 'Start a monitor', {}, function(argv) {
maxctrl(argv, function(host) {
return doRequest(host, 'monitors/' + argv.name + '/start', null, {method: 'PUT'})
})
})
.usage('Usage: start <command>')
.help()
.command('*', 'the default command', {}, () => {
logger.log('Unknown command. See output of `help start` for a list of commands.')
})
}

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2020-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
require('../common.js')()
exports.command = 'stop <command>'
exports.desc = 'Stop objects'
exports.handler = function() {}
exports.builder = function(yargs) {
yargs
.command('service <name>', 'Stop a service', {}, function(argv) {
maxctrl(argv, function(host) {
return doRequest(host, 'services/' + argv.name + '/stop', null, {method: 'PUT'})
})
})
.command('monitor <name>', 'Stop a monitor', {}, function(argv) {
maxctrl(argv, function(host) {
return doRequest(host, 'monitors/' + argv.name + '/stop', null, {method: 'PUT'})
})
})
.usage('Usage: stop <command>')
.help()
.command('*', 'the default command', {}, () => {
logger.log('Unknown command. See output of `help stop` for a list of commands.')
})
}

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2020-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
require('../common.js')()
function removeServer(argv, path, targets) {
maxctrl(argv, function(host) {
return doRequest(host, path, function(res) {
var servers =_.get(res, 'data.relationships.servers.data', [])
_.remove(servers, function(i) {
return targets.indexOf(i.id) != -1
})
// Update relationships and remove unnecessary parts
_.set(res, 'data.relationships.servers.data', servers)
delete res.data.attributes
return doAsyncRequest(host, path, null, {method: 'PATCH', body: res})
})
})
}
exports.command = 'unlink <command>'
exports.desc = 'Unlink objects'
exports.handler = function() {}
exports.builder = function(yargs) {
yargs
.command('service <name> <server...>', 'Unlink servers from a service', {}, function(argv) {
removeServer(argv, 'services/' + argv.name, argv.server)
})
.command('monitor <name> <server...>', 'Unlink servers from a monitor', {}, function(argv) {
removeServer(argv, 'monitors/' + argv.name, argv.server)
})
.usage('Usage: unlink <command>')
.help()
.command('*', 'the default command', {}, () => {
logger.log('Unknown command. See output of `help unlink` for a list of commands.')
})
}

View File

@ -0,0 +1,20 @@
{
"name": "maxctrl-core",
"version": "1.0.0",
"description": "MaxScale Administrative Client Core Library",
"repository": "https://github.com/mariadb-corporation/MaxScale",
"main": "core.js",
"keywords": [
"maxscale"
],
"author": "MariaDB Corporation Ab",
"license": "SEE LICENSE IN ../../LICENSE.TXT",
"dependencies": {
"cli-table": "^0.3.1",
"lodash": "^4.17.4",
"lodash-getpath": "^0.2.4",
"request": "^2.81.0",
"request-promise-native": "^1.0.3",
"yargs": "^8.0.2"
}
}