You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by an...@apache.org on 2015/07/23 10:41:21 UTC

[50/50] [abbrv] incubator-ignite git commit: #ignite-1121 Merged with ignite-843

#ignite-1121 Merged with ignite-843


Project: http://git-wip-us.apache.org/repos/asf/incubator-ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ignite/commit/a45a700c
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ignite/tree/a45a700c
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ignite/diff/a45a700c

Branch: refs/heads/ignite-1121
Commit: a45a700c63b70c075df494fd080e668803387c32
Parents: 71b313c 14fa6f6
Author: Andrey <an...@gridgain.com>
Authored: Thu Jul 23 15:41:20 2015 +0700
Committer: Andrey <an...@gridgain.com>
Committed: Thu Jul 23 15:41:20 2015 +0700

----------------------------------------------------------------------
 modules/web-control-center/nodejs/.gitignore    |    4 -
 modules/web-control-center/nodejs/DEVNOTES.txt  |   21 -
 .../nodejs/agents/agent-manager.js              |  283 -----
 .../nodejs/agents/agent-server.js               |   90 --
 modules/web-control-center/nodejs/app.js        |  156 ---
 modules/web-control-center/nodejs/bin/www       |  110 --
 .../nodejs/config/default.json                  |   17 -
 .../nodejs/controllers/admin-controller.js      |   68 -
 .../nodejs/controllers/caches-controller.js     |  333 -----
 .../nodejs/controllers/clusters-controller.js   |  309 -----
 .../nodejs/controllers/common-module.js         |  422 -------
 .../nodejs/controllers/metadata-controller.js   |  678 ----------
 .../nodejs/controllers/models/caches.json       |  905 -------------
 .../nodejs/controllers/models/clusters.json     |  896 -------------
 .../nodejs/controllers/models/metadata.json     |  218 ----
 .../nodejs/controllers/models/sql.json          |    5 -
 .../nodejs/controllers/models/summary.json      |  150 ---
 .../nodejs/controllers/profile-controller.js    |   51 -
 .../nodejs/controllers/sql-controller.js        |   60 -
 .../nodejs/controllers/summary-controller.js    |  171 ---
 modules/web-control-center/nodejs/db.js         |  358 ------
 .../nodejs/helpers/configuration-loader.js      |   22 -
 .../nodejs/helpers/data-structures.js           |   84 --
 modules/web-control-center/nodejs/keys/test.crt |   13 -
 modules/web-control-center/nodejs/keys/test.key |   18 -
 modules/web-control-center/nodejs/package.json  |   51 -
 .../nodejs/public/favicon.ico                   |  Bin 1150 -> 0 bytes
 .../nodejs/public/images/docker.png             |  Bin 994 -> 0 bytes
 .../nodejs/public/images/java.png               |  Bin 170 -> 0 bytes
 .../nodejs/public/images/logo.png               |  Bin 8148 -> 0 bytes
 .../nodejs/public/images/xml.png                |  Bin 232 -> 0 bytes
 .../nodejs/public/stylesheets/style.less        | 1125 -----------------
 .../web-control-center/nodejs/routes/admin.js   |   79 --
 .../web-control-center/nodejs/routes/agent.js   |   37 -
 .../web-control-center/nodejs/routes/caches.js  |   95 --
 .../nodejs/routes/clusters.js                   |  104 --
 .../nodejs/routes/generator/common.js           |  299 -----
 .../nodejs/routes/generator/docker.js           |   58 -
 .../nodejs/routes/generator/java.js             |  626 ---------
 .../nodejs/routes/generator/xml.js              |  580 ---------
 .../nodejs/routes/metadata.js                   |   95 --
 .../web-control-center/nodejs/routes/profile.js |   97 --
 .../web-control-center/nodejs/routes/public.js  |  123 --
 modules/web-control-center/nodejs/routes/sql.js |   24 -
 .../web-control-center/nodejs/routes/summary.js |  108 --
 .../nodejs/tests/routes/agent.js                |   94 --
 .../nodejs/views/configuration/caches.jade      |   70 -
 .../nodejs/views/configuration/clusters.jade    |   73 --
 .../nodejs/views/configuration/metadata.jade    |  120 --
 .../nodejs/views/configuration/sidebar.jade     |   39 -
 .../nodejs/views/configuration/summary.jade     |  115 --
 .../web-control-center/nodejs/views/error.jade  |   22 -
 .../nodejs/views/includes/controls.jade         |  316 -----
 .../nodejs/views/includes/footer.jade           |   22 -
 .../nodejs/views/includes/header.jade           |   39 -
 .../web-control-center/nodejs/views/index.jade  |   30 -
 .../web-control-center/nodejs/views/login.jade  |   55 -
 .../nodejs/views/settings/admin.jade            |   58 -
 .../nodejs/views/settings/profile.jade          |   58 -
 .../nodejs/views/sql/sql.jade                   |   70 -
 .../nodejs/views/templates/confirm.jade         |   27 -
 .../nodejs/views/templates/layout.jade          |   61 -
 .../nodejs/views/templates/saveAs.jade          |   31 -
 .../nodejs/views/templates/select.jade          |   26 -
 .../web-control-center/src/main/js/.gitignore   |    4 +
 .../web-control-center/src/main/js/DEVNOTES.txt |   21 +
 .../src/main/js/agents/agent-manager.js         |  283 +++++
 .../src/main/js/agents/agent-server.js          |   90 ++
 modules/web-control-center/src/main/js/app.js   |  156 +++
 modules/web-control-center/src/main/js/bin/www  |  110 ++
 .../src/main/js/config/default.json             |   17 +
 .../src/main/js/controllers/admin-controller.js |   68 +
 .../js/controllers/cache-viewer-controller.js   |   77 ++
 .../main/js/controllers/caches-controller.js    |  333 +++++
 .../main/js/controllers/clusters-controller.js  |  309 +++++
 .../src/main/js/controllers/common-module.js    |  422 +++++++
 .../main/js/controllers/metadata-controller.js  |  680 ++++++++++
 .../src/main/js/controllers/models/caches.json  |  918 ++++++++++++++
 .../main/js/controllers/models/clusters.json    |  907 +++++++++++++
 .../main/js/controllers/models/metadata.json    |  230 ++++
 .../src/main/js/controllers/models/sql.json     |    5 +
 .../src/main/js/controllers/models/summary.json |  163 +++
 .../main/js/controllers/profile-controller.js   |   51 +
 .../src/main/js/controllers/sql-controller.js   |   60 +
 .../main/js/controllers/summary-controller.js   |  170 +++
 modules/web-control-center/src/main/js/db.js    |  358 ++++++
 .../src/main/js/helpers/configuration-loader.js |   22 +
 .../src/main/js/helpers/data-structures.js      |   84 ++
 .../src/main/js/keys/test.crt                   |   13 +
 .../src/main/js/keys/test.key                   |   18 +
 .../web-control-center/src/main/js/package.json |   51 +
 .../src/main/js/public/favicon.ico              |  Bin 0 -> 1150 bytes
 .../src/main/js/public/images/docker.png        |  Bin 0 -> 994 bytes
 .../src/main/js/public/images/java.png          |  Bin 0 -> 170 bytes
 .../src/main/js/public/images/logo.png          |  Bin 0 -> 8148 bytes
 .../src/main/js/public/images/xml.png           |  Bin 0 -> 232 bytes
 .../src/main/js/public/stylesheets/style.less   | 1193 ++++++++++++++++++
 .../src/main/js/routes/admin.js                 |   79 ++
 .../src/main/js/routes/agent.js                 |   37 +
 .../src/main/js/routes/caches.js                |   95 ++
 .../src/main/js/routes/clusters.js              |  104 ++
 .../src/main/js/routes/generator/common.js      |  299 +++++
 .../src/main/js/routes/generator/docker.js      |   58 +
 .../src/main/js/routes/generator/java.js        |  626 +++++++++
 .../src/main/js/routes/generator/xml.js         |  580 +++++++++
 .../src/main/js/routes/metadata.js              |   95 ++
 .../src/main/js/routes/profile.js               |   97 ++
 .../src/main/js/routes/public.js                |  123 ++
 .../src/main/js/routes/sql.js                   |   24 +
 .../src/main/js/routes/summary.js               |  108 ++
 .../src/main/js/views/configuration/caches.jade |   74 ++
 .../main/js/views/configuration/clusters.jade   |   77 ++
 .../main/js/views/configuration/metadata.jade   |  121 ++
 .../main/js/views/configuration/sidebar.jade    |   39 +
 .../main/js/views/configuration/summary.jade    |  115 ++
 .../src/main/js/views/error.jade                |   22 +
 .../src/main/js/views/includes/controls.jade    |  336 +++++
 .../src/main/js/views/includes/footer.jade      |   22 +
 .../src/main/js/views/includes/header.jade      |   39 +
 .../src/main/js/views/index.jade                |   30 +
 .../src/main/js/views/login.jade                |   55 +
 .../src/main/js/views/settings/admin.jade       |   58 +
 .../src/main/js/views/settings/profile.jade     |   58 +
 .../src/main/js/views/sql.jade                  |   70 +
 .../src/main/js/views/templates/confirm.jade    |   27 +
 .../src/main/js/views/templates/copy.jade       |   31 +
 .../src/main/js/views/templates/layout.jade     |   61 +
 .../src/main/js/views/templates/select.jade     |   26 +
 .../src/main/js/views/templates/tab.jade        |    3 +
 .../src/test/js/routes/agent.js                 |   94 ++
 130 files changed, 10496 insertions(+), 10269 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/a45a700c/modules/web-control-center/src/main/js/agents/agent-manager.js
----------------------------------------------------------------------
diff --cc modules/web-control-center/src/main/js/agents/agent-manager.js
index 0000000,0000000..252b984
new file mode 100644
--- /dev/null
+++ b/modules/web-control-center/src/main/js/agents/agent-manager.js
@@@ -1,0 -1,0 +1,283 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements.  See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You 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.
++ */
++
++var WebSocketServer = require('ws').Server;
++
++var ignite = require('apache-ignite');
++
++var db = require('../db');
++
++var AgentServer = require('./agent-server').AgentServer;
++
++/**
++ * @constructor
++ */
++function AgentManager(srv) {
++    this._clients = {};
++
++    this._server = srv;
++
++    this._wss = new WebSocketServer({ server: this._server });
++
++    var self = this;
++
++    this._wss.on('connection', function(ws) {
++        var client = new Client(ws, self);
++    });
++}
++
++/**
++ * @param userId
++ * @param {Client} client
++ */
++AgentManager.prototype._removeClient = function(userId, client) {
++    var connections = this._clients[userId];
++
++    if (connections) {
++        removeFromArray(connections, client);
++
++        if (connections.length == 0)
++            delete this._clients[userId];
++    }
++};
++
++/**
++ * @param userId
++ * @param {Client} client
++ */
++AgentManager.prototype._addClient = function(userId, client) {
++    var existingConnections = this._clients[userId];
++
++    if (!existingConnections) {
++        existingConnections = [];
++
++        this._clients[userId] = existingConnections;
++    }
++
++    existingConnections.push(client);
++};
++
++/**
++ * @param userId
++ * @return {Client}
++ */
++AgentManager.prototype.findClient = function(userId) {
++    var clientsList = this._clients[userId];
++
++    if (!clientsList)
++        return null;
++
++    return clientsList[0];
++};
++
++/**
++ * For tests only!!!
++ * @return {Client}
++ */
++AgentManager.prototype.getOneClient = function() {
++    for (var userId in this._clients) {
++        if (this._clients.hasOwnProperty(userId)) {
++            var m = this._clients[userId];
++
++            if (m.length > 0)
++                return m[0];
++        }
++    }
++
++    return null;
++};
++
++
++/**
++ * @constructor
++ * @param {AgentManager} manager
++ * @param {WebSocket} ws
++ */
++function Client(ws, manager) {
++    var self = this;
++
++    this._manager = manager;
++    this._ws = ws;
++
++    ws.on('close', function() {
++        if (self.user) {
++            self._manager._removeClient(self.user._id, self);
++        }
++    });
++
++    ws.on('message', function (msg) {
++        self._handleMessage(JSON.parse(msg))
++    });
++
++    this._restCounter = 0;
++
++    this._cbMap = {};
++}
++
++/**
++ * @param {String|Object} msg
++ * @param {Function} cb
++ */
++Client.prototype.sendMessage = function(msg, cb) {
++    if (typeof msg == 'object') {
++        msg = JSON.stringify(msg);
++    }
++
++    this._ws.send(msg, cb);
++};
++
++/**
++ * @param {String} path
++ * @param {Object} params
++ * @param {Function} cb
++ * @param {String} method
++ * @param {String} body
++ * @param {Object} headers
++ */
++Client.prototype.invokeRest = function(path, params, cb, method, body, headers) {
++    var self = this;
++
++    if (typeof(params) != 'object')
++        throw "'params' argument must be an object";
++
++    if (typeof(cb) != 'function')
++        throw "callback must be a function";
++
++    if (body && typeof(body) != 'string')
++        throw "body must be a string";
++
++    if (headers && typeof(headers) != 'object')
++        throw "headers must be an object";
++
++    if (!method)
++        method = 'GET';
++    else
++        method = method.toUpperCase();
++
++    if (method != 'GET' && method != 'POST')
++        throw "Unknown HTTP method: " + method;
++
++    var reqId = this._restCounter++;
++
++    this._cbMap[reqId] = cb;
++
++    this.sendMessage({
++        id: reqId,
++        type: 'RestRequest',
++        method: method,
++        params: params,
++        path: path,
++        body: body,
++        headers: headers
++    }, function(err) {
++        if (err) {
++            delete self._cbMap[reqId];
++
++            cb(err)
++        }
++    })
++};
++
++/**
++ * @param {Object} msg
++ */
++Client.prototype._handleMessage = function(msg) {
++    var self = this;
++
++    switch (msg.type) {
++        case 'AuthMessage':
++            var account = db.Account.findByUsername(msg.login, function(err, account) {
++                if (err) {
++                    ws.send("{type: 'AuthResult', success: false}");
++                }
++                else {
++                    account.authenticate(msg.password, function(err, user, res) {
++                        if (!user) {
++                            self._ws.send(JSON.stringify({type: 'AuthResult', success: false, message: res.message}));
++                        }
++                        else {
++                            self._ws.send("{type: 'AuthResult', success: true}");
++
++                            self._user = account;
++
++                            self._manager._addClient(account._id, self);
++
++                            self._ignite = new ignite.Ignite(new AgentServer(self));
++                        }
++                    });
++                }
++            });
++
++            break;
++
++        case 'RestResult':
++            var cb = this._cbMap[msg.requestId];
++
++            if (!cb)
++                break;
++
++            delete this._cbMap[msg.requestId];
++
++            if (!msg.executed) {
++                cb(msg.message)
++            }
++            else {
++                cb(null, msg.code, msg.message)
++            }
++
++            break;
++
++        default:
++            this._ws.close()
++    }
++};
++
++/**
++ * @return {Ignite}
++ */
++Client.prototype.ignite = function() {
++    return this._ignite;
++};
++
++function removeFromArray(arr, val) {
++    var idx;
++
++    while ((idx = arr.indexOf(val)) !== -1) {
++        arr.splice(idx, 1);
++    }
++}
++
++exports.AgentManager = AgentManager;
++
++/**
++ * @type {AgentManager}
++ */
++var manager = null;
++
++exports.createManager = function(srv) {
++    if (manager)
++        throw "Agent manager already cleared!";
++
++    manager = new AgentManager(srv);
++};
++
++/**
++ * @return {AgentManager}
++ */
++exports.getAgentManager = function() {
++    return manager;
++};

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/a45a700c/modules/web-control-center/src/main/js/agents/agent-server.js
----------------------------------------------------------------------
diff --cc modules/web-control-center/src/main/js/agents/agent-server.js
index 0000000,0000000..31dee5a
new file mode 100644
--- /dev/null
+++ b/modules/web-control-center/src/main/js/agents/agent-server.js
@@@ -1,0 -1,0 +1,90 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements.  See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You 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.
++ */
++
++/**
++ * Creates an instance of server for Ignite
++ *
++ * @constructor
++ * @this {AgentServer}
++ * @param {Client} client connected client
++ */
++function AgentServer(client) {
++    this._client = client;
++}
++
++/**
++ * Run http request
++ *
++ * @this {AgentServer}
++ * @param {Command} cmd Command
++ * @param {callback} callback on finish
++ */
++AgentServer.prototype.runCommand = function(cmd, callback) {
++    var params = {cmd: cmd.name()};
++
++    for (var p of cmd._params) {
++        params[p.key] = p.value;
++    }
++
++    var body = undefined;
++
++    var headers = undefined;
++
++    if (cmd._isPost()) {
++        body = cmd.postData();
++
++        headers = {'Content-Length': body.length, 'JSONObject': 'application/json'};
++    }
++
++    this._client.invokeRest("ignite", params, function(error, code, message) {
++        if (error) {
++            callback(error);
++            return
++        }
++
++        if (code !== 200) {
++            if (code === 401) {
++                callback.call(null, "Authentication failed. Status code 401.");
++            }
++            else {
++                callback.call(null, "Request failed. Status code " + code);
++            }
++
++            return;
++        }
++
++        var igniteResponse;
++
++        try {
++            igniteResponse = JSON.parse(message);
++        }
++        catch (e) {
++            callback.call(null, e, null);
++
++            return;
++        }
++
++        if (igniteResponse.successStatus) {
++            callback.call(null, igniteResponse.error, null)
++        }
++        else {
++            callback.call(null, null, igniteResponse.response);
++        }
++    }, cmd._method(), body, headers);
++};
++
++exports.AgentServer = AgentServer;

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/a45a700c/modules/web-control-center/src/main/js/app.js
----------------------------------------------------------------------
diff --cc modules/web-control-center/src/main/js/app.js
index 0000000,8c347db..8cd8494
mode 000000,100644..100644
--- a/modules/web-control-center/src/main/js/app.js
+++ b/modules/web-control-center/src/main/js/app.js
@@@ -1,0 -1,154 +1,156 @@@
+ /*
+  * Licensed to the Apache Software Foundation (ASF) under one or more
+  * contributor license agreements.  See the NOTICE file distributed with
+  * this work for additional information regarding copyright ownership.
+  * The ASF licenses this file to You 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.
+  */
+ 
+ var flash = require('connect-flash');
+ var express = require('express');
+ var path = require('path');
+ var favicon = require('serve-favicon');
+ var logger = require('morgan');
+ var cookieParser = require('cookie-parser');
+ var bodyParser = require('body-parser');
+ var session = require('express-session');
+ var mongoStore = require('connect-mongo')(session);
+ 
+ var publicRoutes = require('./routes/public');
+ var clustersRouter = require('./routes/clusters');
+ var cachesRouter = require('./routes/caches');
+ var metadataRouter = require('./routes/metadata');
+ var summary = require('./routes/summary');
+ var adminRouter = require('./routes/admin');
+ var profileRouter = require('./routes/profile');
+ var sqlRouter = require('./routes/sql');
++var agentRouter = require('./routes/agent');
+ 
+ var passport = require('passport');
+ 
+ var db = require('./db');
+ 
+ var app = express();
+ 
+ // Views engine setup.
+ app.set('views', path.join(__dirname, 'views'));
+ app.set('view engine', 'jade');
+ 
+ // Site favicon.
+ app.use(favicon(__dirname + '/public/favicon.ico'));
+ 
+ app.use(logger('dev'));
+ 
+ app.use(bodyParser.json());
+ app.use(bodyParser.urlencoded({extended: false}));
+ 
+ app.use(require('less-middleware')(path.join(__dirname, 'public'), {
+     render: {
+         compress: false
+     }
+ }));
+ 
+ app.use(express.static(path.join(__dirname, 'public')));
+ app.use(express.static(path.join(__dirname, 'controllers')));
+ app.use(express.static(path.join(__dirname, 'helpers')));
+ 
+ app.use(cookieParser('keyboard cat'));
+ 
+ app.use(session({
+     secret: 'keyboard cat',
+     resave: false,
+     saveUninitialized: true,
+     store: new mongoStore({
+         mongooseConnection: db.mongoose.connection
+     })
+ }));
+ 
+ app.use(flash());
+ 
+ app.use(passport.initialize());
+ app.use(passport.session());
+ 
+ passport.serializeUser(db.Account.serializeUser());
+ passport.deserializeUser(db.Account.deserializeUser());
+ 
+ passport.use(db.Account.createStrategy());
+ 
+ var mustAuthenticated = function (req, res, next) {
+     req.isAuthenticated() ? next() : res.redirect('/');
+ };
+ 
+ var adminOnly = function(req, res, next) {
+     req.isAuthenticated() && req.user.admin ? next() : res.sendStatus(403);
+ };
+ 
+ app.all('/configuration/*', mustAuthenticated);
+ 
+ app.all('*', function(req, res, next) {
+     var becomeUsed = req.session.viewedUser && req.user.admin;
+ 
+     res.locals.user = becomeUsed ? req.session.viewedUser : req.user;
+     res.locals.becomeUsed = becomeUsed;
+ 
+     req.currentUserId = function() {
+         if (!req.user)
+             return null;
+ 
+         if (req.session.viewedUser && req.user.admin)
+             return req.session.viewedUser._id;
+ 
+         return req.user._id;
+     };
+ 
+     next();
+ });
+ 
+ app.use('/', publicRoutes);
+ app.use('/admin', mustAuthenticated, adminOnly, adminRouter);
+ app.use('/profile', mustAuthenticated, profileRouter);
+ 
+ app.use('/configuration/clusters', clustersRouter);
+ app.use('/configuration/caches', cachesRouter);
+ app.use('/configuration/metadata', metadataRouter);
+ app.use('/configuration/summary', summary);
+ app.use('/sql', sqlRouter);
++app.use('/agent', agentRouter);
+ 
+ // Catch 404 and forward to error handler.
+ app.use(function (req, res, next) {
+     var err = new Error('Not Found: ' + req.originalUrl);
+     err.status = 404;
+     next(err);
+ });
+ 
+ // Error handlers.
+ 
+ // Development error handler: will print stacktrace.
+ if (app.get('env') === 'development') {
+     app.use(function (err, req, res) {
+         res.status(err.status || 500);
+         res.render('error', {
+             message: err.message,
+             error: err
+         });
+     });
+ }
+ 
+ // Production error handler: no stacktraces leaked to user.
+ app.use(function (err, req, res) {
+     res.status(err.status || 500);
+     res.render('error', {
+         message: err.message,
+         error: {}
+     });
+ });
+ 
+ module.exports = app;

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/a45a700c/modules/web-control-center/src/main/js/bin/www
----------------------------------------------------------------------
diff --cc modules/web-control-center/src/main/js/bin/www
index 0000000,4cf0583..cbc637a
mode 000000,100644..100644
--- a/modules/web-control-center/src/main/js/bin/www
+++ b/modules/web-control-center/src/main/js/bin/www
@@@ -1,0 -1,85 +1,110 @@@
+ #!/usr/bin/env node
+ 
+ /**
+  * Module dependencies.
+  */
 -var app = require('../app');
++var http = require('http');
++var https = require('https');
+ var config = require('../helpers/configuration-loader.js');
++var app = require('../app');
++var agentManager = require('../agents/agent-manager');
++
++var fs = require('fs');
++
+ var debug = require('debug')('ignite-web-control-center:server');
 -var http = require('http');
+ 
+ /**
+  * Get port from environment and store in Express.
+  */
+ var port = normalizePort(process.env.PORT || config.get('express:port'));
+ app.set('port', port);
+ 
+ /**
+  * Create HTTP server.
+  */
+ var server = http.createServer(app);
+ 
+ /**
+  * Listen on provided port, on all network interfaces.
+  */
+ server.listen(port);
+ server.on('error', onError);
+ server.on('listening', onListening);
+ 
+ /**
++ * Start agent server.
++ */
++var agentServer;
++
++if (config.get('monitor:server:ssl')) {
++    agentServer = https.createServer({
++    key: fs.readFileSync(config.get('monitor:server:key')),
++    cert: fs.readFileSync(config.get('monitor:server:cert')),
++    passphrase: config.get('monitor:server:keyPassphrase')
++  });
++}
++else {
++  agentServer = http.createServer();
++}
++
++agentServer.listen(config.get('monitor:server:port'));
++
++agentManager.createManager(agentServer);
++
++/**
+  * Normalize a port into a number, string, or false.
+  */
+ function normalizePort(val) {
+   var port = parseInt(val, 10);
+ 
+   if (isNaN(port)) {
+     // named pipe
+     return val;
+   }
+ 
+   if (port >= 0) {
+     // port number
+     return port;
+   }
+ 
+   return false;
+ }
+ 
+ /**
+  * Event listener for HTTP server "error" event.
+  */
+ function onError(error) {
+   if (error.syscall !== 'listen') {
+     throw error;
+   }
+ 
+   var bind = typeof port === 'string'
+     ? 'Pipe ' + port
+     : 'Port ' + port;
+ 
+   // handle specific listen errors with friendly messages
+   switch (error.code) {
+     case 'EACCES':
+       console.error(bind + ' requires elevated privileges');
+       process.exit(1);
+       break;
+     case 'EADDRINUSE':
+       console.error(bind + ' is already in use');
+       process.exit(1);
+       break;
+     default:
+       throw error;
+   }
+ }
+ 
+ /**
+  * Event listener for HTTP server "listening" event.
+  */
+ function onListening() {
+   var addr = server.address();
+   var bind = typeof addr === 'string'
+     ? 'pipe ' + addr
+     : 'port ' + addr.port;
+ 
+   debug('Listening on ' + bind);
+ }

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/a45a700c/modules/web-control-center/src/main/js/config/default.json
----------------------------------------------------------------------
diff --cc modules/web-control-center/src/main/js/config/default.json
index 0000000,72dbd4e..f7f7a02
mode 000000,100644..100644
--- a/modules/web-control-center/src/main/js/config/default.json
+++ b/modules/web-control-center/src/main/js/config/default.json
@@@ -1,0 -1,8 +1,17 @@@
+ {
+     "express": {
+         "port": 3000
+     },
+     "mongoDB": {
+         "url": "mongodb://localhost/web-control-center"
++    },
++    "monitor": {
++        "server": {
++            "port": 3001,
++            "ssl": true,
++            "key": "keys/test.key",
++            "cert": "keys/test.crt",
++            "keyPassphrase": "password"
++        }
+     }
 -}
++}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/a45a700c/modules/web-control-center/src/main/js/controllers/models/sql.json
----------------------------------------------------------------------
diff --cc modules/web-control-center/src/main/js/controllers/models/sql.json
index 0000000,0000000..b00e5dd
new file mode 100644
--- /dev/null
+++ b/modules/web-control-center/src/main/js/controllers/models/sql.json
@@@ -1,0 -1,0 +1,5 @@@
++{
++  "screenTip": [
++    "Execute SQL queries."
++  ]
++}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/a45a700c/modules/web-control-center/src/main/js/controllers/sql-controller.js
----------------------------------------------------------------------
diff --cc modules/web-control-center/src/main/js/controllers/sql-controller.js
index 0000000,0000000..2562117
new file mode 100644
--- /dev/null
+++ b/modules/web-control-center/src/main/js/controllers/sql-controller.js
@@@ -1,0 -1,0 +1,60 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements.  See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You 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.
++ */
++
++var demoResults = [
++    {
++        id: 256,
++        firstName: 'Ivan',
++        lastName: 'Ivanov'
++    },
++    {
++        id: 384,
++        firstName: 'Sergey',
++        lastName: 'Petrov'
++    },
++    {
++        id: 923,
++        firstName: 'Andrey',
++        lastName: 'Sidorov'
++    }
++];
++
++var demoCaches = [{_id: '1', name: 'Users', mode: 'LOCAL'}, {_id: '2', name: 'Organizations', mode: 'REPLICATED'}, {_id: '3', name: 'Cities', mode: 'PARTITIONED'}];
++
++controlCenterModule.controller('sqlController', ['$scope', '$http', '$common', function ($scope, $http, $common) {
++    $scope.joinTip = $common.joinTip;
++
++    $scope.modes = [
++        {value: 'PARTITIONED', label: 'PARTITIONED'},
++        {value: 'REPLICATED', label: 'REPLICATED'},
++        {value: 'LOCAL', label: 'LOCAL'}
++    ];
++
++    $http.get('/models/sql.json')
++        .success(function (data) {
++            $scope.screenTip = data.screenTip;
++        })
++        .error(function (errMsg) {
++            $common.showError(errMsg);
++        });
++
++    $scope.query = "select u.id, u.firstName, u.lastName from User u where u.name like 'aaaa';";
++
++    $scope.results = demoResults;
++
++    $scope.caches = demoCaches;
++}]);

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/a45a700c/modules/web-control-center/src/main/js/controllers/summary-controller.js
----------------------------------------------------------------------
diff --cc modules/web-control-center/src/main/js/controllers/summary-controller.js
index 0000000,1291683..531dc83
mode 000000,100644..100644
--- a/modules/web-control-center/src/main/js/controllers/summary-controller.js
+++ b/modules/web-control-center/src/main/js/controllers/summary-controller.js
@@@ -1,0 -1,164 +1,170 @@@
+ /*
+  * Licensed to the Apache Software Foundation (ASF) under one or more
+  * contributor license agreements.  See the NOTICE file distributed with
+  * this work for additional information regarding copyright ownership.
+  * The ASF licenses this file to You 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.
+  */
+ 
+ controlCenterModule.controller('summaryController', ['$scope', '$http', '$common', function ($scope, $http, $common) {
+     $scope.joinTip = $common.joinTip;
+     $scope.getModel = $common.getModel;
+ 
+     $scope.javaClassItems = [
+         {label: 'snippet', value: false},
+         {label: 'factory class', value: true}
+     ];
+ 
+     $scope.evictionPolicies = [
+         {value: 'LRU', label: 'LRU'},
+         {value: 'RND', label: 'Random'},
+         {value: 'FIFO', label: 'FIFO'},
+         {value: 'SORTED', label: 'Sorted'},
+         {value: undefined, label: 'Not set'}
+     ];
+ 
+     $scope.oss = ['debian:8', 'ubuntu:14.10'];
+ 
+     $scope.configServer = {javaClassServer: false, os: undefined};
+     $scope.backupItem = {javaClassClient: false};
+ 
+     $http.get('/models/summary.json')
+         .success(function (data) {
+             $scope.screenTip = data.screenTip;
+             $scope.clientFields = data.clientFields;
+         })
+         .error(function (errMsg) {
+             $common.showError(errMsg);
+         });
+ 
+     $scope.clusters = [];
+ 
+     $scope.aceInit = function (editor) {
+         editor.setReadOnly(true);
+         editor.setOption("highlightActiveLine", false);
+ 
++        var renderer = editor.renderer;
++
++        renderer.setHighlightGutterLine(false);
++        renderer.setShowPrintMargin(false);
++        renderer.setOption('fontSize', '14px');
++
+         editor.setTheme('ace/theme/chrome');
+     };
+ 
+     $scope.reloadServer = function () {
+         $scope.javaServer = $scope.configServer.javaClassServer ? $scope.configServer.javaClass : $scope.configServer.javaSnippet;
+ 
+         if ($scope.configServer.docker) {
+             var os = $scope.configServer.os ? $scope.configServer.os : $scope.oss[0];
+ 
+             $scope.dockerServer = $scope.configServer.docker.replace(new RegExp('\%OS\%', 'g'), os);
+         }
+     };
+ 
+     $scope.selectItem = function (cluster) {
+         if (!cluster)
+             return;
+ 
+         $scope.selectedItem = cluster;
+ 
+         $scope.$watch('javaClassServer', $scope.reloadServer);
+         $scope.$watch('os', $scope.reloadServer);
+ 
+         $scope.generateServer(cluster);
+ 
+         $scope.reloadServer();
+ 
+         $scope.$watch('configServer', function () {
+             $scope.reloadServer();
+         }, true);
+ 
+         $scope.$watch('backupItem', function () {
+             $scope.generateClient();
+         }, true);
+     };
+ 
+     $scope.generateServer = function (cluster) {
+         $http.post('summary/generator', {_id: cluster._id})
+             .success(function (data) {
+                 $scope.xmlServer = data.xmlServer;
+ 
+                 $scope.configServer.javaClass = data.javaClassServer;
+                 $scope.configServer.javaSnippet = data.javaSnippetServer;
+                 $scope.configServer.docker = data.docker;
+             }).error(function (errMsg) {
+                 $common.showError('Failed to generate config: ' + errMsg);
+             });
+     };
+ 
+     $scope.generateClient = function () {
+         $http.post('summary/generator', {
+             _id: $scope.selectedItem._id, javaClass: $scope.backupItem.javaClassClient,
+             clientNearConfiguration: $scope.backupItem.nearConfiguration
+         })
+             .success(function (data) {
+                 $scope.xmlClient = data.xmlClient;
+                 $scope.javaClient = data.javaClient;
+             }).error(function (errMsg) {
+                 $common.showError('Failed to generate config: ' + errMsg);
+             });
+     };
+ 
+     $scope.download = function () {
+         $http.post('summary/download', {_id: $scope.selectedItem._id, javaClass: $scope.javaClass, os: $scope.os})
+             .success(function (data) {
+                 var file = document.createElement('a');
+ 
+                 file.setAttribute('href', 'data:application/octet-stream;charset=utf-8,' + data);
+                 file.setAttribute('download', $scope.selectedItem.name + '-configuration.zip');
+ 
+                 file.style.display = 'none';
+ 
+                 document.body.appendChild(file);
+ 
+                 file.click();
+ 
+                 document.body.removeChild(file);
+             })
+             .error(function (errMsg) {
+                 $common.showError('Failed to generate zip: ' + errMsg);
+             });
+     };
+ 
+     $http.post('clusters/list').success(function (data) {
+         $scope.clusters = data.clusters;
+ 
+         if ($scope.clusters.length > 0) {
+             var restoredId = sessionStorage.summarySelectedId;
+ 
+             var selectIdx = 0;
+ 
+             if (restoredId) {
+                 var idx = _.findIndex($scope.clusters, function (cluster) {
+                     return cluster._id == restoredId;
+                 });
+ 
+                 if (idx >= 0)
+                     selectIdx = idx;
+                 else
+                     delete sessionStorage.summarySelectedId;
+             }
+ 
+             $scope.selectItem($scope.clusters[selectIdx]);
+ 
+             $scope.$watch('selectedItem', function (val) {
+                 if (val)
+                     sessionStorage.summarySelectedId = val._id;
+             }, true);
+         }
+     });
+ }]);

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/a45a700c/modules/web-control-center/src/main/js/keys/test.crt
----------------------------------------------------------------------
diff --cc modules/web-control-center/src/main/js/keys/test.crt
index 0000000,0000000..50c6d5c
new file mode 100644
--- /dev/null
+++ b/modules/web-control-center/src/main/js/keys/test.crt
@@@ -1,0 -1,0 +1,13 @@@
++-----BEGIN CERTIFICATE-----
++MIIB6zCCAVQCCQDcAphbU6UcLjANBgkqhkiG9w0BAQsFADA6MRIwEAYDVQQDDAls
++b2NhbGhvc3QxJDAiBgkqhkiG9w0BCQEWFXNldmRva2ltb3ZAYXBhY2hlLm9yZzAe
++Fw0xNTA3MTQxMzAyNTNaFw0xODA2MjMxMzAyNTNaMDoxEjAQBgNVBAMMCWxvY2Fs
++aG9zdDEkMCIGCSqGSIb3DQEJARYVc2V2ZG9raW1vdkBhcGFjaGUub3JnMIGfMA0G
++CSqGSIb3DQEBAQUAA4GNADCBiQKBgQDP/zpJrdHqCj6lPpeFF6LQtzKef6UiyBBo
++rbuOtCCgW8KMJJciluBWk2126qLt9smBN4jBpSNU3pq0r9gBMUTd/LSe7aY4D5ED
++Pjp7XsypNVKeHaHbFi7KhfHy0LYxsWiNPmmHJv4dtYOp+pGK25rkXNfyJxxjgxN6
++wo34+MnZIQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAFk9XEjcdyihws+fVmdGGUFo
++bVxI9YGH6agiNbU3WNF4B4VRzcPPW8z2mEo7eF9kgYmq/YzH4T8tgi/qkL/u8eZV
++Wmi9bg6RThLN6/hj3wVoOFKykbDQ05FFdhIJXN5UOjPmxYM97EKqg6J0W2HAb8SG
+++UekPnmAo/2HTKsLykH8
++-----END CERTIFICATE-----

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/a45a700c/modules/web-control-center/src/main/js/keys/test.key
----------------------------------------------------------------------
diff --cc modules/web-control-center/src/main/js/keys/test.key
index 0000000,0000000..1b395c0
new file mode 100644
--- /dev/null
+++ b/modules/web-control-center/src/main/js/keys/test.key
@@@ -1,0 -1,0 +1,18 @@@
++-----BEGIN RSA PRIVATE KEY-----
++Proc-Type: 4,ENCRYPTED
++DEK-Info: DES-EDE3-CBC,6798185330CE2EE2
++
++sOwkmD8rvjx11l09V26dJhLhl+SyPIhyeZ3TqHXrYCATKoXlzidT+uPu1jVYtrwr
++nBLA6TrIDYRrBNlEsqGZ0cSvWTIczzVW1xZKHEJo5q2vUT/W8u/Q1QQtS3P3GeKF
++dEzx496rpZqwwVw59GNbuIwyYoVvQf3iEXzfhplGmLPELYIplDFOLgNuXQyXSGx6
++rwKsCxXMLsDyrA6DCz0Odf08p2HvWk/s5Ne3DFcQlqRNtIrBVGD2O0/Fp8ZZ2I4E
++Yn2OIIWJff3HanOjLOWKdN8YAn5UleNmlEUdIHeS5qaQ68mabOxLkSef9qglV+sd
++FHTtUq0cG6t6nhxZBziexha6v1yl/xABAHHhNPOfak+HthWxRD4N9f1yFYAeTmkn
++4kwBWoSUe12XRf2pGNqhEUKN/KhDmWk85wI55i/Cu2XmNoiBFlS9BXrRYU8uVCJw
++KlxjKTDWl1opCyvxTDxJnMkt44ZT445LRePKVueGIIKSUIXNQypOE+C1I0CL0N2W
++Ts3m9nthquvLeMx92k7b8yW69BER5uac3SIlGCOJObQXsHgyk8wYiyd/zLKfjctG
++PXieaW81UKjp+GqWpvWPz3VqnKwoyUWeVOOTviurli6kYOrHuySTMqMb6hxJctw9
++grAQTT0UPiAKWcM7InLzZnRjco+v9QLLEokjVngXPba16K/CItFY16xuGlaFLW7Y
++XTc67AkL8b76HBZelMjmCsqjvSoULhuMFwTOvUMm/mSM8rMoi9asrJRLQHRMWCST
++/6RENPLzPlOMnNLBujpBbn8V3/aYzEZsHMI+6S3d27WYlTJIqpabSA==
++-----END RSA PRIVATE KEY-----

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/a45a700c/modules/web-control-center/src/main/js/package.json
----------------------------------------------------------------------
diff --cc modules/web-control-center/src/main/js/package.json
index 0000000,5e5463c..477266a
mode 000000,100644..100644
--- a/modules/web-control-center/src/main/js/package.json
+++ b/modules/web-control-center/src/main/js/package.json
@@@ -1,0 -1,50 +1,51 @@@
+ {
+   "name": "ignite-web-control-center",
+   "version": "1.0.0",
+   "description": "Web application for configuration, monitoring Ignite Cluster",
+   "private": true,
+   "scripts": {
+     "start": "node ./bin/www"
+   },
+   "author": "",
+   "contributors": [
+     {
+       "name": "",
+       "email": ""
+     }
+   ],
+   "license": "Apache-2.0",
+   "keywords": "grid",
+   "homepage": "https://ignite.incubator.apache.org/",
+   "engines": {
+     "node": ">=0.12.4"
+   },
+   "dependencies": {
+     "angular-ui-ace": "^0.2.3",
+     "archiver": "^0.14.4",
+     "body-parser": "~1.12.0",
+     "connect-flash": "^0.1.1",
+     "connect-mongo": "^0.8.1",
+     "cookie-parser": "~1.3.4",
+     "debug": "~2.1.1",
+     "express": "~4.12.2",
+     "express-session": "^1.11.1",
+     "jade": "~1.9.2",
+     "less-middleware": "1.0.x",
+     "lodash": "3.10.0",
+     "mongoose": "^4.0.2",
+     "nconf": "^0.7.1",
+     "passport": "^0.2.1",
+     "passport-local": "^1.0.0",
+     "passport-local-mongoose": "^1.0.0",
+     "pg": "^4.4.0",
+     "serve-favicon": "~2.2.0",
 -    "util": "^0.10.3"
++    "util": "^0.10.3",
++    "ws": "~0.7.2"
+   },
+   "devDependencies": {
+     "morgan": "~1.5.1",
+     "supertest": "^1.0.1",
+     "mocha": "~2.0.1",
+     "should": "~3.1.3"
+   }
+ }

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/a45a700c/modules/web-control-center/src/main/js/public/stylesheets/style.less
----------------------------------------------------------------------
diff --cc modules/web-control-center/src/main/js/public/stylesheets/style.less
index 0000000,e05f0ff..3d0ede7
mode 000000,100644..100644
--- a/modules/web-control-center/src/main/js/public/stylesheets/style.less
+++ b/modules/web-control-center/src/main/js/public/stylesheets/style.less
@@@ -1,0 -1,1182 +1,1193 @@@
+ /*
+  * Licensed to the Apache Software Foundation (ASF) under one or more
+  * contributor license agreements.  See the NOTICE file distributed with
+  * this work for additional information regarding copyright ownership.
+  * The ASF licenses this file to You 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.
+  */
+ 
+ @logo-path: "https://www.filepicker.io/api/file/QagunjDGRFul2JgNCAli";
+ @input-height: 28px;
+ @ignite-red: #ec1c24;
+ @ignite-block-callout-background: #f3f8f3;
+ @ignite-block-callout: #50af51;
+ 
+ hr {
+   margin-top: 20px;
+   margin-bottom: 20px;
+ }
+ 
+ .main-header .logo {
+   height: auto;
+ }
+ 
+ .main-sidebar {
+   padding-top: 60px;
+ }
+ 
+ .navbar-default .navbar-brand, .navbar-default .navbar-brand:hover {
+   position: absolute;
+   width: 100%;
+   left: 0;
+   text-align: center;
+ }
+ 
+ .modal-backdrop.am-fade {
+   opacity: .5;
+   transition: opacity .15s linear;
+   &.ng-enter {
+     opacity: 0;
+     &.ng-enter-active {
+       opacity: .5;
+     }
+   }
+   &.ng-leave {
+     opacity: .5;
+     &.ng-leave-active {
+       opacity: 0;
+     }
+   }
+ }
+ 
+ .modal.center .modal-dialog {
+   position: fixed;
+   top: 50%;
+   left: 50%;
+   -webkit-transform: translateX(-50%) translateY(-50%);
+   transform: translateX(-50%) translateY(-50%);
+ }
+ 
+ .border-left {
+   box-shadow: 1px 0 0 0 #eee inset;
+ }
+ 
+ .border-right {
+   box-shadow: 1px 0 0 0 #eee;
+ }
+ 
+ .theme-line {
+   background-color: #f9f9f9;
+ }
+ 
+ .theme-line header {
+   background-color: #fff;
+ }
+ 
+ .theme-line header a.btn {
+   border: 0 none;
+   padding: 10px 25px;
+   background-color: rgba(0, 0, 0, 0.15);
+ }
+ 
+ .theme-line header a.btn:hover {
+   background-color: rgba(0, 0, 0, 0.25);
+ }
+ 
+ .theme-line header a.btn.btn-link {
+   background: transparent;
+   color: rgba(255, 255, 255, 0.8);
+ }
+ 
+ .theme-line header a.btn.btn-link:hover {
+   color: #fff;
+   text-decoration: none;
+ }
+ 
+ .theme-line .navbar-nav a {
+   background-color: transparent;
+ }
+ 
+ .theme-line .navbar-nav a:hover,
+ .theme-line .navbar-nav a:active,
+ .theme-line .navbar-nav a:focus {
+   background-color: transparent;
+ }
+ 
+ .theme-line .main-links {
+   padding-top: 50px;
+ }
+ 
+ .theme-line .main-links h3 {
+   margin-top: 0;
+   font-size: 17px;
+ }
+ 
+ .theme-line .main-links .links a {
+   color: #888;
+ }
+ 
+ .theme-line .main-links .links a:hover {
+   text-decoration: none;
+ }
+ 
+ .theme-line #category-columns,
+ .theme-solid #category-columns {
+   margin: 50px 30px 0;
+ }
+ 
+ .theme-line #category-columns h4 {
+   text-transform: uppercase;
+   font-weight: 300;
+   color: #999;
+   font-size: 14px;
+ }
+ 
+ .theme-line #category-columns ul {
+   list-style: none;
+   padding: 0;
+   margin-bottom: 15px;
+ }
+ 
+ .theme-line #category-columns ul li a {
+   padding: 5px 0;
+   display: block;
+   font-size: 16px;
+ }
+ 
+ .theme-line #category-columns ul .view-all {
+   font-size: 0.85em;
+ }
+ 
+ .theme-line .docs-header {
+   color: #999;
+   overflow: hidden;
+ }
+ 
+ .theme-line .docs-header h1 {
+   color: #444;
+   margin-top: 0;
+   font-size: 22px;
+ }
+ 
+ .theme-line .btn-primary {
+   border: 0 none;
+   background-color: @ignite-red;
+ }
+ 
+ .theme-line .btn-primary:hover {
+   background-color: #950d12;
+ }
+ 
+ .theme-line .main-content .nav-horizontal a {
+   box-shadow: 0 0;
+   border: 0 none;
+   background-color: #fff;
+   border-radius: 0;
+   color: #aaa;
+   padding: 6px;
+   margin: 0 14px;
+ }
+ 
+ .theme-line .main-content .nav-horizontal a:hover {
+   color: #999;
+   border-bottom: 5px solid #ddd;
+ }
+ 
+ .theme-line .main-content .nav-horizontal a.active {
+   border-bottom: 5px solid #888;
+ }
+ 
+ .theme-line .navbar-nav, .theme-line .sidebar-nav {
+   ul li > a.active {
+     cursor: default;
+     pointer-events: none;
+   }
+ }
+ 
+ .theme-line .sidebar-nav {
+   color: #474a54;
+   padding-bottom: 30px;
+ 
+   ul {
+     padding: 0;
+     list-style: none;
+     font-size: 14px;
+     margin: 3px 0 0;
+     li {
+       color: #666;
+       line-height: @input-height;
+ 
+       span.fa-stack {
+         margin-right: 5px;
+         font-size: 12px;
+         height: 26px;
+       }
+ 
+       a {
+         font-size: 18px;
+         color: #666;
+         position: relative;
+         white-space: nowrap;
+         overflow: hidden;
+         -o-text-overflow: ellipsis;
+         text-overflow: ellipsis;
+       }
+     }
+   }
+ }
+ 
+ .theme-line .sidebar-nav ul li a:hover {
+   text-decoration: none;
+ }
+ 
+ .theme-line .select,
+ .theme-line .typeahead {
+   li a {
+     color: #666;
+     background-color: transparent;
+   }
+ 
+   li a:hover {
+     color: @ignite-red;
+   }
+ 
+   .active {
+     background-color: #eee;
+   }
+ }
+ 
+ .theme-line .sidebar-nav ul li .subcategory {
+   padding-left: 15px;
+ }
+ 
+ .theme-line .sidebar-nav h4 {
+   margin-top: 2em;
+   font-weight: normal;
+   text-transform: uppercase;
+   font-size: 11px;
+   margin-bottom: 10px;
+   color: #bbb;
+ }
+ 
+ .theme-line .sidebar-nav h4:first-child {
+   margin-top: 0;
+ }
+ 
+ .theme-line .sidebar-nav .ask {
+   width: 100%;
+   text-align: center;
+   padding: 10px;
+ }
+ 
+ .theme-line .border-left .sidebar-nav {
+   padding-left: 15px;
+ }
+ 
+ .theme-line .suggest {
+   padding: 5px;
+   display: inline-block;
+   font-size: 12px;
+ }
+ 
+ .header {
+   padding: 15px;
+ }
+ 
+ .header .has-github {
+   padding-right: 136px;
+ }
+ 
+ .header h1.navbar-brand {
+   height: 40px;
+   width: 200px;
+   padding: 0;
+   margin: 5px 15px 0 0;
+ }
+ 
+ .header h1.navbar-brand a {
+   text-indent: -99999px;
+   background: no-repeat center center;
+   display: block;
+   width: 100%;
+   height: 100%;
+   background-size: contain;
+ }
+ 
+ .header .nav.navbar-nav.pull-right {
+   position: relative;
+   right: -30px;
+ }
+ 
+ .header .nav.navbar-nav .not-link {
+   padding: 15px;
+   display: inline-block;
+ }
+ 
+ .header .nav.navbar-nav .stable,
+ .header .nav.navbar-nav .beta,
+ .header .nav.navbar-nav .private {
+   font-size: 9px;
+   padding: 3px 5px;
+   display: inline-block;
+   line-height: 8px;
+   border-radius: 3px;
+   margin-left: 6px;
+   color: #fff;
+   top: -2px;
+   position: relative;
+   opacity: 0.6;
+   -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)";
+   filter: alpha(opacity=60);
+ }
+ 
+ .header .nav.navbar-nav a:hover > .stable,
+ .header .nav.navbar-nav a:hover > .beta,
+ .header .nav.navbar-nav a:hover > .private {
+   opacity: 1;
+   -ms-filter: none;
+   filter: none;
+ }
+ 
+ .header .nav.navbar-nav .beta {
+   background-color: #59c3d1;
+ }
+ 
+ .header .nav.navbar-nav .stable {
+   background-color: #41b841;
+ }
+ 
+ .header .nav.navbar-nav .private {
+   background-color: #333;
+ }
+ 
+ .theme-line header {
+   border-bottom: 8px solid;
+ }
+ 
+ .theme-line header h2 {
+   color: #aaa;
+ }
+ 
+ .theme-line header p {
+   color: #666;
+ }
+ 
+ .theme-line header {
+   border-bottom-color: @ignite-red;
+ }
+ 
+ .theme-line .navbar-nav {
+   color: #888;
+ }
+ 
+ .theme-line .navbar-nav a {
+   color: #bbb;
+ }
+ 
+ .theme-line header a.btn {
+   background-color: @ignite-red;
+ }
+ 
+ .theme-line header a.btn:hover {
+   background-color: #950d12;
+ }
+ 
+ .theme-line header .navbar-nav .tt-cursor {
+   background-color: @ignite-red;
+ }
+ 
+ .theme-line header .navbar-nav a:hover, .theme-line header .navbar-nav .open > a {
+   color: @ignite-red;
+ }
+ 
+ .theme-line .navbar-nav .active a {
+   //font-weight: bold;
+   color: @ignite-red;
+ }
+ 
+ .theme-line .navbar-nav .active a:hover {
+   color: #950d12;
+ }
+ 
+ .theme-line .main-links .links a:hover {
+   color: @ignite-red;
+ }
+ 
+ .theme-line .main-content a {
+   color: #666;
+ }
+ 
+ .theme-line .main-content a:hover {
+   color: #950d12;
+ }
+ 
+ .theme-line .sidebar-nav ul li a.active:before {
+   background-color: @ignite-red;
+ }
+ 
+ .theme-line .sidebar-nav ul li a.active {
+   color: @ignite-red;
+ }
+ 
+ .theme-line .sidebar-nav ul li a:hover, .theme-line .sidebar-nav ul li a.active:hover {
+   color: #950d12;
+ }
+ 
+ .theme-line .main-content .nav-horizontal a.active {
+   border-color: @ignite-red;
+   color: @ignite-red;
+ }
+ 
+ .theme-line .main-content .nav-horizontal a:hover {
+   color: #950d12;
+ }
+ 
+ .theme-line .main-content .nav-horizontal a.active:hover {
+   border-color: #950d12;
+ }
+ 
+ .theme-line header .navbar-nav a.active, .theme-line #versions-list li a:hover strong, .theme-line #versions-list li a.active .current, .theme-line #versions-list li a:active .current {
+   color: @ignite-red;
+ }
+ 
+ .theme-line header .navbar-nav a {
+   font-size: 18px;
+ }
+ 
+ .theme-line.body-threes .section-right .threes-nav .btn-default:hover, .theme-line.page-docs.body-threes .section-right .threes-nav .pull-right a:hover {
+   color: @ignite-red;
+   border-color: @ignite-red;
+ }
+ 
+ .theme-line .section-right {
+   padding-left: 30px;
+ }
+ 
+ .body-overlap .main-content {
+   margin-top: 30px;
+ }
+ 
+ .body-box .main-content,
+ .body-overlap .main-content {
+   padding: 30px;
+   box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);
+   background-color: #fff;
+ }
+ 
+ body {
+   font-weight: 400;
+   font-family: Roboto Slab, serif;;
+ }
+ 
+ h1, h2, h3, h4, h5, h6 {
+   font-weight: 700;
+   font-family: Roboto Slab, serif;
+   margin-bottom: 10px;
+ }
+ 
+ .submit-vote.submit-vote-parent.voted a.submit-vote-button, .submit-vote.submit-vote-parent a.submit-vote-button:hover {
+   background-color: @ignite-red;
+ }
+ 
+ div.submit-vote.submit-vote-parent.voted a.submit-vote-button:hover {
+   background-color: #950d12;
+ }
+ 
+ a, .link .title {
+   color: @ignite-red;
+ }
+ 
+ a:hover, .link:hover .title {
+   color: #950d12;
+ }
+ 
+ .header h1.navbar-brand a {
+   background-image: url("@{logo-path}");
+ }
+ 
+ .header h1.navbar-brand {
+   width: 96px;
+ }
+ 
+ .block-edit-parameters {
+   text-align: right;
+   padding-bottom: 5px;
+ }
+ 
+ .container-footer {
+   margin-top: 20px;
+ }
+ 
+ /* Modal */
+ .modal {
+   display: block;
+   overflow: hidden;
+ }
+ 
+ .modal .close {
+   position: absolute;
+   top: 10px;
+   right: 10px;
+   float: none;
+ }
+ 
+ // Close icon
+ .modal-header .close {
+   margin-right: -2px;
+ }
+ 
+ .modal .modal-dialog {
+   width: 610px;
+ }
+ 
+ .modal .modal-content {
+   border-radius: 0;
+   background-color: #f7f7f7;
+ }
+ 
+ .modal .modal-content .modal-header {
+   background-color: #fff;
+   text-align: center;
+   color: #555;
+   padding: 15px;
+   font-family: "myriad-pro", sans-serif;
+ }
+ 
+ .modal .modal-content .modal-header h4 {
+   font-family: "myriad-pro", sans-serif;
+   font-size: 22px;
+ }
+ 
+ .modal .modal-content .modal-header h4 .fa {
+   display: block;
+   font-size: 41px;
+   color: #ddd;
+   margin-bottom: 5px;
+ }
+ 
+ .modal .modal-content .modal-header p {
+   color: #aaa;
+   font-size: 1em;
+   margin: 3px 0 0;
+ }
+ 
+ .modal .modal-content .modal-spacer {
+   padding: 10px 10px 0 10px;
+ }
+ 
+ .modal .modal-content .modal-footer {
+   margin-top: 0;
+ }
+ 
+ .modal-body {
+   padding-top: 15px;
+ }
+ 
+ h1.ignite-logo {
+   background-image: url("@{logo-path}");
+ }
+ 
+ .block-display-image img {
+   max-width: 100%;
+   max-height: 450px;
+   margin: auto;
+   display: block;
+ }
+ 
+ .greedy {
 -  min-height: 200px;
++  min-height: 100%;
+   height: ~"calc(100vh - 290px)";
+ }
+ 
+ @media (min-width: 768px) {
+   .navbar-nav > li > a {
+     padding-top: 18px;
+     padding-bottom: 10px;
+   }
+ }
+ 
+ .details-row {
+   padding: 0 10px;
+ }
+ 
+ .details-row, .settings-row {
+   display: block;
+   margin: 10px 0;
+ 
+   label.table-header {
+     line-height: @input-height;
+   }
+ 
+   [class*="col-"] {
+     display: inline-block;
+     vertical-align: middle;
+     float: none;
+ 
+     padding-left: 0 !important;
+     padding-right: 0 !important;
+   }
+ 
+   input[type="checkbox"] {
+     line-height: 20px;
+     margin-right: 5px;
+   }
+ 
+   .checkbox label {
+     line-height: 20px;
+     vertical-align: middle;
+   }
+ }
+ 
+ button {
+   margin-right: 5px;
+ }
+ 
+ h1,
+ h2,
+ h3 {
+   user-select: none;
+   font-weight: normal;
+   /* Makes the vertical size of the text the same for all fonts. */
+   line-height: 1;
+ }
+ 
+ h3 {
+   color: black;
+   font-size: 1.2em;
+   margin-top: 0;
+   margin-bottom: 1.5em;
+ }
+ 
+ table tr:hover {
+   cursor: pointer;
+ }
+ 
+ .btn {
+   padding: 3px 6px;
+ }
+ 
+ button .caret, .btn .caret {
+   float: right;
+   margin-left: 5px;
+   margin-top: 7px;
+ }
+ 
+ .base-control {
+   text-align: left;
+   padding: 3px 3px;
+   height: @input-height;
+ }
+ 
+ .form-control:extend(.base-control all) {
+   display: inline-block;
+ 
+   button {
+     text-align: left;
+   }
+ }
+ 
+ .theme-line .panel-heading {
+   padding: 10px 10px;
+   margin: 0;
+ 
+   h3 {
+     margin-bottom: 0;
+   }
+ 
+   h3 > a {
+     color: black;
+   }
+ }
+ 
+ .theme-line .panel-title {
+   a {
+     color: @ignite-red;
+   }
+ 
+   h3 {
+     margin-bottom: 20px;
+   }
+ }
+ 
+ .theme-line .panel-body {
+   padding: 10px 20px;
+ }
+ 
+ .theme-line .main-content a.customize {
+   margin-left: 5px;
+   color: @ignite-red;
+ }
+ 
+ .theme-line .panel-collapse {
+   margin: 0;
+ }
+ 
+ .theme-line .links table {
+   display: table;
+   table-layout: fixed;
+ 
+   td {
+     padding-left: 18px;
+   }
+ 
+   .active a {
+     color: @ignite-red;
+     font-weight: bold;
+   }
+ 
+   a:hover {
+     color: #950d12;
+   }
+ 
+   a {
+     color: #666;
+   }
+ }
+ 
+ .theme-line table.links-edit:extend(.theme-line .links table all) {
+   margin-top: 5px;
+   margin-bottom: 5px;
+ 
+   label {
+     line-height: @input-height;
+     color: #666;
+   }
+ }
+ 
+ .theme-line table.links-edit-details:extend(.theme-line .links table all) {
+   margin-bottom: 10px;
+ 
+   label {
+     line-height: @input-height;
+     color: #666;
+   }
+ 
+   td {
+     padding: 0;
+ 
+     .input-tip {
+       padding: 0;
+     }
+   }
+ }
+ 
+ .theme-line table.admin {
+   tr:hover {
+     cursor: default;
+   }
+ 
+   thead > tr th.header {
+     padding: 0 0 10px;
+ 
+     div {
+       padding: 0
+     }
+   }
+ 
+   margin-bottom: 10px;
+ 
+   label {
+     line-height: @input-height;
+     color: #666;
+   }
+ 
+   thead > tr th, td {
+     padding: 10px 10px;
+ 
+     .input-tip {
+       padding: 0;
+     }
+   }
+ 
+   tfoot > tr > td {
+     padding: 0;
+ 
+     .pagination {
+       margin: 10px 0;
+ 
+       > .active > a {
+         color: @ignite-red;
+         font-weight: bold;
+         border-color: #ddd;
+         background-color: #eee;
+       }
+     }
+   }
+ }
+ 
+ .panel-title a {
+   font-size: 14px;
+ }
+ 
+ .panel-details {
+   margin-top: 10px;
+ 
+   padding: 0;
+ 
+   border-radius: 5px;
+   border: thin dotted lightgrey;
+ }
+ 
+ .table-details {
+   border-radius: 5px;
+   border: thin dotted lightgrey;
+ 
+   margin-top: 10px;
+ 
+   padding-left: 10px;
+   padding-right: 5px;
+ }
+ 
+ .tooltip.right .tooltip-arrow {
+   border-right-color: @ignite-red;
+ }
+ 
+ .tooltip > .tooltip-inner {
+   max-width: 400px;
+   text-align: left;
+   background-color: @ignite-red;
+ }
+ 
+ label {
+   font-weight: normal;
+   margin-bottom: 0;
+ }
+ 
+ .form-horizontal .checkbox {
+   padding-top: 0;
+ }
+ 
+ .input-tip {
+   display: block;
+   overflow: hidden;
+ }
+ 
+ .labelField {
+   float: left;
+   margin-right: 5px;
+ }
+ 
+ .labelFormField {
+   float: left;
+   line-height: @input-height;
+ }
+ 
+ .form-horizontal .form-group {
+   margin: 0;
+ }
+ 
+ .form-horizontal .has-feedback .form-control-feedback {
+   right: 0;
+ }
+ 
+ .tipField {
+   float: right;
+   line-height: @input-height;
+   margin-left: 5px;
+ }
+ 
+ .tipLabel {
+   font-size: 14px;
+   margin-left: 5px;
+ }
+ 
+ .fieldSep {
+   float: right;
+   line-height: @input-height;
+   margin: 0 5px;
+ }
+ 
+ .fieldButton {
+   float: right;
+   margin-left: 5px;
+   margin-right: 0;
+ }
+ 
+ .fa-plus {
+   cursor: pointer;
+ }
+ 
+ .fa-remove {
+   color: @ignite-red;
+   cursor: pointer;
+ }
+ 
+ .fa-floppy-o {
+   cursor: pointer;
+ }
+ 
+ .fa-arrow-up {
+   cursor: pointer;
+ }
+ 
+ .fa-arrow-down {
+   cursor: pointer;
+ }
+ 
+ label.required:after {
+   color: @ignite-red;
+   content: ' *';
+   display: inline;
+ }
+ 
+ .blank {
+   visibility: hidden;
+ }
+ 
+ .alert {
+   outline: 0
+ }
+ 
+ .alert.bottom, .alert.bottom-left, .alert.bottom-right, .alert.top,
+ .alert.top-left, .alert.top-right {
+   position: fixed;
+   z-index: 1050;
+   margin: 20px
+ }
+ 
+ .alert.top, .alert.top-left, .alert.top-right {
+   top: 50px
+ }
+ 
+ .alert.top {
+   right: 0;
+   left: 0
+ }
+ 
+ .alert.top-right {
+   right: 0
+ }
+ 
+ .alert.top-right .close {
+   padding-left: 10px
+ }
+ 
+ .alert.top-left {
+   left: 0
+ }
+ 
+ .alert.top-left .close {
+   padding-right: 10px
+ }
+ 
+ .alert.bottom, .alert.bottom-left, .alert.bottom-right {
+   bottom: 0
+ }
+ 
+ .alert.bottom {
+   right: 0;
+   left: 0
+ }
+ 
+ .alert.bottom-right {
+   right: 0
+ }
+ 
+ .alert.bottom-right .close {
+   padding-left: 10px
+ }
+ 
+ .alert.bottom-left {
+   left: 0
+ }
+ 
+ .alert.bottom-left .close {
+   padding-right: 10px
+ }
+ 
+ //  Summary page
+ #cfgResult textarea {
+   font-family: monospace;
+   font-size: 12px;
+ }
+ 
+ input[type="number"]::-webkit-outer-spin-button,
+ input[type="number"]::-webkit-inner-spin-button {
+   -webkit-appearance: none;
+   margin: 0;
+ }
+ 
+ input[type="number"] {
+   -moz-appearance: textfield;
+ }
+ 
+ input.ng-dirty.ng-invalid, button.ng-dirty.ng-invalid {
+   border-color: @ignite-red;
+ 
+   :focus {
+     border-color: @ignite-red;
+   }
+ }
+ 
+ .form-control-feedback {
+   display: inline-block;
+   color: @ignite-red;
+   right: 18px;
+   line-height: @input-height;
+   pointer-events: initial;
+ }
+ 
+ .syntaxhighlighter {
+   padding: 10px 5px;
+   border-radius: 6px;
+ }
+ 
+ .theme-line table.links-edit-small-padding:extend(.theme-line .links table all) {
+   label {
+     line-height: @input-height;
+     color: #666;
+   }
+ 
+   a {
+     line-height: @input-height;
+   }
+ 
+   input[type="checkbox"] {
+     line-height: 20px;
+     margin-right: 5px;
+   }
+ 
+   .checkbox label {
+     line-height: 20px;
+     vertical-align: middle;
+   }
+ 
+   th {
+     text-align: center;
+   }
+ 
+   td {
+     padding-left: 10px;
+   }
+ 
+   margin-top: 10px;
+ }
+ 
 -.configBox .nav > li > a {
++.nav-tabs > li > a {
+   padding: 5px 5px;
+ }
+ 
+ .viewedUser {
+   position: absolute;
+   width: 100%;
+   left: 0;
+ 
+   text-align: center;
+ 
+   margin-top: -15px;
+ 
+   background-color: #f8d5d8;
+ }
+ 
+ a {
+   cursor: pointer;
+ }
+ 
+ .st-sort-ascent:after {
+   content: '\25B2';
+ }
+ 
+ .st-sort-descent:after {
+   content: '\25BC';
+ }
+ 
+ .panel {
+   margin-bottom: 0;
+ }
+ 
+ .panel-group {
+   margin-bottom: 0;
+ }
+ 
+ .panel-group .panel + .panel {
+   margin-top: 20px;
+ }
+ 
+ .margin-top-dflt {
+   margin-top: 10px;
+ }
+ 
+ .margin-bottom-dflt {
+   margin-bottom: 10px;
+ }
+ 
+ .margin-dflt {
+   margin-top: 10px;
+   margin-bottom: 10px;
+ }
+ 
+ .padding-top-dflt {
+   padding-top: 10px;
+ }
+ 
+ .padding-bottom-dflt {
+   padding-bottom: 10px;
+ }
+ 
+ .padding-dflt {
+   padding-top: 10px;
+   padding-bottom: 10px;
+ }
+ 
+ .theme-line .panel-title h3 {
+   margin-top: 20px;
+   margin-bottom: 20px;
+ }
+ 
+ .block-callout-parent {
+   background-color: @ignite-block-callout-background;
+   overflow: hidden;
+ }
+ 
+ .block-callout {
+   background-color: @ignite-block-callout-background;
+   display: inline-block;
+   vertical-align: top;
+   width: 50%;
+ 
+   i {
+     padding: 10px 5px 0 10px;
+     color: @ignite-block-callout;
+   }
+ 
+   ul {
+     padding-left: 20px;
+     margin-bottom: 0;
+   }
+ 
+   p {
+     padding: 5px 0 10px 20px;
+     margin: 0;
+   }
+ 
+   label {
+     font-weight: bold;
+     color: @ignite-block-callout;
+   }
+ }
+ 
+ .block-callout-border {
+   border-left: 5px solid;
+   border-color: @ignite-block-callout;
+ }
+ 
+ .labelHeader {
+   font-weight: bold;
+ }
+ 
+ .ace_editor, #ace_document {
++  margin:  0.65em 0 0 0;
++
+   width: 100%;
+   height: 400px;
 -}
+ 
++  .ace_gutter {
++    background: transparent !important;
++    border: 1px #ddd;
++    border-right-style: solid;
++  }
+ 
++  .ace_gutter-cell, .ace_folding-enabled > .ace_gutter-cell {
++    padding-left: 0.65em;
++    padding-right: 0.9em;
++  }
++}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/a45a700c/modules/web-control-center/src/main/js/routes/agent.js
----------------------------------------------------------------------
diff --cc modules/web-control-center/src/main/js/routes/agent.js
index 0000000,0000000..19379e2
new file mode 100644
--- /dev/null
+++ b/modules/web-control-center/src/main/js/routes/agent.js
@@@ -1,0 -1,0 +1,37 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements.  See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You 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.
++ */
++
++var router = require('express').Router();
++var agentManager = require('../agents/agent-manager');
++
++/* GET summary page. */
++router.post('/topology', function(req, res) {
++    var c = agentManager.getAgentManager().getOneClient();
++
++    if (!c)
++        return res.status(500).send("Client not found");
++
++    var ignite = c.ignite();
++
++    ignite.cluster().then(function (cluster) {
++        res.json(cluster);
++    }, function (err) {
++        res.send(err);
++    });
++});
++
++module.exports = router;

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/a45a700c/modules/web-control-center/src/main/js/views/configuration/summary.jade
----------------------------------------------------------------------
diff --cc modules/web-control-center/src/main/js/views/configuration/summary.jade
index 0000000,6f2f6d8..0370ef4
mode 000000,100644..100644
--- a/modules/web-control-center/src/main/js/views/configuration/summary.jade
+++ b/modules/web-control-center/src/main/js/views/configuration/summary.jade
@@@ -1,0 -1,117 +1,115 @@@
+ //-
+     Licensed to the Apache Software Foundation (ASF) under one or more
+     contributor license agreements.  See the NOTICE file distributed with
+     this work for additional information regarding copyright ownership.
+     The ASF licenses this file to You 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.
+ 
+ extends sidebar
+ 
+ append scripts
+     script(src='/summary-controller.js')
+ 
+     script(src="//cdn.jsdelivr.net/angularjs/1.3.15/angular-animate.min.js")
+     script(src="//cdn.jsdelivr.net/angularjs/1.3.15/angular-sanitize.min.js")
+ 
+     script(src='//cdnjs.cloudflare.com/ajax/libs/ace/1.2.0/theme-chrome.js')
+     script(src='//cdnjs.cloudflare.com/ajax/libs/ace/1.2.0/mode-xml.js')
+     script(src='//cdnjs.cloudflare.com/ajax/libs/ace/1.2.0/mode-java.js')
+     script(src='//cdnjs.cloudflare.com/ajax/libs/ace/1.2.0/mode-dockerfile.js')
+ 
+ append css
+ 
+ include ../includes/controls
+ 
+ mixin hard-link(ref, txt)
+     a(style='color:#ec1c24' href=ref target="_blank") #{txt}
+ 
+ block content
+     .docs-header
+         h1 Configurations Summary
+         hr
+     .docs-body(ng-controller='summaryController')
+         +block-callout('{{screenTip.workflowTitle}}', 'joinTip(screenTip.workflowContent)', '{{screenTip.whatsNextTitle}}', 'joinTip(screenTip.whatsNextContent)')
+         .padding-dflt(ng-if='clusters.length == 0')
+             | No cluster configured. You can&nbsp;
+             a(href='clusters') configure
+             | &nbsp;it.
+         .padding-dflt(ng-if='clusters.length > 0')
+             lable.labelHeader Clusters:
+             .links
+                 table(st-table='clusters')
+                     tbody
+                         tr(ng-repeat='row in clusters track by row._id')
+                             td.col-sm-6(ng-class='{active: row._id == selectedItem._id}')
+                                 a(ng-click='selectItem(row)') {{$index + 1}}) {{row.name}}
+         div(ng-show='selectedItem' role="tab" method='post' action='summary/download')
+             .padding-dflt(bs-collapse data-start-collapsed='false')
+                 .panel.panel-default
+                     form.panel-heading(role='tab' method='post' action='summary/download')
+                         input(type="hidden" name="_id" value="{{selectedItem._id}}")
+                         input(type="hidden" name="os" value="{{os}}")
+                         input(type="hidden" name="javaClass" value="{{javaClassServer}}")
+                         h3
+                             a(bs-collapse-toggle) Server
+                             button.btn.btn-primary.pull-right(type='submit' style='margin-top: -5px') Download
+                     .panel-collapse(role="tabpanel" bs-collapse-target)
 -                        .panel-body
 -                            .configBox(ng-show='selectedItem' bs-tabs)
 -                                div(title='<img src="/images/xml.png" width="16px" height="16px"/> XML' bs-pane)
 -                                    .configBox(ui-ace='{ onLoad: aceInit, mode: "xml" }' ng-model='xmlServer' style='margin: 0.65em 0;')
 -                                div(title='<img src="/images/java.png" width="16px" height="16px"/> Java' bs-pane)
 -                                    .settings-row
 -                                        .col-sm-1
 -                                            label Generate:
 -                                        .col-sm-3
 -                                            button.form-control(type='button' ng-model='configServer.javaClassServer'  bs-select data-placeholder='{{detail.placeholder}}' bs-options='item.value as item.label for item in javaClassItems' data-sort='false')
 -                                    .configBox(ui-ace='{ onLoad: aceInit, mode: "java" }' ng-model='javaServer')
 -                                div(title='<img src="/images/docker.png" width="16px" height="16px"/> Dockerfile' bs-pane)
 -                                    .settings-row
 -                                        p
 -                                            +hard-link('https://docs.docker.com/reference/builder', 'Docker')
 -                                            | &nbsp;file is a text file with instructions to create Docker image.<br/>
 -                                            | To build image you have to store following Docker file with your Ignite XML configuration to the same directory.<br>
 -                                            | Also you could use predefined&nbsp;
 -                                            +hard-link('https://ignite.incubator.apache.org/download.html#docker', 'Apache Ignite docker image')
 -                                            | . For more information about using Ignite with Docker please read&nbsp;
 -                                            +hard-link('http://apacheignite.readme.io/docs/docker-deployment', 'documentation')
 -                                            |.
 -                                        .col-sm-2
 -                                            label(for='os') Operation System:
 -                                        .col-sm-4
 -                                            input#os.form-control(type='text', ng-model='configServer.os' placeholder='debian:8' data-min-length="0" data-html="1" data-auto-select="true" data-animation="am-flip-x" bs-typeahead bs-options='os for os in oss')
 -                                    div(ui-ace='{ onLoad: aceInit, mode: "dockerfile" }' ng-model='dockerServer')
++                        div(ng-show='selectedItem' bs-tabs style='margin-top: 0.65em')
++                            div(title='<img src="/images/xml.png" width="16px" height="16px"/> XML' bs-pane)
++                                div(ui-ace='{ onLoad: aceInit, mode: "xml" }' ng-model='xmlServer')
++                            div(title='<img src="/images/java.png" width="16px" height="16px"/> Java' bs-pane)
++                                .details-row
++                                    .col-sm-1
++                                        label Generate:
++                                    .col-sm-3
++                                        button.form-control(type='button' ng-model='configServer.javaClassServer'  bs-select data-placeholder='{{detail.placeholder}}' bs-options='item.value as item.label for item in javaClassItems' data-sort='false')
++                                div(ui-ace='{ onLoad: aceInit, mode: "java" }' ng-model='javaServer')
++                            div(title='<img src="/images/docker.png" width="16px" height="16px"/> Dockerfile' bs-pane)
++                                .details-row
++                                    p
++                                        +hard-link('https://docs.docker.com/reference/builder', 'Docker')
++                                        | &nbsp;file is a text file with instructions to create Docker image.<br/>
++                                        | To build image you have to store following Docker file with your Ignite XML configuration to the same directory.<br>
++                                        | Also you could use predefined&nbsp;
++                                        +hard-link('https://ignite.incubator.apache.org/download.html#docker', 'Apache Ignite docker image')
++                                        | . For more information about using Ignite with Docker please read&nbsp;
++                                        +hard-link('http://apacheignite.readme.io/docs/docker-deployment', 'documentation')
++                                        |.
++                                    .col-sm-2
++                                        label(for='os') Operation System:
++                                    .col-sm-4
++                                        input#os.form-control(type='text', ng-model='configServer.os' placeholder='debian:8' data-min-length="0" data-html="1" data-auto-select="true" data-animation="am-flip-x" bs-typeahead bs-options='os for os in oss')
++                                div(ui-ace='{ onLoad: aceInit, mode: "dockerfile" }' ng-model='dockerServer')
+             .padding-dflt(bs-collapse data-start-collapsed='false')
+                 .panel.panel-default
+                     form.panel-heading(role='tab' method='post' action='summary/download')
+                         input(type="hidden" name="_id" value="{{selectedItem._id}}")
+                         input(type="hidden" name="javaClass" value="{{javaClassClient}}")
+                         input(type="hidden" name="clientNearConfiguration" value="{{backupItem}}")
+ 
+                         h3
+                             a(bs-collapse-toggle) Client
+                             button.btn.btn-primary.pull-right(type='submit' style='margin-top: -5px') Download
+                     .panel-collapse(role="tabpanel" bs-collapse-target)
 -                        .panel-body
 -                            div(ng-show='selectedItem')
 -                                .settings-row(ng-repeat='field in clientFields')
 -                                    +form-row-custom(['col-sm-3'], ['col-sm-3'])
 -                                .configBox(bs-tabs)
 -                                    div(title='<img src="/images/xml.png" width="16px" height="16px"/> XML' bs-pane)
 -                                        .configBox(ui-ace='{ onLoad: aceInit, mode: "xml" }' ng-model='xmlClient' style='margin: 0.65em 0;')
 -                                    div(title='<img src="/images/java.png" width="16px" height="16px"/> Java' bs-pane)
 -                                        .settings-row
 -                                            .col-sm-1
 -                                                label Generate:
 -                                            .col-sm-4
 -                                                button.form-control(type='button' ng-model='backupItem.javaClassClient' bs-select data-placeholder='{{detail.placeholder}}' bs-options='item.value as item.label for item in javaClassItems' data-sort='false')
 -                                        div(ui-ace='{ onLoad: aceInit, mode: "java" }' ng-model='javaClient')
++                        div(ng-show='selectedItem')
++                            .details-row(ng-repeat='field in clientFields')
++                                +form-row-custom(['col-sm-3'], ['col-sm-3'])
++                            div(bs-tabs style='margin-top: 0.65em')
++                                div(title='<img src="/images/xml.png" width="16px" height="16px"/> XML' bs-pane)
++                                    div(ui-ace='{ onLoad: aceInit, mode: "xml" }' ng-model='xmlClient')
++                                div(title='<img src="/images/java.png" width="16px" height="16px"/> Java' bs-pane)
++                                    .details-row
++                                        .col-sm-1
++                                            label Generate:
++                                        .col-sm-4
++                                            button.form-control(type='button' ng-model='backupItem.javaClassClient' bs-select data-placeholder='{{detail.placeholder}}' bs-options='item.value as item.label for item in javaClassItems' data-sort='false')
++                                    div(ui-ace='{ onLoad: aceInit, mode: "java" }' ng-model='javaClient')

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/a45a700c/modules/web-control-center/src/main/js/views/includes/header.jade
----------------------------------------------------------------------
diff --cc modules/web-control-center/src/main/js/views/includes/header.jade
index 0000000,ab2d31e..bfd5275
mode 000000,100644..100644
--- a/modules/web-control-center/src/main/js/views/includes/header.jade
+++ b/modules/web-control-center/src/main/js/views/includes/header.jade
@@@ -1,0 -1,39 +1,39 @@@
+ //-
+     Licensed to the Apache Software Foundation (ASF) under one or more
+     contributor license agreements.  See the NOTICE file distributed with
+     this work for additional information regarding copyright ownership.
+     The ASF licenses this file to You 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.
+ mixin header-item(active, ref, txt)
+     li
+         a(ng-class='{active: isActive("#{active}")}' href='#{ref}') #{txt}
+ 
+ header.header(id='header')
+     .viewedUser(ng-show='becomeUsed') Currently assuming "
+         strong {{user.username}}
+         | ",&nbsp;&nbsp;
+         a(href='/admin/become') revert to your identity.
+     .container
+         h1.navbar-brand
+             a(href='/') Apache Ignite Web Configurator
+         .navbar-collapse.collapse(ng-controller='auth')
+             ul.nav.navbar-nav(ng-controller='activeLink' ng-show='user')
+                 +header-item('/configuration', '/configuration/clusters', 'Configuration')
+                 //+header-item('/monitoring', '/monitoring', 'Monitoring')
 -                //+header-item('/sql', '/sql', 'SQL')
++                +header-item('/sql', '/sql', 'SQL')
+                 //+header-item('/deploy', '/deploy', 'Deploy')
+             ul.nav.navbar-nav.pull-right
+                 li(ng-if='user')
+                     a.dropdown-toggle(data-toggle='dropdown' bs-dropdown='userDropdown' data-placement='bottom-right') {{user.username}}
+                         span.caret
+                 li.nav-login(ng-if='!user')
+                     a(ng-click='login()' href='#') Log In

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/a45a700c/modules/web-control-center/src/main/js/views/sql.jade
----------------------------------------------------------------------
diff --cc modules/web-control-center/src/main/js/views/sql.jade
index 0000000,0000000..9ba3056
new file mode 100644
--- /dev/null
+++ b/modules/web-control-center/src/main/js/views/sql.jade
@@@ -1,0 -1,0 +1,70 @@@
++//-
++    Licensed to the Apache Software Foundation (ASF) under one or more
++    contributor license agreements.  See the NOTICE file distributed with
++    this work for additional information regarding copyright ownership.
++    The ASF licenses this file to You 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.
++extends ../templates/layout
++
++append scripts
++    script(src='/sql-controller.js')
++
++    script(src='//cdnjs.cloudflare.com/ajax/libs/ace/1.2.0/theme-chrome.js')
++    script(src='//cdnjs.cloudflare.com/ajax/libs/ace/1.2.0/mode-sql.js')
++    script(src='//cdnjs.cloudflare.com/ajax/libs/ace/1.2.0/ext-language_tools.js')
++
++block container
++    .row
++        .col-sm-12
++            .docs-content
++                .docs-header
++                    h1 Connect to Ignite and execute SQL queries
++                    hr
++                .docs-body(ng-controller='sqlController')
++                    .block-callout
++                        p(ng-bind-html='joinTip(screenTip)')
++                    .tabs-below(bs-tabs bs-active-pane='tabs.activeTab')
++                        div(title='Query #1' bs-pane)
++                            .row
++                                .col-sm-9
++                                    div(style='height: 200px' ui-ace='{ theme: "chrome", mode: "sql",' +
++                                        'require: ["ace/ext/language_tools"],' +
++                                        'rendererOptions: {showPrintMargin: false, highlightGutterLine: false, fontSize: 14},' +
++                                        'advanced: {enableSnippets: false, enableBasicAutocompletion: true, enableLiveAutocompletion: true}}'  ng-model='query')
++                                .col-sm-3
++                                    .links(ng-hide='caches.length == 0' style='margin-top: 0.65em')
++                                        lable.labelHeader Caches:
++                                        table(st-table='caches')
++                                            tbody
++                                                tr(ng-repeat='row in caches track by row._id')
++                                                    td.col-sm-6(ng-class='{active: row._id == selectedItem._id}')
++                                                        a(ng-click='selectItem(row)') {{$index + 1}}) {{row.name}}, {{row.mode | displayValue:modes:'Cache mode not set'}}
++                        div(title='Query #2' bs-pane)
++                            .row
++                    .settings-row
++                        button.btn.btn-primary(ng-click='') Explain
++                        button.btn.btn-primary(ng-click='') Execute
++                        button.btn.btn-primary(ng-click='' disabled) Scan
++                    //#resultPanel
++                    //    strong Results
++                    //
++                    //    #queryResult
++                    //        div(ng-repeat='r in results')
++                    //            .resultRow
++                    //                | {{r.id}} -> {{r.s}}
++                    //                span.props  {{r.fields}}
++
++                    div(ng-hide='results.length == 0' style='margin-top: 0.65em')
++                        lable.labelHeader Results:
++                        table.table-bordered(st-table='results')
++                            tbody
++                                tr(ng-repeat='row in results')
++                                    td
++                                        a(ng-click='selectItem(row)') {{$index + 1}}) {{row.name}}, {{row.mode | displayValue:modes:'Cache mode not set'}}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/a45a700c/modules/web-control-center/src/main/js/views/templates/layout.jade
----------------------------------------------------------------------
diff --cc modules/web-control-center/src/main/js/views/templates/layout.jade
index 0000000,a4191ae..8fbcd7e
mode 000000,100644..100644
--- a/modules/web-control-center/src/main/js/views/templates/layout.jade
+++ b/modules/web-control-center/src/main/js/views/templates/layout.jade
@@@ -1,0 -1,61 +1,61 @@@
+ //-
+     Licensed to the Apache Software Foundation (ASF) under one or more
+     contributor license agreements.  See the NOTICE file distributed with
+     this work for additional information regarding copyright ownership.
+     The ASF licenses this file to You 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.
+ 
+ doctype html
+ html(ng-app='ignite-web-control-center', ng-init='user = #{JSON.stringify(user)}; becomeUsed = #{becomeUsed}')
+     head
+         title= title
+ 
+         block css
+             // Bootstrap
+             link(rel='stylesheet', href='//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.4/css/bootstrap.css')
+ 
+             // Font Awesome Icons
+             link(rel='stylesheet', href='//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.css')
+ 
+             // Font
+             link(rel='stylesheet', href='//fonts.googleapis.com/css?family=Roboto+Slab:700:serif|Roboto+Slab:400:serif')
+ 
+             link(rel='stylesheet', href='/stylesheets/style.css')
+ 
+         block scripts
+             script(src='//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.js')
+ 
+             script(src='//cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.0/lodash.min.js')
+ 
+             script(src='//ajax.googleapis.com/ajax/libs/angularjs/1.4.2/angular.js')
+             script(src='//cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.2/angular-sanitize.js')
+             script(src='//cdnjs.cloudflare.com/ajax/libs/angular-strap/2.3.0/angular-strap.js')
+             script(src='//cdnjs.cloudflare.com/ajax/libs/angular-strap/2.3.0/angular-strap.tpl.min.js')
+ 
+             script(src='//cdnjs.cloudflare.com/ajax/libs/angular-smart-table/2.0.3/smart-table.js')
+ 
+             script(src='//cdnjs.cloudflare.com/ajax/libs/ace/1.2.0/ace.js')
+             script(src='//angular-ui.github.io/ui-ace/dist/ui-ace.min.js')
+ 
+             script(src='/common-module.js')
+             script(src='/data-structures.js')
+ 
 -    body.theme-line.body-overlap
++    body.theme-line.body-overlap.greedy
+         .wrapper
+             include ../includes/header
+ 
+             block main-container
+                 .container.body-container
+                     .main-content
+                         block container
+ 
+             include ../includes/footer

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/a45a700c/modules/web-control-center/src/main/js/views/templates/tab.jade
----------------------------------------------------------------------
diff --cc modules/web-control-center/src/main/js/views/templates/tab.jade
index 0000000,0000000..ce62bd6
new file mode 100644
--- /dev/null
+++ b/modules/web-control-center/src/main/js/views/templates/tab.jade
@@@ -1,0 -1,0 +1,3 @@@
++//
++   Created by nva on 23/07/15.
++