/*jslint indent: 2, nomen: true, maxlen: 100, white: true, plusplus: true, unparam: true */ /*global require, applicationContext*/ //////////////////////////////////////////////////////////////////////////////// /// @brief A Foxx.Controller to show all Foxx Applications /// /// @file /// /// DISCLAIMER /// /// Copyright 2010-2013 triagens 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 triAGENS GmbH, Cologne, Germany /// /// @author Michael Hackstein /// @author Copyright 2011-2013, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// (function() { "use strict"; // Initialise a new FoxxController called controller under the urlPrefix: "cluster". var FoxxController = require("org/arangodb/foxx").Controller, controller = new FoxxController(applicationContext), cluster = require("org/arangodb/cluster"), _ = require("underscore"); /** Plan and start a new cluster * * This will plan a new cluster with the information * given in the body */ controller.get("/amICoordinator", function(req, res) { res.json(cluster.isCoordinator()); }); controller.get("/amIDispatcher", function(req, res) { res.json(!cluster.dispatcherDisabled()); }); if (!cluster.dispatcherDisabled()) { var Plans = require("./repositories/plans.js"), plans = new Plans.Repository( require("internal").db._collection( "_cluster_kickstarter_plans" )), getStarter = function() { var config = plans.loadConfig(), k; if (!config) { return; } k = new cluster.Kickstarter(config.plan); k.runInfo = config.runInfo; return k; }, parseUser = function(header) { if (header && header.authorization) { var auth = require("internal").base64Decode( header.authorization.substr(6) ); return { username: auth.substr(0, auth.indexOf(":")), passwd: auth.substr(auth.indexOf(":")+1) || "" }; } return { username: "root", passwd: "" } }, startUp = function(req, res) { cleanUp(); var config = {}, input = req.body(), result = {}, starter, i, tmp, planner, auth, uname, pwd; auth = parseUser(req.headers); uname = auth.username; pwd = auth.passwd; if (input.type === "testSetup") { config.dispatchers = { "d1": { "username": uname, "passwd": pwd, "endpoint": "tcp://" + input.dispatchers } }; config.numberOfDBservers = input.numberDBServers; config.numberOfCoordinators = input.numberCoordinators; } else { i = 0; config.dispatchers = {}; config.numberOfDBservers = 0; config.numberOfCoordinators = 0; _.each(input.dispatchers, function(d) { i++; var inf = {}; inf.username = d.username; inf.passwd = d.passwd; inf.endpoint = "tcp://" + d.host; if (d.isCoordinator) { config.numberOfCoordinators++; } else { inf.allowCoordinators = false; } if (d.isDBServer) { config.numberOfDBservers++; } else { inf.allowDBservers = false; } config.dispatchers["d" + i] = inf; }); config.useSSLonDBservers = input.useSSLonDBservers; config.useSSLonCoordinators = input.useSSLonCoordinators; } result.config = config; planner = new cluster.Planner(config); result.plan = planner.getPlan(); starter = new cluster.Kickstarter(planner.getPlan()); tmp = starter.launch(); result.runInfo = tmp.runInfo; result.user = { name: "root", passwd: "" }; plans.storeConfig(result); res.json(result); }, cleanUp = function() { var k = getStarter(); if (k) { k.cleanup(); } }; // only make these functions available in dispatcher mode! controller.post("/plan", startUp); controller.put("/plan", startUp); controller.put("/plan/credentials", function(req, res) { var body = req.body(), u = body.user, p = body.passwd; plans.saveCredentials(u, p); }); controller.get("/plan", function(req, res) { res.json(plans.loadConfig()); }); controller.del("/plan", function(req, res) { plans.clear(); res.json("ok"); }); controller.get("/healthcheck", function(req, res) { var out = getStarter().isHealthy(); var stf = JSON.stringify(out); if (out.error) { require("console").log("Cluster HealthCheck Error:", out.error); require("console").log("Cluster HealthCheck Result:", stf); res.json(false); return; } res.json(true); }); controller.get("/shutdown", function(req, res) { var k = getStarter(); var shutdownInfo = k.shutdown(); if (shutdownInfo.error) { require("console").log("%s", JSON.stringify(shutdownInfo.results)); } }); controller.get("/cleanup", function(req, res) { var k = getStarter(); var shutdownInfo = k.shutdown(); cleanUp(); if (shutdownInfo.error) { require("console").log("%s", JSON.stringify(shutdownInfo.results)); return; } }); controller.get("/relaunch", function(req, res) { var k = getStarter(); var r = k.relaunch(); if (r.error) { res.json("Unable to relaunch cluster"); res.status(409); return; } var result = { plan: r.plan, runInfo: r.runInfo }; result = plans.updateConfig(result); res.json(result); }); } if (cluster.isCluster()) { // only make these functions available in cluster mode! var Communication = require("org/arangodb/cluster/agency-communication"), comm = new Communication.Communication(), beats = comm.sync.Heartbeats(), diff = comm.diff.current, servers = comm.current.DBServers(), dbs = comm.current.Databases(), coords = comm.current.Coordinators(); /** Get the type of the cluster * * Returns a string containing the cluster type * Possible anwers: * - testSetup * - symmetricalSetup * - asymmetricalSetup * */ controller.get("/ClusterType", function(req, res) { // Not yet implemented res.json({ type: "symmetricSetup" }); }); /** Get all DBServers * * Get a list of all running and expected DBServers * within the cluster */ controller.get("/DBServers", function(req, res) { var resList = [], list = servers.getList(), diffList = diff.DBServers(), didBeat = beats.didBeat(), serving = beats.getServing(); _.each(list, function(v, k) { v.name = k; resList.push(v); if (!_.contains(didBeat, k)) { v.status = "critical"; return; } if (v.role === "primary" && !_.contains(serving, k)) { v.status = "warning"; return; } v.status = "ok"; }); _.each(diffList.missing, function(v) { v.status = "missing"; resList.push(v); }); res.json(resList); }); controller.get("/Coordinators", function(req, res) { var resList = [], list = coords.getList(), diffList = diff.Coordinators(), didBeat = beats.didBeat(); _.each(list, function(v, k) { v.name = k; resList.push(v); if (!_.contains(didBeat, k)) { v.status = "critical"; return; } v.status = "ok"; }); _.each(diffList.missing, function(v) { v.status = "missing"; resList.push(v); }); res.json(resList); }); controller.get("/Databases", function(req, res) { var list = dbs.getList(); res.json(_.map(list, function(d) { return {name: d}; })); }); controller.get("/:dbname/Collections", function(req, res) { var dbname = req.params("dbname"), selected = dbs.select(dbname); try { res.json(_.map(selected.getCollections(), function(c) { return {name: c}; }) ); } catch(e) { res.json([]); } }); controller.get("/:dbname/:colname/Shards", function(req, res) { var dbname = req.params("dbname"), colname = req.params("colname"), selected = dbs.select(dbname).collection(colname); res.json(selected.getShardsByServers()); }); controller.get("/:dbname/:colname/Shards/:servername", function(req, res) { var dbname = req.params("dbname"), colname = req.params("colname"), servername = req.params("servername"), selected = dbs.select(dbname).collection(colname); res.json(_.map(selected.getShardsForServer(servername), function(c) { return {id: c}; }) ); }); } // end isCluster() }());