heatmap_metric.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  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. var heatmap = require('utils/heatmap');
  20. /**
  21. * Base class for any heatmap metric.
  22. *
  23. * This class basically provides the following for each heatmap metric.
  24. * <ul>
  25. * <li> Provides number of slots in which temperature can fall.
  26. * <li> Maintains the maximum value so as to scale slot ranges.
  27. * <li> Gets JSON data from server and maps response for all hosts into above
  28. * slots.
  29. * </ul>
  30. *
  31. */
  32. App.MainChartHeatmapMetric = Em.Object.extend(heatmap.mappers, {
  33. /**
  34. * Name of this metric
  35. */
  36. name: null,
  37. /**
  38. * Number of slots this metric will be mapped into. When changing this value,
  39. * the color count in 'slotColors' should also be changed.
  40. */
  41. numberOfSlots: 5,
  42. /**
  43. * Colors for the each of the number of slots defined above. When changing the
  44. * number of slots, the number of colors also should be updated.
  45. *
  46. * @type {Array}
  47. */
  48. slotColors: [ {
  49. r: 0x00,
  50. g: 0xcc,
  51. b: 0x00
  52. }, // Green
  53. {
  54. r: 0x9f,
  55. g: 0xee,
  56. b: 0x00
  57. }, {
  58. r: 0xff,
  59. g: 0xff,
  60. b: 0x00
  61. }, // Yellow
  62. {
  63. r: 0xff,
  64. g: 0xc0,
  65. b: 0x00
  66. }, // Orange
  67. {
  68. r: 0xff,
  69. g: 0x00,
  70. b: 0x00
  71. } ],// Red
  72. /**
  73. * Minimum value of this metric. Default is 0.
  74. */
  75. minimumValue: 0,
  76. /**
  77. * Maximum value of this metric. This has to be specified by extending classes
  78. * so that the range from 'minimumValue' to 'maximumValue' can be split among
  79. * 'numberOfSlots'. It is recommended that this value be a multiple of
  80. * 'numberOfSlots'.
  81. */
  82. maximumValue: 100,
  83. /**
  84. * Units of the maximum value which is shown in UI {String}
  85. */
  86. units: '',
  87. /**
  88. * Indicates whether this metric is currently loading data from the server.
  89. * {Boolean}
  90. */
  91. loading: false,
  92. /**
  93. * Provides following information about slots in an array of objects.
  94. * <ul>
  95. * <li> from: {number} Slot starts from this value
  96. * <li> to: {number} Slot ends at this value (inclusive)
  97. * <li> label: {String} Slot name to be shown
  98. * <li> cssStyle: {String} style to be embedded on hosts which fall into this
  99. * slot.
  100. * </ul>
  101. *
  102. * Slot count will be the same as specified in 'numberOfSlots'. Slot
  103. * definitions will be given in increasing temperature from 'minimumValue' to
  104. * 'maximumValue'.
  105. *
  106. */
  107. slotDefinitions: function () {
  108. var min = this.get('minimumValue');
  109. var max = parseFloat(this.get('maximumValue'));
  110. var slotCount = this.get('numberOfSlots');
  111. var labelSuffix = this.get('slotDefinitionLabelSuffix');
  112. var delta = (max - min) / slotCount;
  113. var defs = [];
  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. var label;
  120. if ($.trim(labelSuffix) == 'ms') {
  121. label = date.timingFormat(from, 'zeroValid') + " - " + date.timingFormat(to, 'zeroValid');
  122. } else {
  123. label = from + labelSuffix + " - " + to + labelSuffix;
  124. }
  125. var slotColor = slotColors[slotColorIndex++];
  126. defs.push(Em.Object.create({
  127. from: from,
  128. to: to,
  129. label: label,
  130. cssStyle: "background-color:rgb(" + slotColor.r + "," + slotColor.g + "," + slotColor.b + ")"
  131. }));
  132. }
  133. from = this.formatLegendNumber((slotCount - 1) * delta);
  134. to = this.formatLegendNumber(max);
  135. if ($.trim(labelSuffix) == 'ms') {
  136. label = date.timingFormat(from, 'zeroValid') + " - " + date.timingFormat(to, 'zeroValid');
  137. } else {
  138. label = from + labelSuffix + " - " + to + labelSuffix;
  139. }
  140. slotColor = slotColors[slotColorIndex++];
  141. defs.push(Em.Object.create({
  142. from: from,
  143. to: to,
  144. label: label,
  145. cssStyle: "background-color:rgb(" + slotColor.r + "," + slotColor.g + "," + slotColor.b + ")"
  146. }));
  147. var hatchStyle = "background-color:rgb(135, 206, 250)";
  148. if(jQuery.browser.webkit){
  149. hatchStyle = "background-image:-webkit-repeating-linear-gradient(-45deg, #FF1E10, #FF1E10 3px, #ff6c00 3px, #ff6c00 6px)";
  150. }else if(jQuery.browser.mozilla){
  151. hatchStyle = "background-image:repeating-linear-gradient(-45deg, #FF1E10, #FF1E10 3px, #ff6c00 3px, #ff6c00 6px)";
  152. }else if(jQuery.browser.msie && jQuery.browser.version){
  153. var majorVersion = parseInt(jQuery.browser.version.split('.')[0]);
  154. if(majorVersion>9){
  155. hatchStyle = "background-image:repeating-linear-gradient(-45deg, #FF1E10, #FF1E10 3px, #ff6c00 3px, #ff6c00 6px)";
  156. }
  157. }
  158. defs.push(Em.Object.create({
  159. from: NaN,
  160. to: NaN,
  161. label: Em.I18n.t('charts.heatmap.label.invalidData'),
  162. cssStyle: hatchStyle
  163. }));
  164. defs.push(Em.Object.create({
  165. from: -1,
  166. to: -1,
  167. label: Em.I18n.t('charts.heatmap.label.notApplicable'),
  168. cssStyle: "background-color:rgb(200, 200, 200)"
  169. }));
  170. return defs;
  171. }.property('minimumValue', 'maximumValue', 'numberOfSlots'),
  172. /**
  173. * In slot definitions this value is used to construct the label by appending
  174. * it to slot min-max values. For example giving '%' here would result in slot
  175. * definition label being '0% - 10%'.
  176. */
  177. slotDefinitionLabelSuffix: '',
  178. defaultMetric: '',
  179. /**
  180. * Name in the <code>App.ajax</code>
  181. * @type {String}
  182. */
  183. ajaxIndex: 'hosts.metrics',
  184. /**
  185. * Additional data for ajax-request
  186. * May be redeclared in child-objects
  187. * @type {Object}
  188. */
  189. ajaxData: {},
  190. /**
  191. * Maps server JSON into an object where keys are hostnames and values are the
  192. * true metric values. This function by default will map 'defaultMetric' into
  193. * its corresponding value.
  194. *
  195. * @Function
  196. */
  197. metricMapper: function (json) {
  198. var hostToValueMap = {};
  199. var metricName = this.get('defaultMetric');
  200. if (json.items) {
  201. var props = metricName.split('.');
  202. json.items.forEach(function (item) {
  203. var value = item;
  204. props.forEach(function (prop) {
  205. if (value != null && prop in value) {
  206. value = value[prop];
  207. } else {
  208. value = null;
  209. }
  210. });
  211. if (value != null) {
  212. var hostName = item.Hosts.host_name;
  213. hostToValueMap[hostName] = value;
  214. }
  215. });
  216. }
  217. return hostToValueMap;
  218. },
  219. hostToValueMap: null,
  220. hostToSlotMap: function(){
  221. var hostToValueMap = this.get('hostToValueMap');
  222. var slotDefs = this.get('slotDefinitions');
  223. var hostNames = App.Host.find().mapProperty('hostName');
  224. var hostToSlotMap = {};
  225. if (hostToValueMap && hostNames) {
  226. hostNames.forEach(function(hostName){
  227. var slot = -1;
  228. if (hostName in hostToValueMap) {
  229. var value = hostToValueMap[hostName];
  230. if (isNaN(value)) {
  231. slot = slotDefs.length - 2;
  232. } else {
  233. for ( var slotIndex = 0; slotIndex < slotDefs.length - 2; slotIndex++) {
  234. var slotDef = slotDefs[slotIndex];
  235. if (value >= slotDef.from && value <= slotDef.to) {
  236. slot = slotIndex;
  237. }
  238. }
  239. if(slot < 0){
  240. // Assign it to the last legend
  241. slot = slotDefs.length - 3;
  242. }
  243. }
  244. } else {
  245. slot = slotDefs.length - 1;
  246. }
  247. if (slot > -1) {
  248. hostToSlotMap[hostName] = slot;
  249. }
  250. });
  251. }
  252. return hostToSlotMap;
  253. }.property('hostToValueMap', 'slotDefinitions'),
  254. /**
  255. * Determines which slot each host falls into. This information is given to
  256. * the callback's #map(hostnameToSlotObject) method. The
  257. * 'hostnameToSlotObject' has key as hostname, and the slot index as value.
  258. */
  259. refreshHostSlots: function () {
  260. this.set('loading', true);
  261. var fixedMetricName = this.get('defaultMetric');
  262. fixedMetricName = fixedMetricName.replace(/\./g, "/");
  263. var ajaxData = {
  264. metricName: fixedMetricName
  265. };
  266. jQuery.extend(ajaxData, this.get('ajaxData'));
  267. App.ajax.send({
  268. name: this.get('ajaxIndex'),
  269. sender: this,
  270. data: ajaxData,
  271. success: 'refreshHostSlotsSuccessCallback',
  272. error: 'refreshHostSlotsErrorCallback'
  273. });
  274. },
  275. refreshHostSlotsSuccessCallback: function (data) {
  276. var hostToValueMap = this.metricMapper(data);
  277. this.set('hostToValueMap', hostToValueMap);
  278. this.set('loading', false);
  279. },
  280. refreshHostSlotsErrorCallback: function () {
  281. this.set('loading', false);
  282. },
  283. /**
  284. * Turns numbers into displayable values. For example 24.345432425 into 24.3
  285. * etc.
  286. *
  287. * @private
  288. */
  289. formatLegendNumber: function (num) {
  290. var fraction = num % 1;
  291. if (fraction > 0) {
  292. return parseFloat(num.toFixed(1));
  293. }
  294. return num;
  295. }
  296. });