You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ak...@apache.org on 2015/05/27 18:31:59 UTC

incubator-ignite git commit: IGNITE-843: WIP on clusters and caches tables.

Repository: incubator-ignite
Updated Branches:
  refs/heads/ignite-843 9bbcbd22a -> db74657b0


IGNITE-843: WIP on clusters and caches tables.


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

Branch: refs/heads/ignite-843
Commit: db74657b0e505e88373dbd54b84601b86dad3aef
Parents: 9bbcbd2
Author: AKuznetsov <ak...@gridgain.com>
Authored: Wed May 27 23:30:39 2015 +0700
Committer: AKuznetsov <ak...@gridgain.com>
Committed: Wed May 27 23:30:39 2015 +0700

----------------------------------------------------------------------
 modules/webconfig/nodejs/app.js                 |   3 +
 modules/webconfig/nodejs/db.js                  |   1 +
 .../public/javascripts/controllers/caches.js    | 118 +++++++++++++++++++
 .../public/javascripts/controllers/clusters.js  |  81 +++++++------
 modules/webconfig/nodejs/routes/caches.js       |  63 ++++++++++
 modules/webconfig/nodejs/routes/pages.js        |   8 +-
 modules/webconfig/nodejs/views/caches.jade      |  62 +++++++++-
 modules/webconfig/nodejs/views/clusters.jade    |  20 ++--
 modules/webconfig/nodejs/views/multicast.jade   |  27 +++++
 modules/webconfig/nodejs/views/staticIps.jade   |  30 +++++
 .../views/tcpDiscoveryMulticastIpFinder.jade    |  27 -----
 .../nodejs/views/tcpDiscoveryVmIpFinder.jade    |  41 -------
 12 files changed, 360 insertions(+), 121 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/db74657b/modules/webconfig/nodejs/app.js
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/app.js b/modules/webconfig/nodejs/app.js
index c1c36d9..711b216 100644
--- a/modules/webconfig/nodejs/app.js
+++ b/modules/webconfig/nodejs/app.js
@@ -10,6 +10,7 @@ var mongoStore = require('connect-mongo')(session);
 
 var pageRoutes = require('./routes/pages');
 var clustersRouter = require('./routes/clusters');
+var cachesRouter = require('./routes/caches');
 var authRouter = require('./routes/auth');
 
 var passport = require('passport');
@@ -62,9 +63,11 @@ var mustAuthenticated = function (req, res, next) {
 };
 
 app.all('/clusters', mustAuthenticated);
+app.all('/caches', mustAuthenticated);
 
 app.use('/', pageRoutes);
 app.use('/rest/clusters', clustersRouter);
+app.use('/rest/caches', cachesRouter);
 app.use('/rest/auth', authRouter);
 
 // catch 404 and forward to error handler

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/db74657b/modules/webconfig/nodejs/db.js
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/db.js b/modules/webconfig/nodejs/db.js
index aa5b00d..7b8e3a8 100644
--- a/modules/webconfig/nodejs/db.js
+++ b/modules/webconfig/nodejs/db.js
@@ -42,6 +42,7 @@ exports.Cache =  mongoose.model('Cache', new Schema({
     name: String,
     mode: { type: String, enum: ['PARTITIONED', 'REPLICATED', 'LOCAL'] },
     backups: Number,
+    atomicity: { type: String, enum: ['ATOMIC', 'TRANSACTIONAL'] },
     clusters: [{ type: ObjectId, ref: 'Cluster' }]
 }));
 

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/db74657b/modules/webconfig/nodejs/public/javascripts/controllers/caches.js
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/public/javascripts/controllers/caches.js b/modules/webconfig/nodejs/public/javascripts/controllers/caches.js
new file mode 100644
index 0000000..482eae7
--- /dev/null
+++ b/modules/webconfig/nodejs/public/javascripts/controllers/caches.js
@@ -0,0 +1,118 @@
+// Controller for caches page.
+configuratorModule.controller('cachesController', ['$scope', '$modal', '$http', '$filter', 'ngTableParams',
+    function($scope, $modal, $http, $filter, ngTableParams) {
+        $scope.editColumn = {};
+
+        $scope.editCache = {};
+
+        $scope.modes = [
+            {value: 'PARTITIONED', label: 'PARTITIONED'},
+            {value: 'REPLICATED', label: 'REPLICATED'},
+            {value: 'LOCAL', label: 'LOCAL'}
+        ];
+
+        $scope.atomicities = [
+            {value: 'ATOMIC', label: 'ATOMIC'},
+            {value: 'TRANSACTIONAL', label: 'TRANSACTIONAL'}
+        ];
+
+        // When landing on the page, get caches and show them.
+        $http.get('/rest/caches')
+            .success(function(data) {
+                $scope.spaces = data.spaces;
+                $scope.caches = data.caches;
+
+                $scope.cachesTable = new ngTableParams({
+                    page: 1,                    // Show first page.
+                    count: Number.MAX_VALUE,    // Count per page.
+                    sorting: {name: 'asc'}      // Initial sorting.
+                }, {
+                    total: $scope.caches.length, // Length of data.
+                    counts: [],
+                    getData: function($defer, params) {
+                        // Use build-in angular filter.
+                        var orderedData = params.sorting() ?
+                            $filter('orderBy')($scope.caches, params.orderBy()) :
+                            $scope.caches;
+
+                        var page = params.page();
+                        var cnt = params.count();
+
+                        $defer.resolve(orderedData.slice(page - 1 * cnt, page * cnt));
+                    }
+                });
+            });
+
+        // Add new cache.
+        $scope.addCache = function() {
+            $scope.caches.push({space: $scope.spaces[0]._id, mode: 'PARTITIONED', backups: 1, atomicity: 'ATOMIC'});
+
+            $scope.cachesTable.reload();
+        };
+
+        $scope.beginEditCache = function(column, cache) {
+            $scope.revertCache();
+
+            $scope.currentCache = cache;
+
+            $scope.editColumn = column;
+
+            $scope.editCache = angular.copy(cache);
+        };
+
+        $scope.revertCache = function() {
+            if ($scope.editColumn && $scope.currentCache) {
+                $scope.caches[$scope.caches.indexOf($scope.currentCache)] = $scope.editCache;
+
+                $scope.currentCache = undefined;
+
+                $scope.editColumn = undefined;
+
+                $scope.cachesTable.reload();
+            }
+        };
+
+        $scope.submit = function() {
+            if ($scope.editColumn && $scope.currentCache) {
+                var cache = $scope.currentCache;
+
+                var data = {
+                    _id: cache._id,
+                    space: cache.space,
+                    name: cache.name,
+                    mode: cache.mode,
+                    backups: cache.backups,
+                    atomicity: cache.atomicity
+                };
+
+                $scope.currentCache = undefined;
+
+                $scope.editColumn = undefined;
+
+                $http.post('/rest/caches/save', data)
+                    .success(function(data) {
+                        $scope.spaces = data.spaces;
+                        $scope.caches = data.caches;
+
+                        $scope.cachesTable.reload();
+                    })
+                    .error(function(errorMessage) {
+                        console.log('Error: ' + errorMessage);
+                    });
+            }
+        };
+
+        $scope.deleteCache = function(cache) {
+            $http.post('/rest/caches/remove', {_id: cache._id})
+                .success(function(data) {
+                    $scope.spaces = data.spaces;
+                    $scope.caches = data.caches;
+
+                    $scope.cachesTable.reload();
+                })
+                .error(function(errorMessage) {
+                    console.log('Error: ' + errorMessage);
+                });
+        };
+    }]
+);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/db74657b/modules/webconfig/nodejs/public/javascripts/controllers/clusters.js
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/public/javascripts/controllers/clusters.js b/modules/webconfig/nodejs/public/javascripts/controllers/clusters.js
index 5a20a7e..858ccf6 100644
--- a/modules/webconfig/nodejs/public/javascripts/controllers/clusters.js
+++ b/modules/webconfig/nodejs/public/javascripts/controllers/clusters.js
@@ -47,59 +47,70 @@ configuratorModule.controller('clustersController', ['$scope', '$modal', '$http'
             });
 
         // Create popup for tcpDiscoveryVmIpFinder advanced settings.
-        var tcpDiscoveryVmIpFinderModal = $modal({scope: $scope, template: '/tcpDiscoveryVmIpFinder', show: false});
+        var staticIpsModal = $modal({scope: $scope, template: '/staticIps', show: false});
+
+        $scope.editStaticIps = function(cluster) {
+            $scope.staticIpsTable = new ngTableParams({
+                page: 1,                    // Show first page.
+                count: Number.MAX_VALUE     // Count per page.
+            }, {
+                total: cluster.addresses.length, // Length of data.
+                counts: [],
+                getData: function($defer, params) {
+                    var addresses = cluster.addresses;
+
+                    var page = params.page();
+                    var cnt = params.count();
+
+                    $defer.resolve(addresses.slice(page - 1 * cnt, page * cnt));
+                }
+            });
 
-        $scope.editTcpDiscoveryVmIpFinder = function(cluster) {
-            tcpDiscoveryVmIpFinderModal.$promise.then(tcpDiscoveryVmIpFinderModal.show);
+            staticIpsModal.$promise.then(staticIpsModal.show);
         };
 
-        $scope.tcpDiscoveryVmIpFinderTable = new ngTableParams({
-            page: 1,                    // Show first page.
-            count: Number.MAX_VALUE,    // Count per page.
-        }, {
-            total: $scope.editCluster.addresses.length, // Length of data.
-            counts: [],
-            getData: function($defer, params) {
-                var addresses = $scope.editCluster.addresses;
+        // Add new cluster.
+        $scope.addStaticIp = function(cluster) {
+            cluster.push({space: $scope.spaces[0]._id, discovery: 'TcpDiscoveryVmIpFinder'});
+
+            $scope.clustersTable.reload();
+        };
 
-                var page = params.page();
-                var cnt = params.count();
+        $scope.beginEditStaticIp = function(address) {
+            $scope.revertStaticIp();
 
-                $defer.resolve(addresses.slice(page - 1 * cnt, page * cnt));
-            }
-        });
+            $scope.editAddress = angular.copy(address);
+        };
 
         // Create popup for tcpDiscoveryMulticastIpFinder advanced settings.
-        var tcpDiscoveryMulticastIpFinder = $modal({scope: $scope, template: '/tcpDiscoveryMulticastIpFinder', show: false});
+        var multicastModal = $modal({scope: $scope, template: '/staticIps', show: false});
 
-        $scope.editTcpDiscoveryMulticastIpFinder = function(cluster) {
-            tcpDiscoveryMulticastIpFinderModal.$promise.then(tcpDiscoveryMulticastIpFinderModal.show);
+        $scope.editMulticast = function(cluster) {
+            multicastModal.$promise.then(multicastModal.show);
         };
 
         // Add new cluster.
-        $scope.add = function() {
+        $scope.addCluster = function() {
             $scope.clusters.push({space: $scope.spaces[0]._id, discovery: 'TcpDiscoveryVmIpFinder'});
 
             $scope.clustersTable.reload();
         };
 
-        $scope.beginEdit = function(name, cluster) {
-            $scope.revert();
+        $scope.beginEditCluster = function(column, cluster) {
+            $scope.revertCluster();
 
-            $scope.currentRow = cluster;
+            $scope.currentCluster = cluster;
 
-            $scope.editColumn = name;
+            $scope.editColumn = column;
 
             $scope.editCluster = angular.copy(cluster);
-
-            $scope.editIdx = $scope.clusters.indexOf(cluster);
         };
 
-        $scope.revert = function() {
-            if ($scope.editColumn && $scope.currentRow) {
-                $scope.clusters[$scope.clusters.indexOf($scope.currentRow)] = $scope.editCluster;
+        $scope.revertCluster = function() {
+            if ($scope.editColumn && $scope.currentCluster) {
+                $scope.clusters[$scope.clusters.indexOf($scope.currentCluster)] = $scope.editCluster;
 
-                $scope.currentRow = undefined;
+                $scope.currentCluster = undefined;
 
                 $scope.editColumn = undefined;
 
@@ -108,18 +119,18 @@ configuratorModule.controller('clustersController', ['$scope', '$modal', '$http'
         };
 
         $scope.submit = function() {
-            if ($scope.editColumn && $scope.currentRow) {
-                var cluster = $scope.currentRow;
+            if ($scope.editColumn && $scope.currentCluster) {
+                var cluster = $scope.currentCluster;
 
                 var data = {
                     _id: cluster._id,
                     space: cluster.space,
                     name: cluster.name,
                     discovery: cluster.discovery,
-                    addresses: ['127.0.0.1', '192.168.1.1']
+                    addresses: cluster.addresses
                 };
 
-                $scope.currentRow = undefined;
+                $scope.currentCluster = undefined;
 
                 $scope.editColumn = undefined;
 
@@ -136,7 +147,7 @@ configuratorModule.controller('clustersController', ['$scope', '$modal', '$http'
             }
         };
 
-        $scope.delete = function(cluster) {
+        $scope.deleteCluster = function(cluster) {
             $http.post('/rest/clusters/remove', {_id: cluster._id})
                 .success(function(data) {
                     $scope.spaces = data.spaces;

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/db74657b/modules/webconfig/nodejs/routes/caches.js
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/routes/caches.js b/modules/webconfig/nodejs/routes/caches.js
new file mode 100644
index 0000000..c4ca8df
--- /dev/null
+++ b/modules/webconfig/nodejs/routes/caches.js
@@ -0,0 +1,63 @@
+var router = require('express').Router();
+var db = require('../db');
+
+/**
+ * Send spaces and caches accessed for user account.
+ *
+ * @param req Request.
+ * @param res Response.
+ */
+function selectAll(req, res) {
+    var user_id = req.user._id;
+
+    // Get owned space and all accessed space.
+    db.Space.find({$or: [{owner: user_id}, {usedBy: {$elemMatch: {account: user_id}}}]}, function (err, spaces) {
+        if (err)
+            return res.status(500).send(err);
+
+        var space_ids = spaces.map(function(value, index) {
+            return value._id;
+        });
+
+        // Get all caches for spaces.
+        db.Cache.find({space: {$in: space_ids}}, function (err, caches) {
+            if (err)
+                return res.status(500).send(err);
+
+            res.json({spaces: spaces, caches: caches});
+        });
+    });
+}
+
+/**
+ * Get spaces and caches accessed for user account.
+ */
+router.get('/', function(req, res) {
+    selectAll(req, res);
+});
+
+/**
+ * Save cache.
+ */
+router.post('/save', function(req, res) {
+    db.upsert(db.Cache, req.body, function(err) {
+        if (err)
+            return res.status(500).send(err);
+
+        selectAll(req, res);
+    });
+});
+
+/**
+ * Remove cache by ._id.
+ */
+router.post('/remove', function(req, res) {
+    db.Cache.remove(req.body, function (err) {
+        if (err)
+            return res.send(err);
+
+        selectAll(req, res);
+    })
+});
+
+module.exports = router;
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/db74657b/modules/webconfig/nodejs/routes/pages.js
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/routes/pages.js b/modules/webconfig/nodejs/routes/pages.js
index 1cbcfd9..bd5f374 100644
--- a/modules/webconfig/nodejs/routes/pages.js
+++ b/modules/webconfig/nodejs/routes/pages.js
@@ -8,13 +8,13 @@ router.get('/login', function(req, res) {
 });
 
 /* GET advanced options for TcpDiscoveryVmIpFinder page. */
-router.get('/tcpDiscoveryVmIpFinder', function(req, res) {
-    res.render('tcpDiscoveryVmIpFinder');
+router.get('/staticIps', function(req, res) {
+    res.render('staticIps');
 });
 
 /* GET advanced options for TcpDiscoveryMulticastIpFinder page. */
-router.get('/tcpDiscoveryMulticastIpFinder', function(req, res) {
-    res.render('tcpDiscoveryMulticastIpFinder');
+router.get('/multicast', function(req, res) {
+    res.render('multicast');
 });
 
 /* GET register page. */

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/db74657b/modules/webconfig/nodejs/views/caches.jade
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/views/caches.jade b/modules/webconfig/nodejs/views/caches.jade
index 1852190..beedfd1 100644
--- a/modules/webconfig/nodejs/views/caches.jade
+++ b/modules/webconfig/nodejs/views/caches.jade
@@ -1,10 +1,64 @@
+// Page with caches configuration.
 extends layout-sidebar
-block head
+append css
+    link(rel='stylesheet', href='/stylesheets/ng-table.css')
+append scripts
+    script(src='/javascripts/controllers/caches.js')
 block content
     div.docs-header
         h1 Caches
         p Create and configure Ignite caches.
         hr
-    div.docs-body(ng-controller='cachesRouter')
-block body
-    script(src='/javascripts/controllers/caches.js')
\ No newline at end of file
+    div.docs-body(ng-controller='cachesController')
+        div.block-edit-parameters
+            div.btn-group
+                button(ng-click='addCache()' class=['btn', 'btn-default', 'fa', 'fa-plus'] ) &nbspAdd
+        table(ng-table="cachesTable" class=['table', 'table-bordered', 'table-hover'])
+            tr(ng-repeat="cache in $data")
+                td(data-title="'#'" class=['text-center', 'vcenter'] style='width: 50px') {{$index + 1}}
+                td(data-title="'Name'" sortable="'name'" class=['text-center', 'col-sm-2'])
+                    div(ng-if='!(editColumn == "name" && currentCache == cache)')
+                        span(ng-if='cache.name') {{cache.name}}
+                        span.pull-right(type='button' ng-click='beginEditCache("name", cache);')
+                            i(class=['fa', 'fa-pencil'])
+                    div.input-group(ng-if='editColumn == "name" && currentCache == cache')
+                        input.form-control(type='text' ng-model='cache.name')
+                        span.input-group-addon
+                            i(class=['fa', 'fa-repeat'] ng-click='revert();')
+                            i(class=['fa', 'fa-save'] ng-click='submit();' style='margin-left: 10px;')
+                td(data-title="'Mode'" sortable="'mode'" class=['text-center', 'col-sm-4'])
+                    div(ng-if='!(editColumn == "mode" && currentCache == cache)')
+                        span(ng-if='cache.mode') {{cache.mode}}
+                        span.pull-right(type='button')
+                            i(class=['fa', 'fa-pencil'] ng-click='beginEditCache("mode", cache);' style='margin-left: 10px;')
+                    div.input-group(ng-if='editColumn == "mode" && currentCache == cache')
+                        button(class=['btn', 'btn-default', 'form-control', 'pull-right'] style='width: 85%' data-placement='bottom-center' ng-model='cache.mode' data-template='/select' data-placeholder='Choose mode' bs-options='mode.value as mode.label for mode in modes' bs-select)
+                            span.caret
+                        span.input-group-addon
+                            i(class=['fa', 'fa-repeat'] ng-click='revertCache();')
+                            i(class=['fa', 'fa-save'] ng-click='submit();' style='margin-left: 10px;')
+                td(data-title="'Backups'" sortable="'backups'" class=['text-center', 'col-sm-1'])
+                    div(ng-if='!(editColumn == "backups" && currentCache == cache)')
+                        span(ng-if='cache.backups') {{cache.backups}}
+                        span.pull-right(type='button' ng-click='beginEditCache("backups", cache);')
+                            i(class=['fa', 'fa-pencil'])
+                    div.input-group(ng-if='editColumn == "name" && currentCache == cache')
+                        input.form-control(type='text' ng-model='cache.name')
+                        span.input-group-addon
+                            i(class=['fa', 'fa-repeat'] ng-click='revert();')
+                            i(class=['fa', 'fa-save'] ng-click='submit();' style='margin-left: 10px;')
+                td(data-title="'Atomicity'" sortable="'atomicity'" class=['text-center', 'col-sm-4'])
+                    div(ng-if='!(editColumn == "atomicity" && currentCache == cache)')
+                        span(ng-if='cache.atomicity') {{cache.atomicity}}
+                        span.pull-right(type='button')
+                            i(class=['fa', 'fa-pencil'] ng-click='beginEditCache("atomicity", cache);' style='margin-left: 10px;')
+                    div.input-group(ng-if='editColumn == "atomicity" && currentCache == cache')
+                        button(class=['btn', 'btn-default', 'form-control', 'pull-right'] style='width: 85%' data-placement='bottom-center' ng-model='cache.atomicity' data-template='/select' data-placeholder='Choose atomicity' bs-options='atomicity.value as atomicity.label for atomicity in atomicities' bs-select)
+                            span.caret
+                        span.input-group-addon
+                            i(class=['fa', 'fa-repeat'] ng-click='revertCache();')
+                            i(class=['fa', 'fa-save'] ng-click='submit();' style='margin-left: 10px;')
+                td.col-sm-1(data-title="'Delete'")
+                    center
+                        span(type='button' ng-click='deleteCache(Cache)')
+                            i(class=['fa', 'fa-remove'])        
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/db74657b/modules/webconfig/nodejs/views/clusters.jade
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/views/clusters.jade b/modules/webconfig/nodejs/views/clusters.jade
index 339ccc9..0732ac2 100644
--- a/modules/webconfig/nodejs/views/clusters.jade
+++ b/modules/webconfig/nodejs/views/clusters.jade
@@ -15,33 +15,33 @@ block content
     div.docs-body(ng-controller='clustersController')
         div.block-edit-parameters
             div.btn-group
-                button(ng-click='add()' class=['btn', 'btn-default', 'fa', 'fa-plus'] ) &nbspAdd
+                button(ng-click='addCluster()' class=['btn', 'btn-default', 'fa', 'fa-plus'] ) &nbspAdd
         table(ng-table="clustersTable" class=['table', 'table-bordered', 'table-hover'])
             tr(ng-repeat="cluster in $data")
                 td(data-title="'#'" class=['text-center', 'vcenter'] style='width: 50px') {{$index + 1}}
                 td(data-title="'Name'" sortable="'name'" class=['text-center', 'col-sm-7'])
-                    div(ng-if='!(editColumn == "name" && currentRow == cluster)')
+                    div(ng-if='!(editColumn == "name" && currentCluster == cluster)')
                         span(ng-if='cluster.name') {{cluster.name}}
-                        span.pull-right(type='button' ng-click='beginEdit("name", cluster);')
+                        span.pull-right(type='button' ng-click='beginEditCluster("name", cluster);')
                             i(class=['fa', 'fa-pencil'])
-                    div.input-group(ng-if='editColumn == "name" && currentRow == cluster')
+                    div.input-group(ng-if='editColumn == "name" && currentCluster == cluster')
                         input.form-control(type='text' ng-model='cluster.name')
                         span.input-group-addon
                             i(class=['fa', 'fa-repeat'] ng-click='revert();')
                             i(class=['fa', 'fa-save'] ng-click='submit();' style='margin-left: 10px;')
                 td.text-center(data-title="'Discovery'" sortable="'discovery'")
-                    div(ng-if='!(editColumn == "discovery" && currentRow == cluster)')
+                    div(ng-if='!(editColumn == "discovery" && currentCluster == cluster)')
                         span(ng-if='cluster.discovery') {{discoveryAsString(cluster.discovery)}}
                         span.pull-right(type='button')
-                            i(class=['fa', 'fa-caret-square-o-down'] ng-click='editTcpDiscoveryVmIpFinder(cluster);')
-                            i(class=['fa', 'fa-pencil'] ng-click='beginEdit("discovery", cluster);' style='margin-left: 10px;')
-                    div.input-group(ng-if='editColumn == "discovery" && currentRow == cluster')
+                            i(class=['fa', 'fa-caret-square-o-down'] ng-click='editStaticIps(cluster);')
+                            i(class=['fa', 'fa-pencil'] ng-click='beginEditCluster("discovery", cluster);' style='margin-left: 10px;')
+                    div.input-group(ng-if='editColumn == "discovery" && currentCluster == cluster')
                         button(class=['btn', 'btn-default', 'form-control', 'pull-right'] style='width: 85%' data-placement='bottom-center' ng-model='cluster.discovery' data-template='/select' data-placeholder='Choose discovery' bs-options='discovery.value as discovery.label for discovery in discoveries' bs-select)
                             span.caret
                         span.input-group-addon
-                            i(class=['fa', 'fa-repeat'] ng-click='revert();')
+                            i(class=['fa', 'fa-repeat'] ng-click='revertCluster();')
                             i(class=['fa', 'fa-save'] ng-click='submit();' style='margin-left: 10px;')
                 td.col-sm-1(data-title="'Delete'")
                     center
-                        span(type='button' ng-click='delete(cluster)')
+                        span(type='button' ng-click='deleteCluster(cluster)')
                             i(class=['fa', 'fa-remove'])
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/db74657b/modules/webconfig/nodejs/views/multicast.jade
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/views/multicast.jade b/modules/webconfig/nodejs/views/multicast.jade
new file mode 100644
index 0000000..475aa95
--- /dev/null
+++ b/modules/webconfig/nodejs/views/multicast.jade
@@ -0,0 +1,27 @@
+div(class=['modal', 'center'] tabindex='-1' role='dialog')
+    .modal-dialog
+        .modal-content
+            .modal-header
+                button.close(type='button', ng-click='$hide()', aria-hidden='true') &times;
+                h4.modal-title Configure TcpDiscoveryMulticastIpFinder
+            .modal-body
+                div.block-edit-parameters
+                    div.btn-group
+                        button(ng-click='add()' class=['btn', 'btn-default', 'fa', 'fa-plus'] ) &nbspAdd
+                    table(ng-table="clustersTable" class=['table', 'table-bordered', 'table-hover'])
+                form.form-horizontal(name='discoveryForm')
+                    .modal-body.row
+                        div(ng-show='errorMessage')
+                            p.text-center.error-message {{errorMessage}}
+                        .col-xs-10.login.col-xs-offset-1
+                            div.form-group
+                                label.col-sm-3.control-label IP Address
+                                .controls.col-sm-9
+                                    input.form-control(type='text', ng-model='discovery.ip', placeholder='127.0.0.0', focus-me='true', required)
+                            div.form-group
+                                label.col-sm-3.control-label Ports range
+                                .controls.col-sm-9
+                                    input.form-control(type='text', ng-model='discovery.ports', placeholder='47500..47509', required)
+                    .modal-footer
+                        button.btn.btn-primary(ng-click='saveDiscovery(disco_info)' ng-disabled='discoveryForm.$invalid') Save
+                        button.btn.btn-primary(ng-click='$hide()') Cancel

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/db74657b/modules/webconfig/nodejs/views/staticIps.jade
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/views/staticIps.jade b/modules/webconfig/nodejs/views/staticIps.jade
new file mode 100644
index 0000000..46a4241
--- /dev/null
+++ b/modules/webconfig/nodejs/views/staticIps.jade
@@ -0,0 +1,30 @@
+div(class=['modal', 'center'] tabindex='-1' role='dialog')
+    .modal-dialog
+        .modal-content
+            .modal-header
+                button.close(type='button', ng-click='$hide()', aria-hidden='true') &times;
+                h4.modal-title Configure Static IPs
+            .modal-body
+                div.block-edit-parameters
+                    div.btn-group
+                        button(ng-click='add()' class=['btn', 'btn-default', 'fa', 'fa-plus'] ) &nbspAdd
+                table(ng-table="tcpDiscoveryVmIpFinderTable" class=['table', 'table-bordered', 'table-hover'])
+                    tr(ng-repeat="address in $data")
+                        td(data-title="'#'" class=['text-center', 'vcenter'] style='width: 50px') {{$index + 1}}
+                        td.text-center(data-title="'IP Address with ports range'")
+                            div(ng-if='!(editColumn == "ip" && currentRow == cluster)')
+                                span(ng-if='cluster.name') {{'127.0.0.1:47500..47509'}}
+                                span.pull-right(type='button' ng-click='beginEditStaticIps(cluster);')
+                                    i(class=['fa', 'fa-pencil'])
+                            div.input-group(ng-if='editColumn == "name" && currentRow == cluster')
+                                input.form-control(type='text' ng-model='cluster.name')
+                                span.input-group-addon
+                                    i(class=['fa', 'fa-repeat'] ng-click='revert();')
+                                    i(class=['fa', 'fa-save'] ng-click='submit();' style='margin-left: 10px;')
+                        td.col-sm-1(data-title="'Delete'")
+                            center
+                                span(type='button' ng-click='delete(cluster)')
+                                    i(class=['fa', 'fa-remove'])
+            .modal-footer
+                button.btn.btn-primary(ng-click='saveDiscovery(disco_info)' ng-disabled='discoveryForm.$invalid') Save
+                button.btn.btn-primary(ng-click='$hide()') Cancel

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/db74657b/modules/webconfig/nodejs/views/tcpDiscoveryMulticastIpFinder.jade
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/views/tcpDiscoveryMulticastIpFinder.jade b/modules/webconfig/nodejs/views/tcpDiscoveryMulticastIpFinder.jade
deleted file mode 100644
index 475aa95..0000000
--- a/modules/webconfig/nodejs/views/tcpDiscoveryMulticastIpFinder.jade
+++ /dev/null
@@ -1,27 +0,0 @@
-div(class=['modal', 'center'] tabindex='-1' role='dialog')
-    .modal-dialog
-        .modal-content
-            .modal-header
-                button.close(type='button', ng-click='$hide()', aria-hidden='true') &times;
-                h4.modal-title Configure TcpDiscoveryMulticastIpFinder
-            .modal-body
-                div.block-edit-parameters
-                    div.btn-group
-                        button(ng-click='add()' class=['btn', 'btn-default', 'fa', 'fa-plus'] ) &nbspAdd
-                    table(ng-table="clustersTable" class=['table', 'table-bordered', 'table-hover'])
-                form.form-horizontal(name='discoveryForm')
-                    .modal-body.row
-                        div(ng-show='errorMessage')
-                            p.text-center.error-message {{errorMessage}}
-                        .col-xs-10.login.col-xs-offset-1
-                            div.form-group
-                                label.col-sm-3.control-label IP Address
-                                .controls.col-sm-9
-                                    input.form-control(type='text', ng-model='discovery.ip', placeholder='127.0.0.0', focus-me='true', required)
-                            div.form-group
-                                label.col-sm-3.control-label Ports range
-                                .controls.col-sm-9
-                                    input.form-control(type='text', ng-model='discovery.ports', placeholder='47500..47509', required)
-                    .modal-footer
-                        button.btn.btn-primary(ng-click='saveDiscovery(disco_info)' ng-disabled='discoveryForm.$invalid') Save
-                        button.btn.btn-primary(ng-click='$hide()') Cancel

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/db74657b/modules/webconfig/nodejs/views/tcpDiscoveryVmIpFinder.jade
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/views/tcpDiscoveryVmIpFinder.jade b/modules/webconfig/nodejs/views/tcpDiscoveryVmIpFinder.jade
deleted file mode 100644
index ac15848..0000000
--- a/modules/webconfig/nodejs/views/tcpDiscoveryVmIpFinder.jade
+++ /dev/null
@@ -1,41 +0,0 @@
-div(class=['modal', 'center'] tabindex='-1' role='dialog')
-    .modal-dialog
-        .modal-content
-            .modal-header
-                button.close(type='button', ng-click='$hide()', aria-hidden='true') &times;
-                h4.modal-title Configure Static IPs
-            .modal-body
-                div.block-edit-parameters
-                    div.btn-group
-                        button(ng-click='add()' class=['btn', 'btn-default', 'fa', 'fa-plus'] ) &nbspAdd
-                table(ng-table="tcpDiscoveryVmIpFinderTable" class=['table', 'table-bordered', 'table-hover'])
-                    tr(ng-repeat="cluster in $data")
-                        td(data-title="'#'" class=['text-center', 'vcenter'] style='width: 50px') {{$index + 1}}
-                        td(data-title="'IP Address'" sortable="'name'" class=['text-center', 'col-sm-5'])
-                            div(ng-if='!(editColumn == "name" && currentRow == cluster)')
-                                span(ng-if='cluster.name') {{'127.0.0.1'}}
-                                span.pull-right(type='button' ng-click='beginEdit("name", cluster);')
-                                    i(class=['fa', 'fa-pencil'])
-                            div.input-group(ng-if='editColumn == "name" && currentRow == cluster')
-                                input.form-control(type='text' ng-model='cluster.name')
-                                span.input-group-addon
-                                    i(class=['fa', 'fa-repeat'] ng-click='revert();')
-                                    i(class=['fa', 'fa-save'] ng-click='submit();' style='margin-left: 10px;')
-                        td(data-title="'Ports range'" sortable="'discovery'" class=['text-center', 'col-sm-5'])
-                            div(ng-if='!(editColumn == "discovery" && currentRow == cluster)')
-                                span(ng-if='cluster.discovery') {{'47500..47509'}}
-                                span.pull-right(type='button')
-                                    i(class=['fa', 'fa-pencil'] ng-click='beginEdit("discovery", cluster);')
-                            div.input-group(ng-if='editColumn == "discovery" && currentRow == cluster')
-                                button(class=['btn', 'btn-default', 'form-control', 'pull-right'] style='width: 85%' data-placement='bottom-center' ng-model='cluster.discovery' data-template='/select' data-placeholder='Choose discovery' bs-options='discovery.value as discovery.label for discovery in discoveries' bs-select)
-                                    span.caret
-                                span.input-group-addon
-                                    i(class=['fa', 'fa-repeat'] ng-click='revert();')
-                                    i(class=['fa', 'fa-save'] ng-click='submit();' style='margin-left: 10px;')
-                        td.col-sm-1(data-title="'Delete'")
-                            center
-                                span(type='button' ng-click='delete(cluster)')
-                                    i(class=['fa', 'fa-remove'])
-            .modal-footer
-                button.btn.btn-primary(ng-click='saveDiscovery(disco_info)' ng-disabled='discoveryForm.$invalid') Save
-                button.btn.btn-primary(ng-click='$hide()') Cancel