5896c53b1f881fa1b97b8eac63cb8bb0f020bbf5
[ccsdk/features.git] /
1 /**
2  * Copyright 2010-2013 Ben Birch
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this software except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 (function( $, app, i18n ) {
17
18         var ui = app.ns("ui");
19         var services = app.ns("services");
20
21         // ( master ) master = true, data = true 
22         // ( coordinator ) master = true, data = false
23         // ( worker ) master = false, data = true;
24         // ( client ) master = false, data = false;
25         // http enabled ?
26
27         function nodeSort_name(a, b) {
28                 if (!(a.cluster && b.cluster)) {
29                         return 0;
30                 }
31                 return a.cluster.name.toString().localeCompare( b.cluster.name.toString() );
32         }
33
34         function nodeSort_addr( a, b ) {
35                 if (!(a.cluster && b.cluster)) {
36                         return 0;
37                 }
38                 return a.cluster.transport_address.toString().localeCompare( b.cluster.transport_address.toString() );
39         }
40
41         function nodeSort_type( a, b ) {
42                 if (!(a.cluster && b.cluster)) {
43                         return 0;
44                 }
45                 if( a.master_node ) {
46                         return -1;
47                 } else if( b.master_node ) {
48                         return 1;
49                 } else if( a.data_node && !b.data_node ) {
50                         return -1;
51                 } else if( b.data_node && !a.data_node ) {
52                         return 1;
53                 } else {
54                         return a.cluster.name.toString().localeCompare( b.cluster.name.toString() );
55                 }
56         }
57
58         var NODE_SORT_TYPES = {
59                 "Sort.ByName": nodeSort_name,
60                 "Sort.ByAddress": nodeSort_addr,
61                 "Sort.ByType": nodeSort_type
62         };
63
64         function nodeFilter_none( a ) {
65                 return true;
66         }
67
68         function nodeFilter_clients( a ) {
69                 return (a.master_node || a.data_node );
70         }
71
72
73         ui.ClusterOverview = ui.Page.extend({
74                 defaults: {
75                         cluster: null // (reqired) an instanceof app.services.Cluster
76                 },
77                 init: function() {
78                         this._super();
79                         this.cluster = this.config.cluster;
80                         this.prefs = services.Preferences.instance();
81                         this._clusterState = this.config.clusterState;
82                         this._clusterState.on("data", this.draw_handler );
83                         this._refreshButton = new ui.RefreshButton({
84                                 onRefresh: this.refresh.bind(this),
85                                 onChange: function( btn ) {
86                                         if( btn.value === -1 ) {
87                                                 this.draw_handler();
88                                         }
89                                 }.bind( this )
90                         });
91                         var nodeSortPref = this.prefs.get("clusterOverview-nodeSort") || Object.keys(NODE_SORT_TYPES)[0];
92                         this._nodeSort = NODE_SORT_TYPES[ nodeSortPref ];
93                         this._nodeSortMenu = new ui.MenuButton({
94                                 label: i18n.text( "Preference.SortCluster" ),
95                                 menu: new ui.SelectMenuPanel({
96                                         value: nodeSortPref,
97                                         items: Object.keys( NODE_SORT_TYPES ).map( function( k ) {
98                                                 return { text: i18n.text( k ), value: k };
99                                         }),
100                                         onSelect: function( panel, event ) {
101                                                 this._nodeSort = NODE_SORT_TYPES[ event.value ];
102                                                 this.prefs.set("clusterOverview-nodeSort", event.value );
103                                                 this.draw_handler();
104                                         }.bind(this)
105                                 })
106                         });
107                         this._indicesSort = this.prefs.get( "clusterOverview-indicesSort") || "desc";
108                         this._indicesSortMenu = new ui.MenuButton({
109                                 label: i18n.text( "Preference.SortIndices" ),
110                                 menu: new ui.SelectMenuPanel({
111                                         value: this._indicesSort,
112                                         items: [
113                                                 { value: "desc", text: i18n.text( "SortIndices.Descending" ) },
114                                                 { value: "asc", text: i18n.text( "SortIndices.Ascending" ) } ],
115                                         onSelect: function( panel, event ) {
116                                                 this._indicesSort = event.value;
117                                                 this.prefs.set( "clusterOverview-indicesSort", this._indicesSort );
118                                                 this.draw_handler();
119                                         }.bind(this)
120                                 })
121                         });
122                         this._aliasRenderer = this.prefs.get( "clusterOverview-aliasRender" ) || "full";
123                         this._aliasMenu = new ui.MenuButton({
124                                 label: i18n.text( "Preference.ViewAliases" ),
125                                 menu: new ui.SelectMenuPanel({
126                                         value: this._aliasRenderer,
127                                         items: [
128                                                 { value: "full", text: i18n.text( "ViewAliases.Grouped" ) },
129                                                 { value: "list", text: i18n.text( "ViewAliases.List" ) },
130                                                 { value: "none", text: i18n.text( "ViewAliases.None" ) } ],
131                                         onSelect: function( panel, event ) {
132                                                 this._aliasRenderer = event.value;
133                                                 this.prefs.set( "clusterOverview-aliasRender", this._aliasRenderer );
134                                                 this.draw_handler();
135                                         }.bind(this)
136                                 })
137                         });
138                         this._indexFilter = new ui.TextField({
139                                 value: this.prefs.get("clusterOverview-indexFilter"),
140                                 placeholder: i18n.text( "Overview.IndexFilter" ),
141                                 onchange: function( indexFilter ) {
142                                         this.prefs.set("clusterOverview-indexFilter", indexFilter.val() );
143                                         this.draw_handler();
144                                 }.bind(this)
145                         });
146                         this.el = $(this._main_template());
147                         this.tablEl = this.el.find(".uiClusterOverview-table");
148                         this.refresh();
149                 },
150                 remove: function() {
151                         this._clusterState.removeObserver( "data", this.draw_handler );
152                 },
153                 refresh: function() {
154                         this._refreshButton.disable();
155                         this._clusterState.refresh();
156                 },
157                 draw_handler: function() {
158                         var data = this._clusterState;
159                         var indexFilter;
160                         try {
161                                 var indexFilterRe = new RegExp( this._indexFilter.val() );
162                                 indexFilter = function(s) { return indexFilterRe.test(s); };
163                         } catch(e) {
164                                 indexFilter = function() { return true; };
165                         }
166                         var clusterState = data.clusterState;
167                         var status = data.status;
168                         var nodeStats = data.nodeStats;
169                         var clusterNodes = data.clusterNodes;
170                         var nodes = [];
171                         var indices = [];
172                         var cluster = {};
173                         var nodeIndices = {};
174                         var indexIndices = {}, indexIndicesIndex = 0;
175                         function newNode(n) {
176                                 return {
177                                         name: n,
178                                         routings: [],
179                                         master_node: clusterState.master_node === n
180                                 };
181                         }
182                         function newIndex(i) {
183                                 return {
184                                         name: i,
185                                         replicas: []
186                                 };
187                         }
188                         function getIndexForNode(n) {
189                                 return nodeIndices[n] = (n in nodeIndices) ? nodeIndices[n] : nodes.push(newNode(n)) - 1;
190                         }
191                         function getIndexForIndex(routings, i) {
192                                 var index = indexIndices[i] = (i in indexIndices) ?
193                                                 (routings[indexIndices[i]] = routings[indexIndices[i]] || newIndex(i)) && indexIndices[i]
194                                                 : ( ( routings[indexIndicesIndex] = newIndex(i) )  && indexIndicesIndex++ );
195                                 indices[index] = i;
196                                 return index;
197                         }
198                         $.each(clusterNodes.nodes, function(name, node) {
199                                 getIndexForNode(name);
200                         });
201
202                         var indexNames = [];
203                         $.each(clusterState.routing_table.indices, function(name, index){
204                                 indexNames.push(name);
205                         });
206                         indexNames.sort();
207                         if (this._indicesSort === "desc") indexNames.reverse();
208                         indexNames.filter( indexFilter ).forEach(function(name) {
209                                 var indexObject = clusterState.routing_table.indices[name];
210                                 $.each(indexObject.shards, function(name, shard) {
211                                         shard.forEach(function(replica){
212                                                 var node = replica.node;
213                                                 if(node === null) { node = "Unassigned"; }
214                                                 var index = replica.index;
215                                                 var shard = replica.shard;
216                                                 var routings = nodes[getIndexForNode(node)].routings;
217                                                 var indexIndex = getIndexForIndex(routings, index);
218                                                 var replicas = routings[indexIndex].replicas;
219                                                 if(node === "Unassigned" || !indexObject.shards[shard]) {
220                                                         replicas.push({ replica: replica });
221                                                 } else {
222                                                         replicas[shard] = {
223                                                                 replica: replica,
224                                                                 status: indexObject.shards[shard].filter(function(replica) {
225                                                                         return replica.node === node;
226                                                                 })[0]
227                                                         };
228                                                 }
229                                         });
230                                 });
231                         });
232                         indices = indices.map(function(index){
233                                 return {
234                                         name: index,
235                                         state: "open",
236                                         metadata: clusterState.metadata.indices[index],
237                                         status: status.indices[index]
238                                 };
239                         }, this);
240                         $.each(clusterState.metadata.indices, function(name, index) {
241                                 if(index.state === "close" && indexFilter( name )) {
242                                         indices.push({
243                                                 name: name,
244                                                 state: "close",
245                                                 metadata: index,
246                                                 status: null
247                                         });
248                                 }
249                         });
250                         nodes.forEach(function(node) {
251                                 node.stats = nodeStats.nodes[node.name];
252                                 var cluster = clusterNodes.nodes[node.name];
253                                 node.cluster = cluster || { name: "<unknown>" };
254                                 node.data_node = !( cluster && cluster.attributes && cluster.attributes.data === "false" );
255                                 for(var i = 0; i < indices.length; i++) {
256                                         node.routings[i] = node.routings[i] || { name: indices[i].name, replicas: [] };
257                                         node.routings[i].max_number_of_shards = indices[i].metadata.settings["index.number_of_shards"];
258                                         node.routings[i].open = indices[i].state === "open";
259                                 }
260                         });
261                         var aliasesIndex = {};
262                         var aliases = [];
263                         var indexClone = indices.map(function() { return false; });
264                         $.each(clusterState.metadata.indices, function(name, index) {
265                                 index.aliases.forEach(function(alias) {
266                                         var aliasIndex = aliasesIndex[alias] = (alias in aliasesIndex) ? aliasesIndex[alias] : aliases.push( { name: alias, max: -1, min: 999, indices: [].concat(indexClone) }) - 1;
267                                         var indexIndex = indexIndices[name];
268                                         var aliasRow = aliases[aliasIndex];
269                                         aliasRow.min = Math.min(aliasRow.min, indexIndex);
270                                         aliasRow.max = Math.max(aliasRow.max, indexIndex);
271                                         aliasRow.indices[indexIndex] = indices[indexIndex];
272                                 });
273                         });
274                         cluster.aliases = aliases;
275                         cluster.nodes = nodes
276                                 .filter( nodeFilter_none )
277                                 .sort( this._nodeSort );
278                         indices.unshift({ name: null });
279                         this._drawNodesView( cluster, indices );
280                         this._refreshButton.enable();
281                 },
282                 _drawNodesView: function( cluster, indices ) {
283                         this._nodesView && this._nodesView.remove();
284                         this._nodesView = new ui.NodesView({
285                                 onRedraw: function() {
286                                         this.refresh();
287                                 }.bind(this),
288                                 interactive: ( this._refreshButton.value === -1 ),
289                                 aliasRenderer: this._aliasRenderer,
290                                 cluster: this.cluster,
291                                 data: {
292                                         cluster: cluster,
293                                         indices: indices
294                                 }
295                         });
296                         this._nodesView.attach( this.tablEl );
297                 },
298                 _main_template: function() {
299                         return { tag: "DIV", id: this.id(), cls: "uiClusterOverview", children: [
300                                 new ui.Toolbar({
301                                         label: i18n.text("Overview.PageTitle"),
302                                         left: [
303                                                 this._nodeSortMenu,
304                                                 this._indicesSortMenu,
305                                                 this._aliasMenu,
306                                                 this._indexFilter
307                                         ],
308                                         right: [
309                                                 this._refreshButton
310                                         ]
311                                 }),
312                                 { tag: "DIV", cls: "uiClusterOverview-table" }
313                         ] };
314                 }
315         });
316
317 })( this.jQuery, this.app, this.i18n );