heatmap_metric.js 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. /**
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with this
  4. * work for additional information regarding copyright ownership. The ASF
  5. * licenses this file to you under the Apache License, Version 2.0 (the
  6. * "License"); you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  13. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  14. * License for the specific language governing permissions and limitations under
  15. * the License.
  16. */
  17. var App = require('app');
  18. var date = require('utils/date');
  19. /**
  20. * Base class for any heatmap metric.
  21. *
  22. * This class basically provides the following for each heatmap metric.
  23. * <ul>
  24. * <li> Provides number of slots in which temperature can fall.
  25. * <li> Maintains the maximum value so as to scale slot ranges.
  26. * <li> Gets JSON data from server and maps response for all hosts into above
  27. * slots.
  28. * </ul>
  29. *
  30. */
  31. App.MainChartHeatmapMetric = Em.Object.extend({
  32. /**
  33. * Name of this metric
  34. */
  35. name: null,
  36. /**
  37. * Number of slots this metric will be mapped into. When changing this value,
  38. * the color count in 'slotColors' should also be changed.
  39. */
  40. numberOfSlots: 5,
  41. /**
  42. * Colors for the each of the number of slots defined above. When changing the
  43. * number of slots, the number of colors also should be updated.
  44. *
  45. * @type {Array}
  46. */
  47. slotColors: [ {
  48. r: 0x00,
  49. g: 0xcc,
  50. b: 0x00
  51. }, // Green
  52. {
  53. r: 0x9f,
  54. g: 0xee,
  55. b: 0x00
  56. }, {
  57. r: 0xff,
  58. g: 0xff,
  59. b: 0x00
  60. }, // Yellow
  61. {
  62. r: 0xff,
  63. g: 0xc0,
  64. b: 0x00
  65. }, // Orange
  66. {
  67. r: 0xff,
  68. g: 0x00,
  69. b: 0x00
  70. } ],// Red
  71. /**
  72. * Minimum value of this metric. Default is 0.
  73. */
  74. minimumValue: 0,
  75. /**
  76. * Maximum value of this metric. This has to be specified by extending classes
  77. * so that the range from 'minimumValue' to 'maximumValue' can be split among
  78. * 'numberOfSlots'. It is recommended that this value be a multiple of
  79. * 'numberOfSlots'.
  80. */
  81. maximumValue: 100,
  82. /**
  83. * Units of the maximum value which is shown in UI {String}
  84. */
  85. units: '',
  86. /**
  87. * Indicates whether this metric is currently loading data from the server.
  88. * {Boolean}
  89. */
  90. loading: false,
  91. /**
  92. * Provides following information about slots in an array of objects.
  93. * <ul>
  94. * <li> from: {number} Slot starts from this value
  95. * <li> to: {number} Slot ends at this value (inclusive)
  96. * <li> label: {String} Slot name to be shown
  97. * <li> cssStyle: {String} style to be embedded on hosts which fall into this
  98. * slot.
  99. * </ul>
  100. *
  101. * Slot count will be the same as specified in 'numberOfSlots'. Slot
  102. * definitions will be given in increasing temperature from 'minimumValue' to
  103. * 'maximumValue'.
  104. *
  105. */
  106. slotDefinitions: function () {
  107. var min = this.get('minimumValue');
  108. var max = this.get('maximumValue');
  109. var slotCount = this.get('numberOfSlots');
  110. var labelSuffix = this.get('slotDefinitionLabelSuffix');
  111. var delta = (max - min) / slotCount;
  112. var defs = [];
  113. var fractions = max < 5;
  114. var slotColors = this.get('slotColors');
  115. var slotColorIndex = 0;
  116. for ( var c = 0; c < slotCount - 1; c++) {
  117. var from = this.formatLegendNumber(c * delta);
  118. var to = this.formatLegendNumber((c + 1) * delta);
  119. if ($.trim(labelSuffix) == 'ms') {
  120. var label = date.timingFormat(from) + " - " + date.timingFormat(to);
  121. } else {
  122. var label = from + labelSuffix + " - " + to + labelSuffix;
  123. }
  124. var slotColor = slotColors[slotColorIndex++];
  125. defs.push(Em.Object.create({
  126. from: from,
  127. to: to,
  128. label: label,
  129. cssStyle: "background-color:rgb(" + slotColor.r + "," + slotColor.g + "," + slotColor.b + ")"
  130. }));
  131. }
  132. from = this.formatLegendNumber((slotCount - 1) * delta);
  133. to = this.formatLegendNumber(max);
  134. if ($.trim(labelSuffix) == 'ms') {
  135. var label = date.timingFormat(from) + " - " + date.timingFormat(to);
  136. } else {
  137. var label = from + labelSuffix + " - " + to + labelSuffix;
  138. }
  139. slotColor = slotColors[slotColorIndex++];
  140. defs.push(Em.Object.create({
  141. from: from,
  142. to: to,
  143. label: label,
  144. cssStyle: "background-color:rgb(" + slotColor.r + "," + slotColor.g + "," + slotColor.b + ")"
  145. }));
  146. var hatchStyle = "background-color:rgb(135, 206, 250)";
  147. if(jQuery.browser.webkit){
  148. hatchStyle = "background-image:-webkit-repeating-linear-gradient(-45deg, #FF1E10, #FF1E10 3px, #ff6c00 3px, #ff6c00 6px)";
  149. }else if(jQuery.browser.mozilla){
  150. hatchStyle = "background-image:repeating-linear-gradient(-45deg, #FF1E10, #FF1E10 3px, #ff6c00 3px, #ff6c00 6px)";
  151. }else if(jQuery.browser.msie && jQuery.browser.version){
  152. var majorVersion = parseInt(jQuery.browser.version.split('.')[0]);
  153. if(majorVersion>9){
  154. hatchStyle = "background-image:repeating-linear-gradient(-45deg, #FF1E10, #FF1E10 3px, #ff6c00 3px, #ff6c00 6px)";
  155. }
  156. }
  157. defs.push(Em.Object.create({
  158. from: NaN,
  159. to: NaN,
  160. label: "Invalid data",
  161. cssStyle: hatchStyle
  162. }));
  163. defs.push(Em.Object.create({
  164. from: -1,
  165. to: -1,
  166. label: "Not Applicable",
  167. cssStyle: "background-color:rgb(200, 200, 200)"
  168. }));
  169. return defs;
  170. }.property('minimumValue', 'maximumValue', 'numberOfSlots'),
  171. /**
  172. * In slot definitions this value is used to construct the label by appending
  173. * it to slot min-max values. For example giving '%' here would result in slot
  174. * definition label being '0% - 10%'.
  175. */
  176. slotDefinitionLabelSuffix: '',
  177. /**
  178. * URL template from which metrics will be gotten for all hosts. The
  179. * {metricName} param will be replaced by the 'defaultMetric' value.
  180. */
  181. metricUrlTemplate: "/clusters/{clusterName}/hosts?fields={metricName}",
  182. /**
  183. * URL from which data for this metric can be gotten from. This should be
  184. * extended by classes to provide correct value.
  185. */
  186. metricUrl: function () {
  187. var clusterName = App.router.get('clusterController.clusterName');
  188. var fixedMetricName = this.get('defaultMetric');
  189. fixedMetricName = fixedMetricName.replace(/\./g, "/");
  190. return App.formatUrl(App.apiPrefix + this.get('metricUrlTemplate'), {
  191. clusterName: App.router.get('clusterController.clusterName'),
  192. metricName: fixedMetricName
  193. }, "/data/cluster_metrics/cpu_1hr.json");
  194. }.property('App.router.clusterController.clusterName', 'defaultMetric'),
  195. defaultMetric: '',
  196. /**
  197. * Maps server JSON into an object where keys are hostnames and values are the
  198. * true metric values. This function by default will map 'defaultMetric' into
  199. * its corresponding value.
  200. *
  201. * @type Function
  202. */
  203. metricMapper: function (json) {
  204. var hostToValueMap = {};
  205. var metricName = this.get('defaultMetric');
  206. if (json.items) {
  207. var props = metricName.split('.');
  208. json.items.forEach(function (item) {
  209. var value = item;
  210. props.forEach(function (prop) {
  211. if (value != null && prop in value) {
  212. value = value[prop];
  213. } else {
  214. value = null;
  215. }
  216. });
  217. if (value != null) {
  218. var hostName = item.Hosts.host_name;
  219. hostToValueMap[hostName] = value;
  220. }
  221. });
  222. }
  223. return hostToValueMap;
  224. },
  225. hostToValueMap: null,
  226. hostToSlotMap: function(){
  227. var hostToValueMap = this.get('hostToValueMap');
  228. var slotDefs = this.get('slotDefinitions');
  229. var allHosts = App.Host.find();
  230. var hostToSlotMap = {};
  231. if (hostToValueMap && allHosts) {
  232. allHosts.forEach(function(host, index, list){
  233. var slot = -1;
  234. var key = host.get('hostName');
  235. if (key in hostToValueMap) {
  236. var value = hostToValueMap[key];
  237. if (isNaN(value)) {
  238. slot = slotDefs.length - 2;
  239. } else {
  240. for ( var slotIndex = 0; slotIndex < slotDefs.length - 2; slotIndex++) {
  241. var slotDef = slotDefs[slotIndex];
  242. if (value >= slotDef.from && value <= slotDef.to) {
  243. slot = slotIndex;
  244. }
  245. }
  246. if(slot < 0){
  247. // Assign it to the last legend
  248. slot = slotDefs.length - 3;
  249. }
  250. }
  251. } else {
  252. slot = slotDefs.length - 1;
  253. }
  254. if (slot > -1) {
  255. hostToSlotMap[key] = slot;
  256. }
  257. });
  258. }
  259. return hostToSlotMap;
  260. }.property('hostToValueMap', 'slotDefinitions'),
  261. /**
  262. * Determines which slot each host falls into. This information is given to
  263. * the callback's #map(hostnameToSlotObject) method. The
  264. * 'hostnameToSlotObject' has key as hostname, and the slot index as value.
  265. */
  266. refreshHostSlots: function () {
  267. this.set('loading', true);
  268. jQuery.ajax({
  269. url: this.get('metricUrl'),
  270. dataType: 'json',
  271. error: jQuery.proxy(function () {
  272. this.set('loading', false);
  273. }, this),
  274. success: jQuery.proxy(function (data) {
  275. var hostToValueMap = this.metricMapper(data);
  276. this.set('hostToValueMap', hostToValueMap);
  277. this.set('loading', false);
  278. }, this)
  279. });
  280. }.observes('slotDefinitions'),
  281. /**
  282. * Turns numbers into displayable values. For example 24.345432425 into 24.3
  283. * etc.
  284. *
  285. * @private
  286. */
  287. formatLegendNumber: function (num) {
  288. var fraction = num % 1;
  289. if (fraction > 0) {
  290. return num.toFixed(1);
  291. }
  292. return num;
  293. }
  294. })