1
0
Fork 0
arangodb/js/actions/api-cluster.js

882 lines
30 KiB
JavaScript

/*jshint strict: false, unused: false */
/*global AQL_EXECUTE, SYS_CLUSTER_TEST, UPGRADE_ARGS: true,
ArangoServerState, ArangoClusterComm, ArangoClusterInfo */
////////////////////////////////////////////////////////////////////////////////
/// @brief cluster actions
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2014 ArangoDB GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Max Neunhoeffer
/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany
/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany
/// @author Copyright 2013-2014, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
var actions = require("org/arangodb/actions");
var cluster = require("org/arangodb/cluster");
var internal = require("internal");
// -----------------------------------------------------------------------------
// --SECTION-- public functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @startDocuBlock JSF_cluster_test_GET
/// @brief executes a cluster roundtrip for sharding
///
/// @RESTHEADER{GET /_admin/cluster-test, Execute cluster roundtrip}
///
/// @RESTDESCRIPTION
///
/// Executes a cluster roundtrip from a coordinator to a DB server and
/// back. This call only works in a coordinator node in a cluster.
/// One can and should append an arbitrary path to the URL and the
/// part after */_admin/cluster-test* is used as the path of the HTTP
/// request which is sent from the coordinator to a DB node. Likewise,
/// any form data appended to the URL is forwarded in the request to the
/// DB node. This handler takes care of all request types (see below)
/// and uses the same request type in its request to the DB node.
///
/// The following HTTP headers are interpreted in a special way:
///
/// - *X-Shard-ID*: This specifies the ID of the shard to which the
/// cluster request is sent and thus tells the system to which DB server
/// to send the cluster request. Note that the mapping from the
/// shard ID to the responsible server has to be defined in the
/// agency under *Current/ShardLocation/<shardID>*. One has to give
/// this header, otherwise the system does not know where to send
/// the request.
/// - *X-Client-Transaction-ID*: the value of this header is taken
/// as the client transaction ID for the request
/// - *X-Timeout*: specifies a timeout in seconds for the cluster
/// operation. If the answer does not arrive within the specified
/// timeout, an corresponding error is returned and any subsequent
/// real answer is ignored. The default if not given is 24 hours.
/// - *X-Synchronous-Mode*: If set to *true* the test function uses
/// synchronous mode, otherwise the default asynchronous operation
/// mode is used. This is mainly for debugging purposes.
/// - *Host*: This header is ignored and not forwarded to the DB server.
/// - *User-Agent*: This header is ignored and not forwarded to the DB
/// server.
///
/// All other HTTP headers and the body of the request (if present, see
/// other HTTP methods below) are forwarded as given in the original request.
///
/// In asynchronous mode the DB server answers with an HTTP request of its
/// own, in synchronous mode it sends a HTTP response. In both cases the
/// headers and the body are used to produce the HTTP response of this
/// API call.
///
/// @RESTRETURNCODES
///
/// The return code can be anything the cluster request returns, as well as:
///
/// @RESTRETURNCODE{200}
/// is returned when everything went well, or if a timeout occurred. In the
/// latter case a body of type application/json indicating the timeout
/// is returned.
///
/// @RESTRETURNCODE{403}
/// is returned if ArangoDB is not running in cluster mode.
///
/// @RESTRETURNCODE{404}
/// is returned if ArangoDB was not compiled for cluster operation.
/// @endDocuBlock
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @startDocuBlock JSF_cluster_test_POST
/// @brief executes a cluster roundtrip for sharding
///
/// @RESTHEADER{POST /_admin/cluster-test, Execute cluster roundtrip}
///
/// @RESTBODYPARAM{body,anything,required}
///
/// @RESTDESCRIPTION
/// See GET method. The body can be any type and is simply forwarded.
/// @endDocuBlock
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @startDocuBlock JSF_cluster_test_PUT
/// @brief executes a cluster roundtrip for sharding
///
/// @RESTHEADER{PUT /_admin/cluster-test, Execute cluster roundtrip}
///
/// @RESTBODYPARAM{body,anything,required}
///
/// @RESTDESCRIPTION
/// See GET method. The body can be any type and is simply forwarded.
// @endDocuBlock
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @startDocuBlock JSF_cluster_test_DELETE
/// @brief executes a cluster roundtrip for sharding
///
/// @RESTHEADER{DELETE /_admin/cluster-test, Delete cluster roundtrip}
///
/// @RESTDESCRIPTION
/// See GET method. The body can be any type and is simply forwarded.
/// @endDocuBlock
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @startDocuBlock JSF_cluster_test_PATCH
/// @brief executes a cluster roundtrip for sharding
///
/// @RESTHEADER{PATCH /_admin/cluster-test, Update cluster roundtrip}
///
/// @RESTBODYPARAM{body,anything,required}
///
/// @RESTDESCRIPTION
/// See GET method. The body can be any type and is simply forwarded.
/// @endDocuBlock
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @startDocuBlock JSF_cluster_test_HEAD
/// @brief executes a cluster roundtrip for sharding
///
/// @RESTHEADER{HEAD /_admin/cluster-test, Execute cluster roundtrip}
///
/// @RESTDESCRIPTION
/// See GET method. The body can be any type and is simply forwarded.
/// @endDocuBlock
////////////////////////////////////////////////////////////////////////////////
actions.defineHttp({
url: "_admin/cluster-test",
prefix: true,
callback : function (req, res) {
var path;
if (req.hasOwnProperty('suffix') && req.suffix.length !== 0) {
path = "/"+req.suffix.join("/");
}
else {
path = "/_admin/version";
}
var params = "";
var shard = "";
var p;
for (p in req.parameters) {
if (req.parameters.hasOwnProperty(p)) {
if (params === "") {
params = "?";
}
else {
params += "&";
}
params += p+"="+ encodeURIComponent(String(req.parameters[p]));
}
}
if (params !== "") {
path += params;
}
var headers = {};
var transID = "";
var timeout = 24*3600.0;
var asyncMode = true;
for (p in req.headers) {
if (req.headers.hasOwnProperty(p)) {
if (p === "x-client-transaction-id") {
transID = req.headers[p];
}
else if (p === "x-timeout") {
timeout = parseFloat(req.headers[p]);
if (isNaN(timeout)) {
timeout = 24*3600.0;
}
}
else if (p === "x-synchronous-mode") {
asyncMode = false;
}
else if (p === "x-shard-id") {
shard = req.headers[p];
}
else {
headers[p] = req.headers[p];
}
}
}
var body;
if (req.requestBody === undefined || typeof req.requestBody !== "string") {
body = "";
}
else {
body = req.requestBody;
}
var r;
if (typeof SYS_CLUSTER_TEST === "undefined") {
actions.resultError(req, res, actions.HTTP_NOT_FOUND,
"Not compiled for cluster operation");
}
else {
try {
r = SYS_CLUSTER_TEST(req, res, shard, path, transID,
headers, body, timeout, asyncMode);
if (r.timeout || typeof r.errorMessage === 'string') {
res.responseCode = actions.HTTP_OK;
res.contentType = "application/json; charset=utf-8";
var s = JSON.stringify(r);
res.body = s;
}
else {
res.responseCode = actions.HTTP_OK;
res.contentType = r.headers.contentType;
res.headers = r.headers;
res.body = r.body;
}
}
catch(err) {
actions.resultError(req, res, actions.HTTP_FORBIDDEN, String(err));
}
}
}
});
////////////////////////////////////////////////////////////////////////////////
/// @brief function to parse an authorization header
////////////////////////////////////////////////////////////////////////////////
function parseAuthorization (authorization) {
var auth = require("internal").base64Decode(authorization.substr(6));
var pos = auth.indexOf(":");
if (pos === -1) {
return {username:"root", passwd:""};
}
return { username: auth.substr(0, pos),
passwd: auth.substr(pos+1) || "" };
}
////////////////////////////////////////////////////////////////////////////////
/// @startDocuBlock JSF_cluster_planner_POST
/// @brief exposes the cluster planning functionality
///
/// @RESTHEADER{POST /_admin/clusterPlanner, Produce cluster startup plan}
///
/// @RESTBODYPARAM{body,json,required}
///
/// @RESTDESCRIPTION Given a description of a cluster, this plans the details
/// of a cluster and returns a JSON description of a plan to start up this
/// cluster.
///
/// @RESTRETURNCODES
///
/// @RESTRETURNCODE{200} is returned when everything went well.
///
/// @RESTRETURNCODE{400} the posted body was not valid JSON.
/// @endDocuBlock
////////////////////////////////////////////////////////////////////////////////
actions.defineHttp({
url: "_admin/clusterPlanner",
prefix: false,
callback : function (req, res) {
if (ArangoServerState.disableDispatcherKickstarter() === true) {
actions.resultError(req, res, actions.HTTP_FORBIDDEN);
return;
}
if (req.requestType !== actions.POST) {
actions.resultError(req, res, actions.HTTP_FORBIDDEN);
return;
}
var userconfig;
try {
userconfig = JSON.parse(req.requestBody);
}
catch (err) {
actions.resultError(req, res, actions.HTTP_BAD,
"Posted body was not valid JSON.");
return;
}
var Planner = require("org/arangodb/cluster/planner").Planner;
try {
var p = new Planner(userconfig);
res.responseCode = actions.HTTP_OK;
res.contentType = "application/json; charset=utf-8";
res.body = JSON.stringify({"clusterPlan": p.getPlan(),
"config": p.config,
"error": false});
}
catch (error) {
actions.resultException(req, res, error, undefined, false);
}
}
});
////////////////////////////////////////////////////////////////////////////////
/// @startDocuBlock JSF_cluster_dispatcher_POST
/// @brief exposes the dispatcher functionality to start up, shutdown,
/// relaunch, upgrade or cleanup a cluster according to a cluster plan
/// as for example provided by the kickstarter.
///
/// @RESTHEADER{POST /_admin/clusterDispatch,execute startup commands}
///
/// @RESTQUERYPARAMETERS
///
/// @RESTBODYPARAM{body,json,required}
///
/// @RESTDESCRIPTION The body must be an object with the following properties:
///
/// - *clusterPlan*: is a cluster plan (see JSF_cluster_planner_POST),
/// - *myname*: is the ID of this dispatcher, this is used to decide
/// which commands are executed locally and which are forwarded
/// to other dispatchers
/// - *action*: can be one of the following:
///
/// - "launch": the cluster is launched for the first time, all
/// data directories and log files are cleaned and created
/// - "shutdown": the cluster is shut down, the additional property
/// *runInfo* (see below) must be bound as well
/// - "relaunch": the cluster is launched again, all data directories
/// and log files are untouched and need to be there already
/// - "cleanup": use this after a shutdown to remove all data in the
/// data directories and all log files, use with caution
/// - "isHealthy": checks whether or not the processes involved
/// in the cluster are running or not. The additional property
/// *runInfo* (see above) must be bound as well
/// - "upgrade": performs an upgrade of a cluster, to this end,
/// the agency is started, and then every server is once started
/// with the "--upgrade" option, and then normally. Finally,
/// the script "verion-check.js" is run on one of the coordinators
/// for the cluster.
///
/// - *runInfo": this is needed for the "shutdown" and "isHealthy" actions
/// only and should be the structure that "launch", "relaunch" or
/// "upgrade" returned. It contains runtime information like process
/// IDs.
///
/// This call executes the plan by either doing the work personally
/// or by delegating to other dispatchers.
///
/// @RESTRETURNCODES
///
/// @RESTRETURNCODE{200} is returned when everything went well.
///
/// @RESTRETURNCODE{400} the posted body was not valid JSON, or something
/// went wrong with the startup.
/// @endDocuBlock
////////////////////////////////////////////////////////////////////////////////
actions.defineHttp({
url: "_admin/clusterDispatch",
prefix: false,
callback : function (req, res) {
if (ArangoServerState.disableDispatcherKickstarter() === true) {
actions.resultError(req, res, actions.HTTP_FORBIDDEN);
return;
}
if (req.requestType !== actions.POST) {
actions.resultError(req, res, actions.HTTP_FORBIDDEN);
return;
}
var input;
try {
input = JSON.parse(req.requestBody);
}
catch (error) {
actions.resultError(req, res, actions.HTTP_BAD,
"Posted body was not valid JSON.");
return;
}
if (!input.hasOwnProperty("clusterPlan")) {
actions.resultError(req, res, actions.HTTP_BAD,
'Posted body needs a "clusterPlan" property.');
return;
}
if (!input.hasOwnProperty("myname")) {
actions.resultError(req, res, actions.HTTP_BAD,
'Posted body needs a "myname" property.');
return;
}
var action = input.action;
var Kickstarter, k, r;
if (action === "launch") {
Kickstarter = require("org/arangodb/cluster/kickstarter").Kickstarter;
try {
k = new Kickstarter(input.clusterPlan, input.myname);
r = k.launch();
res.responseCode = actions.HTTP_OK;
res.contentType = "application/json; charset=utf-8";
res.body = JSON.stringify(r);
}
catch (error2) {
actions.resultException(req, res, error2, undefined, false);
}
}
else if (action === "relaunch") {
Kickstarter = require("org/arangodb/cluster/kickstarter").Kickstarter;
try {
k = new Kickstarter(input.clusterPlan, input.myname);
r = k.relaunch();
res.responseCode = actions.HTTP_OK;
res.contentType = "application/json; charset=utf-8";
res.body = JSON.stringify(r);
}
catch (error3) {
actions.resultException(req, res, error3, undefined, false);
}
}
else if (action === "upgrade") {
Kickstarter = require("org/arangodb/cluster/kickstarter").Kickstarter;
try {
k = new Kickstarter(input.clusterPlan, input.myname);
r = k.upgrade(input.username, input.password);
res.responseCode = actions.HTTP_OK;
res.contentType = "application/json; charset=utf-8";
res.body = JSON.stringify(r);
}
catch (error3a) {
actions.resultException(req, res, error3a, undefined, false);
}
}
else if (action === "shutdown") {
if (!input.hasOwnProperty("runInfo")) {
actions.resultError(req, res, actions.HTTP_BAD,
'Posted body needs a "runInfo" property.');
return;
}
Kickstarter = require("org/arangodb/cluster/kickstarter").Kickstarter;
try {
k = new Kickstarter(input.clusterPlan, input.myname);
k.runInfo = input.runInfo;
r = k.shutdown();
res.responseCode = actions.HTTP_OK;
res.contentType = "application/json; charset=utf-8";
res.body = JSON.stringify(r);
}
catch (error4) {
actions.resultException(req, res, error4, undefined, false);
}
}
else if (action === "cleanup") {
Kickstarter = require("org/arangodb/cluster/kickstarter").Kickstarter;
try {
k = new Kickstarter(input.clusterPlan, input.myname);
r = k.cleanup();
res.responseCode = actions.HTTP_OK;
res.contentType = "application/json; charset=utf-8";
res.body = JSON.stringify(r);
}
catch (error5) {
actions.resultException(req, res, error5, undefined, false);
}
}
else if (action === "isHealthy") {
if (!input.hasOwnProperty("runInfo")) {
actions.resultError(req, res, actions.HTTP_BAD,
'Posted body needs a "runInfo" property.');
return;
}
Kickstarter = require("org/arangodb/cluster/kickstarter").Kickstarter;
try {
k = new Kickstarter(input.clusterPlan, input.myname);
k.runInfo = input.runInfo;
r = k.isHealthy();
res.responseCode = actions.HTTP_OK;
res.contentType = "application/json; charset=utf-8";
res.body = JSON.stringify(r);
}
catch (error6) {
actions.resultException(req, res, error6, undefined, false);
}
}
else {
actions.resultError(req, res, actions.HTTP_BAD,
'Action '+action+' not yet implemented.');
}
}
});
////////////////////////////////////////////////////////////////////////////////
/// @startDocuBlock JSF_cluster_check_port_GET
/// @brief allows to check whether a given port is usable
///
/// @RESTHEADER{GET /_admin/clusterCheckPort, Check port}
///
/// @RESTQUERYPARAMETERS
///
/// @RESTQUERYPARAM{port,integer,required}
///
/// @RESTDESCRIPTION Checks whether the requested port is usable.
///
/// @RESTRETURNCODES
///
/// @RESTRETURNCODE{200} is returned when everything went well.
///
/// @RESTRETURNCODE{400} the parameter port was not given or is no integer.
/// @endDocuBlock
////////////////////////////////////////////////////////////////////////////////
actions.defineHttp({
url: "_admin/clusterCheckPort",
prefix: false,
callback : function (req, res) {
if (ArangoServerState.disableDispatcherKickstarter() === true) {
actions.resultError(req, res, actions.HTTP_FORBIDDEN);
return;
}
if (req.requestType !== actions.GET) {
actions.resultError(req, res, actions.HTTP_FORBIDDEN);
return;
}
var port;
if (!req.parameters.hasOwnProperty("port")) {
actions.resultError(req, res, actions.HTTP_BAD,
"required parameter port was not given");
return;
}
try {
port = parseInt(req.parameters.port, 10);
if (port < 1 || port > 65535) {
throw "banana";
}
}
catch (err) {
actions.resultError(req, res, actions.HTTP_BAD,
"given port was not a proper integer");
return;
}
try {
var r = internal.testPort("tcp://0.0.0.0:" + port);
res.responseCode = actions.HTTP_OK;
res.contentType = "application/json; charset=utf-8";
res.body = JSON.stringify(r);
}
catch (err2) {
actions.resultError(req, res, actions.HTTP_BAD,
"exception in port test");
}
}
});
////////////////////////////////////////////////////////////////////////////////
/// @brief allows to query the statistics of a DBserver in the cluster
///
/// @RESTHEADER{GET /_admin/clusterStatistics, Queries statistics of DBserver}
///
/// @RESTQUERYPARAMETERS
///
/// @RESTQUERYPARAM{DBserver,string,required}
///
/// @RESTDESCRIPTION Queries the statistics of the given DBserver
///
/// @RESTRETURNCODES
///
/// @RESTRETURNCODE{200} is returned when everything went well.
///
/// @RESTRETURNCODE{400} the parameter DBserver was not given or is not the
/// ID of a DBserver
///
/// @RESTRETURNCODE{403} server is not a coordinator.
////////////////////////////////////////////////////////////////////////////////
actions.defineHttp({
url: "_admin/clusterStatistics",
prefix: false,
callback : function (req, res) {
if (req.requestType !== actions.GET) {
actions.resultError(req, res, actions.HTTP_FORBIDDEN, 0,
"only GET requests are allowed");
return;
}
if (!require("org/arangodb/cluster").isCoordinator()) {
actions.resultError(req, res, actions.HTTP_FORBIDDEN, 0,
"only allowed on coordinator");
return;
}
if (!req.parameters.hasOwnProperty("DBserver")) {
actions.resultError(req, res, actions.HTTP_BAD,
"required parameter DBserver was not given");
return;
}
var DBserver = req.parameters.DBserver;
var coord = { coordTransactionID: ArangoClusterInfo.uniqid() };
var options = { coordTransactionID: coord.coordTransactionID, timeout:10 };
var op = ArangoClusterComm.asyncRequest("GET","server:"+DBserver,"_system",
"/_admin/statistics","",{},options);
var r = ArangoClusterComm.wait(op);
res.contentType = "application/json; charset=utf-8";
if (r.status === "RECEIVED") {
res.responseCode = actions.HTTP_OK;
res.body = r.body;
}
else if (r.status === "TIMEOUT") {
res.responseCode = actions.HTTP_BAD;
res.body = JSON.stringify( {"error":true,
"errorMessage": "operation timed out"});
}
else {
res.responseCode = actions.HTTP_BAD;
var bodyobj;
try {
bodyobj = JSON.parse(r.body);
}
catch (err) {
}
res.body = JSON.stringify( {"error":true,
"errorMessage": "error from DBserver, possibly DBserver unknown",
"body": bodyobj} );
}
}
});
////////////////////////////////////////////////////////////////////////////////
/// @brief allows to query the historic statistics of a DBserver in the cluster
////////////////////////////////////////////////////////////////////////////////
actions.defineHttp({
url: "_admin/history",
prefix: false,
callback : function (req, res) {
if (req.requestType !== actions.POST) {
actions.resultError(req, res, actions.HTTP_FORBIDDEN, 0,
"only POST requests are allowed");
return;
}
var body = actions.getJsonBody(req, res);
if (body === undefined) {
return;
}
var DBserver = req.parameters.DBserver;
//build query
var figures = body.figures;
var filterString = " filter u.time > @startDate";
var bind = {
startDate: (new Date().getTime() / 1000) - 20 * 60
};
if (cluster.isCoordinator() && !req.parameters.hasOwnProperty("DBserver")) {
filterString += " filter u.clusterId == @serverId";
bind.serverId = cluster.coordinatorId();
}
var returnValue = " return u";
if (figures) {
returnValue = " return { time : u.time, server : {uptime : u.server.uptime} ";
var groups = {};
figures.forEach(function(f) {
var g = f.split(".")[0];
if (!groups[g]) {
groups[g] = [];
}
groups[g].push(f.split(".")[1] + " : u." + f);
});
Object.keys(groups).forEach(function(key) {
returnValue += ", " + key + " : {" + groups[key] +"}";
});
returnValue += "}";
}
// allow at most ((60 / 10) * 20) * 2 documents to prevent total chaos
var myQueryVal = "FOR u in _statistics " + filterString + " LIMIT 240 SORT u.time" + returnValue;
if (! req.parameters.hasOwnProperty("DBserver")) {
// query the local statistics collection
var cursor = AQL_EXECUTE(myQueryVal, bind);
res.contentType = "application/json; charset=utf-8";
if (cursor instanceof Error) {
res.responseCode = actions.HTTP_BAD;
res.body = JSON.stringify( {"error":true,
"errorMessage": "an error occured"});
}
res.responseCode = actions.HTTP_OK;
res.body = JSON.stringify({result : cursor.docs});
}
else {
// query a remote statistics collection
var coord = { coordTransactionID: ArangoClusterInfo.uniqid() };
var options = { coordTransactionID: coord.coordTransactionID, timeout:10 };
var op = ArangoClusterComm.asyncRequest("POST","server:"+DBserver,"_system",
"/_api/cursor",JSON.stringify({query: myQueryVal, bindVars: bind}),{},options);
var r = ArangoClusterComm.wait(op);
res.contentType = "application/json; charset=utf-8";
if (r.status === "RECEIVED") {
res.responseCode = actions.HTTP_OK;
res.body = r.body;
}
else if (r.status === "TIMEOUT") {
res.responseCode = actions.HTTP_BAD;
res.body = JSON.stringify( {"error":true,
"errorMessage": "operation timed out"});
}
else {
res.responseCode = actions.HTTP_BAD;
var bodyobj;
try {
bodyobj = JSON.parse(r.body);
}
catch (err) {
}
res.body = JSON.stringify( {"error":true,
"errorMessage": "error from DBserver, possibly DBserver unknown",
"body": bodyobj} );
}
}
}
});
////////////////////////////////////////////////////////////////////////////////
/// @brief bootstraps the all db servers
////////////////////////////////////////////////////////////////////////////////
actions.defineHttp({
url: "_admin/cluster/bootstrapDbServers",
prefix: false,
callback: function (req, res) {
var body = actions.getJsonBody(req, res);
try {
var result = cluster.bootstrapDbServers(body.isRelaunch);
if (result) {
actions.resultOk(req, res, actions.HTTP_OK);
}
else {
actions.resultBad(req, res);
}
}
catch(err) {
actions.resultException(req, res, err);
}
}
});
////////////////////////////////////////////////////////////////////////////////
/// @brief bootstraps one db server
////////////////////////////////////////////////////////////////////////////////
actions.defineHttp({
url: "_admin/cluster/bootstrapDbServer",
prefix: false,
callback: function (req, res) {
var body = actions.getJsonBody(req, res);
UPGRADE_ARGS = {
isCluster: true,
isDbServer: true,
isRelaunch: body.isRelaunch
};
try {
var func = internal.loadStartup("server/bootstrap/db-server.js");
var result = func && func();
if (result) {
actions.resultOk(req, res, actions.HTTP_OK);
}
else {
actions.resultBad(req, res);
}
}
catch(err) {
actions.resultException(req, res, err);
}
}
});
////////////////////////////////////////////////////////////////////////////////
/// @brief upgrade cluster database
////////////////////////////////////////////////////////////////////////////////
actions.defineHttp({
url: "_admin/cluster/upgradeClusterDatabase",
prefix: false,
callback: function (req, res) {
var body = actions.getJsonBody(req, res);
UPGRADE_ARGS = {
isCluster: true,
isCoordinator: true,
isRelaunch: body.isRelaunch || false,
upgrade: body.upgrade || false
};
try {
var result = internal.loadStartup("server/upgrade-database.js");
if (result) {
actions.resultOk(req, res, actions.HTTP_OK);
}
else {
actions.resultBad(req, res);
}
}
catch(err) {
actions.resultException(req, res, err);
}
}
});
////////////////////////////////////////////////////////////////////////////////
/// @brief bootstraps the coordinator
////////////////////////////////////////////////////////////////////////////////
actions.defineHttp({
url: "_admin/cluster/bootstrapCoordinator",
allowUseDatabase: true,
prefix: false,
callback: function (req, res) {
try {
var func = internal.loadStartup("server/bootstrap/coordinator.js");
var result = func && func();
if (result) {
actions.resultOk(req, res, actions.HTTP_OK);
}
else {
actions.resultBad(req, res);
}
}
catch(err) {
actions.resultException(req, res, err);
}
}
});
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// Local Variables:
// mode: outline-minor
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}"
// End: