1
0

dfshealth.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. /**
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. (function () {
  19. "use strict";
  20. dust.loadSource(dust.compile($('#tmpl-dfshealth').html(), 'dfshealth'));
  21. dust.loadSource(dust.compile($('#tmpl-startup-progress').html(), 'startup-progress'));
  22. dust.loadSource(dust.compile($('#tmpl-datanode').html(), 'datanode-info'));
  23. dust.loadSource(dust.compile($('#tmpl-datanode-volume-failures').html(), 'datanode-volume-failures'));
  24. dust.loadSource(dust.compile($('#tmpl-snapshot').html(), 'snapshot-info'));
  25. $.fn.dataTable.ext.order['ng-value'] = function (settings, col)
  26. {
  27. return this.api().column(col, {order:'index'} ).nodes().map(function (td, i) {
  28. return $(td).attr('ng-value');
  29. });
  30. };
  31. function load_overview() {
  32. var BEANS = [
  33. {"name": "nn", "url": "/jmx?qry=Hadoop:service=NameNode,name=NameNodeInfo"},
  34. {"name": "nnstat", "url": "/jmx?qry=Hadoop:service=NameNode,name=NameNodeStatus"},
  35. {"name": "fs", "url": "/jmx?qry=Hadoop:service=NameNode,name=FSNamesystemState"},
  36. {"name": "fsn", "url": "/jmx?qry=Hadoop:service=NameNode,name=FSNamesystem"},
  37. {"name": "blockstats", "url": "/jmx?qry=Hadoop:service=NameNode,name=BlockStats"},
  38. {"name": "mem", "url": "/jmx?qry=java.lang:type=Memory"}
  39. ];
  40. var HELPERS = {
  41. 'helper_fs_max_objects': function (chunk, ctx, bodies, params) {
  42. var o = ctx.current();
  43. if (o.MaxObjects > 0) {
  44. chunk.write('(' + Math.round((o.FilesTotal + o.BlockTotal) / o.MaxObjects * 100) * 100 + ')%');
  45. }
  46. },
  47. 'helper_dir_status': function (chunk, ctx, bodies, params) {
  48. var j = ctx.current();
  49. for (var i in j) {
  50. chunk.write('<tr><td>' + i + '</td><td>' + j[i] + '</td><td>' + params.type + '</td></tr>');
  51. }
  52. },
  53. 'helper_date_tostring' : function (chunk, ctx, bodies, params) {
  54. var value = dust.helpers.tap(params.value, chunk, ctx);
  55. return chunk.write('' + moment(Number(value)).format('ddd MMM DD HH:mm:ss ZZ YYYY'));
  56. }
  57. };
  58. var data = {};
  59. $.ajax({'url': '/conf', 'dataType': 'xml', 'async': false}).done(
  60. function(d) {
  61. var $xml = $(d);
  62. var namespace, nnId;
  63. $xml.find('property').each(function(idx,v) {
  64. if ($(v).find('name').text() === 'dfs.nameservice.id') {
  65. namespace = $(v).find('value').text();
  66. }
  67. if ($(v).find('name').text() === 'dfs.ha.namenode.id') {
  68. nnId = $(v).find('value').text();
  69. }
  70. });
  71. if (namespace && nnId) {
  72. data['HAInfo'] = {"Namespace": namespace, "NamenodeID": nnId};
  73. }
  74. });
  75. // Workarounds for the fact that JMXJsonServlet returns non-standard JSON strings
  76. function workaround(nn) {
  77. nn.JournalTransactionInfo = JSON.parse(nn.JournalTransactionInfo);
  78. nn.NameJournalStatus = JSON.parse(nn.NameJournalStatus);
  79. nn.NameDirStatuses = JSON.parse(nn.NameDirStatuses);
  80. nn.NodeUsage = JSON.parse(nn.NodeUsage);
  81. nn.CorruptFiles = JSON.parse(nn.CorruptFiles);
  82. return nn;
  83. }
  84. load_json(
  85. BEANS,
  86. guard_with_startup_progress(function(d) {
  87. for (var k in d) {
  88. data[k] = k === 'nn' ? workaround(d[k].beans[0]) : d[k].beans[0];
  89. }
  90. var blockstats = data['blockstats'];
  91. for (var k in blockstats.StorageTypeStats) {
  92. var b = blockstats.StorageTypeStats[k].value;
  93. b.capacityUsedPercentage = b.capacityUsed * 100.0 / b.capacityTotal;
  94. b.capacityRemainingPercentage = b.capacityRemaining * 100.0 / b.capacityTotal;
  95. }
  96. data.fs.ObjectsTotal = data.fs.FilesTotal + data.fs.BlocksTotal;
  97. render();
  98. }),
  99. function (url, jqxhr, text, err) {
  100. show_err_msg('<p>Failed to retrieve data from ' + url + ', cause: ' + err + '</p>');
  101. });
  102. function render() {
  103. var base = dust.makeBase(HELPERS);
  104. dust.render('dfshealth', base.push(data), function(err, out) {
  105. $('#tab-overview').html(out);
  106. $('#ui-tabs a[href="#tab-overview"]').tab('show');
  107. });
  108. }
  109. }
  110. function show_err_msg(msg) {
  111. $('#alert-panel-body').html(msg);
  112. $('#alert-panel').show();
  113. }
  114. function ajax_error_handler(url, jqxhr, text, err) {
  115. show_err_msg('<p>Failed to retrieve data from ' + url + ', cause: ' + err + '</p>');
  116. }
  117. function guard_with_startup_progress(fn) {
  118. return function() {
  119. try {
  120. fn.apply(this, arguments);
  121. } catch (err) {
  122. if (err instanceof TypeError) {
  123. show_err_msg('NameNode is still loading. Redirecting to the Startup Progress page.');
  124. load_startup_progress();
  125. }
  126. }
  127. };
  128. }
  129. function load_startup_progress() {
  130. function workaround(r) {
  131. function rename_property(o, s, d) {
  132. if (o[s] !== undefined) {
  133. o[d] = o[s];
  134. delete o[s];
  135. }
  136. }
  137. r.percentComplete *= 100;
  138. $.each(r.phases, function (idx, p) {
  139. p.percentComplete *= 100;
  140. $.each(p.steps, function (idx2, s) {
  141. s.percentComplete *= 100;
  142. // dust.js is confused by these optional keys in nested
  143. // structure, rename them
  144. rename_property(s, "desc", "stepDesc");
  145. rename_property(s, "file", "stepFile");
  146. rename_property(s, "size", "stepSize");
  147. });
  148. });
  149. return r;
  150. }
  151. $.get('/startupProgress', function (resp) {
  152. var data = workaround(resp);
  153. dust.render('startup-progress', data, function(err, out) {
  154. $('#tab-startup-progress').html(out);
  155. $('#ui-tabs a[href="#tab-startup-progress"]').tab('show');
  156. });
  157. }).error(ajax_error_handler);
  158. }
  159. function load_datanode_info() {
  160. var HELPERS = {
  161. 'helper_relative_time' : function (chunk, ctx, bodies, params) {
  162. var value = dust.helpers.tap(params.value, chunk, ctx);
  163. return chunk.write(moment().subtract(Number(value), 'seconds').format('ddd MMM DD HH:mm:ss ZZ YYYY'));
  164. },
  165. 'helper_usage_bar' : function (chunk, ctx, bodies, params) {
  166. var value = dust.helpers.tap(params.value, chunk, ctx);
  167. var v = Number(value);
  168. var r = null;
  169. if (v < 70) {
  170. r = 'progress-bar-success';
  171. } else if (v < 85) {
  172. r = 'progress-bar-warning';
  173. } else {
  174. r = "progress-bar-danger";
  175. }
  176. return chunk.write(r);
  177. },
  178. };
  179. function workaround(r) {
  180. function node_map_to_array(nodes) {
  181. var res = [];
  182. for (var n in nodes) {
  183. var p = nodes[n];
  184. p.name = n;
  185. res.push(p);
  186. }
  187. return res;
  188. }
  189. function augment_live_nodes(nodes) {
  190. for (var i = 0, e = nodes.length; i < e; ++i) {
  191. var n = nodes[i];
  192. n.usedPercentage = Math.round((n.used + n.nonDfsUsedSpace) * 1.0 / n.capacity * 100);
  193. var addr = n.infoSecureAddr;
  194. var position = addr.lastIndexOf(":");
  195. var port = addr.substring(position + 1, addr.length);
  196. n.secureMode = "off";
  197. if (port != 0) {
  198. n.secureMode = "on";
  199. }
  200. if (n.adminState === "In Service") {
  201. n.state = "alive";
  202. } else if (nodes[i].adminState === "Decommission In Progress") {
  203. n.state = "decommisioning";
  204. } else if (nodes[i].adminState === "Decommissioned") {
  205. n.state = "decommissioned";
  206. }
  207. }
  208. }
  209. function augment_dead_nodes(nodes) {
  210. for (var i = 0, e = nodes.length; i < e; ++i) {
  211. if (nodes[i].decommissioned) {
  212. nodes[i].state = "down-decommissioned";
  213. } else {
  214. nodes[i].state = "down";
  215. }
  216. }
  217. }
  218. r.LiveNodes = node_map_to_array(JSON.parse(r.LiveNodes));
  219. augment_live_nodes(r.LiveNodes);
  220. r.DeadNodes = node_map_to_array(JSON.parse(r.DeadNodes));
  221. augment_dead_nodes(r.DeadNodes);
  222. r.DecomNodes = node_map_to_array(JSON.parse(r.DecomNodes));
  223. return r;
  224. }
  225. $.get(
  226. '/jmx?qry=Hadoop:service=NameNode,name=NameNodeInfo',
  227. guard_with_startup_progress(function (resp) {
  228. var data = workaround(resp.beans[0]);
  229. var base = dust.makeBase(HELPERS);
  230. dust.render('datanode-info', base.push(data), function(err, out) {
  231. $('#tab-datanode').html(out);
  232. $('#table-datanodes').dataTable( {
  233. 'lengthMenu': [ [25, 50, 100, -1], [25, 50, 100, "All"] ],
  234. 'columns': [
  235. { 'orderDataType': 'ng-value', 'searchable': true },
  236. { 'orderDataType': 'ng-value', 'searchable': true },
  237. { 'orderDataType': 'ng-value', 'type': 'numeric' },
  238. { 'orderDataType': 'ng-value', 'type': 'numeric' },
  239. { 'orderData': 3, 'type': 'numeric' },
  240. { 'orderDataType': 'ng-value', 'type': 'numeric'},
  241. { 'orderData': 5 }
  242. ]});
  243. $('#ui-tabs a[href="#tab-datanode"]').tab('show');
  244. });
  245. })).error(ajax_error_handler);
  246. }
  247. function load_datanode_volume_failures() {
  248. var HELPERS = {
  249. 'helper_date_tostring' : function (chunk, ctx, bodies, params) {
  250. var value = dust.helpers.tap(params.value, chunk, ctx);
  251. return chunk.write('' + moment(Number(value)).format('ddd MMM DD HH:mm:ss ZZ YYYY'));
  252. }
  253. };
  254. function workaround(r) {
  255. function node_map_to_array(nodes) {
  256. var res = [];
  257. for (var n in nodes) {
  258. var p = nodes[n];
  259. // Filter the display to only datanodes with volume failures.
  260. if (p.volfails > 0) {
  261. p.name = n;
  262. res.push(p);
  263. }
  264. }
  265. return res;
  266. }
  267. r.LiveNodes = node_map_to_array(JSON.parse(r.LiveNodes));
  268. return r;
  269. }
  270. $.get(
  271. '/jmx?qry=Hadoop:service=NameNode,name=NameNodeInfo',
  272. guard_with_startup_progress(function (resp) {
  273. var data = workaround(resp.beans[0]);
  274. var base = dust.makeBase(HELPERS);
  275. dust.render('datanode-volume-failures', base.push(data), function(err, out) {
  276. $('#tab-datanode-volume-failures').html(out);
  277. $('#ui-tabs a[href="#tab-datanode-volume-failures"]').tab('show');
  278. });
  279. })).error(ajax_error_handler);
  280. }
  281. function load_snapshot_info() {
  282. $.get(
  283. '/jmx?qry=Hadoop:service=NameNode,name=SnapshotInfo',
  284. guard_with_startup_progress(function (resp) {
  285. dust.render('snapshot-info', resp.beans[0], function(err, out) {
  286. $('#tab-snapshot').html(out);
  287. $('#ui-tabs a[href="#tab-snapshot"]').tab('show');
  288. });
  289. })).error(ajax_error_handler);
  290. }
  291. function load_page() {
  292. var hash = window.location.hash;
  293. switch(hash) {
  294. case "#tab-datanode":
  295. load_datanode_info();
  296. break;
  297. case "#tab-datanode-volume-failures":
  298. load_datanode_volume_failures();
  299. break;
  300. case "#tab-snapshot":
  301. load_snapshot_info();
  302. break;
  303. case "#tab-startup-progress":
  304. load_startup_progress();
  305. break;
  306. case "#tab-overview":
  307. load_overview();
  308. break;
  309. default:
  310. window.location.hash = "tab-overview";
  311. break;
  312. }
  313. }
  314. load_page();
  315. $(window).bind('hashchange', function () {
  316. load_page();
  317. });
  318. })();