widget_mixin.js 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  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. var App = require('app');
  19. App.WidgetMixin = Ember.Mixin.create({
  20. /**
  21. * @type {RegExp}
  22. * @const
  23. */
  24. EXPRESSION_REGEX: /\$\{([\w\s\.\,\+\-\*\/\(\)\:\=\[\]]*)\}/g,
  25. /**
  26. * @type {RegExp}
  27. * @const
  28. */
  29. MATH_EXPRESSION_REGEX: /^[\d\s\+\-\*\/\(\)\.]+$/,
  30. /**
  31. * @type {RegExp}
  32. * @const
  33. */
  34. VALUE_NAME_REGEX: /[\w\.\,\:\=\[\]]+/g,
  35. /**
  36. * common metrics container
  37. * @type {Array}
  38. */
  39. metrics: [],
  40. /**
  41. * @type {boolean}
  42. */
  43. isLoaded: false,
  44. /**
  45. * @type {App.Widget}
  46. * @default null
  47. */
  48. content: null,
  49. beforeRender: function () {
  50. this.loadMetrics();
  51. },
  52. /**
  53. * load metrics
  54. */
  55. loadMetrics: function () {
  56. var requestData = this.getRequestData(this.get('content.metrics')),
  57. request,
  58. requestCounter = 0,
  59. self = this;
  60. for (var i in requestData) {
  61. request = requestData[i];
  62. requestCounter++;
  63. if (request.host_component_criteria) {
  64. this.getHostComponentMetrics(request).always(function () {
  65. requestCounter--;
  66. if (requestCounter === 0) self.onMetricsLoaded();
  67. });
  68. } else {
  69. this.getServiceComponentMetrics(request).complete(function () {
  70. requestCounter--;
  71. if (requestCounter === 0) self.onMetricsLoaded();
  72. });
  73. }
  74. }
  75. },
  76. /**
  77. * get data formatted for request
  78. * @param {Array} metrics
  79. */
  80. getRequestData: function (metrics) {
  81. var requestsData = {};
  82. if (metrics) {
  83. metrics.forEach(function (metric, index) {
  84. var key;
  85. if (metric.host_component_criteria) {
  86. this.tweakHostComponentCriteria(metric);
  87. key = metric.service_name + '_' + metric.component_name + '_' + metric.host_component_criteria;
  88. } else {
  89. key = metric.service_name + '_' + metric.component_name;
  90. }
  91. var requestMetric = $.extend({}, metric);
  92. if (requestsData[key]) {
  93. requestsData[key]["metric_paths"].push(requestMetric["metric_path"]);
  94. } else {
  95. requestMetric["metric_paths"] = [requestMetric["metric_path"]];
  96. delete requestMetric["metric_path"];
  97. requestsData[key] = requestMetric;
  98. }
  99. }, this);
  100. }
  101. return requestsData;
  102. },
  103. /**
  104. * Tweak necessary host component criteria
  105. * NameNode HA host component criteria is applicable only in HA mode
  106. */
  107. tweakHostComponentCriteria: function (metric) {
  108. switch (metric.component_name) {
  109. case 'NAMENODE':
  110. if (metric.host_component_criteria === 'host_components/metrics/dfs/FSNamesystem/HAState=active') {
  111. //if (metric.host_component_criteria)
  112. var hdfs = App.HDFSService.find().objectAt(0);
  113. var activeNNHostName = !hdfs.get('snameNode') && hdfs.get('activeNameNode');
  114. if (!activeNNHostName) {
  115. metric.host_component_criteria = 'host_components/HostRoles/component_name=NAMENODE';
  116. }
  117. }
  118. break;
  119. }
  120. },
  121. /**
  122. * make GET call to server in order to fetch service-component metrics
  123. * @param {object} request
  124. * @returns {$.ajax}
  125. */
  126. getServiceComponentMetrics: function (request) {
  127. return App.ajax.send({
  128. name: 'widgets.serviceComponent.metrics.get',
  129. sender: this,
  130. data: {
  131. serviceName: request.service_name,
  132. componentName: request.component_name,
  133. metricPaths: request.metric_paths.join(',')
  134. },
  135. success: 'getMetricsSuccessCallback'
  136. });
  137. },
  138. /**
  139. * make GET call to server in order to fetch service-component metrics
  140. * @param {object} request
  141. * @returns {$.Deferred}
  142. */
  143. getHostComponentMetrics: function (request) {
  144. var dfd;
  145. var self = this;
  146. dfd = $.Deferred();
  147. this.getHostComponentName(request).done(function (data) {
  148. if (data) {
  149. request.host_name = data.host_components[0].HostRoles.host_name;
  150. App.ajax.send({
  151. name: 'widgets.hostComponent.metrics.get',
  152. sender: self,
  153. data: {
  154. componentName: request.component_name,
  155. hostName: request.host_name,
  156. metricPaths: request.metric_paths.join(',')
  157. }
  158. }).done(function(metricData) {
  159. self.getMetricsSuccessCallback(metricData);
  160. dfd.resolve();
  161. }).fail(function(data){
  162. dfd.reject();
  163. });
  164. }
  165. }).fail(function(data){
  166. dfd.reject();
  167. });
  168. return dfd.promise();
  169. },
  170. /**
  171. * make GET call to server in order to fetch host-component names
  172. * @param {object} request
  173. * @returns {$.ajax}
  174. */
  175. getHostComponentName: function (request) {
  176. return App.ajax.send({
  177. name: 'widgets.hostComponent.get.hostName',
  178. sender: this,
  179. data: {
  180. serviceName: request.service_name,
  181. componentName: request.component_name,
  182. metricPaths: request.metric_paths.join(','),
  183. hostComponentCriteria: request.host_component_criteria
  184. }
  185. });
  186. },
  187. /**
  188. * callback on getting aggregated metrics and host component metrics
  189. * @param data
  190. */
  191. getMetricsSuccessCallback: function (data) {
  192. var metrics = [];
  193. this.get('content.metrics').forEach(function (_metric) {
  194. if (!Em.isNone(Em.get(data, _metric.metric_path.replace(/\//g, '.')))) {
  195. _metric.data = Em.get(data, _metric.metric_path.replace(/\//g, '.'));
  196. this.get('metrics').pushObject(_metric);
  197. }
  198. }, this);
  199. },
  200. /**
  201. * callback on metrics loaded
  202. */
  203. onMetricsLoaded: function () {
  204. var self = this;
  205. this.set('isLoaded', true);
  206. this.drawWidget();
  207. setTimeout(function () {
  208. self.loadMetrics();
  209. }, App.contentUpdateInterval);
  210. },
  211. /**
  212. * draw widget
  213. */
  214. drawWidget: function () {
  215. if (this.get('isLoaded')) {
  216. this.calculateValues();
  217. this.set('value', this.get('content.values')[0] && this.get('content.values')[0].computedValue);
  218. }
  219. },
  220. /**
  221. * calculate series datasets for graph widgets
  222. */
  223. calculateValues: function () {
  224. var metrics = this.get('metrics');
  225. var displayUnit = this.get('content.properties.display_unit');
  226. this.get('content.values').forEach(function (value) {
  227. var computeExpression = this.computeExpression(this.extractExpressions(value), metrics);
  228. value.computedValue = value.value.replace(this.get('EXPRESSION_REGEX'), function (match) {
  229. return (!Em.isNone(computeExpression[match])) ? computeExpression[match] + (displayUnit || "") : Em.I18n.t('common.na');
  230. });
  231. }, this);
  232. },
  233. /**
  234. * extract expressions
  235. * Example:
  236. * input: "${a/b} equal ${b+a}"
  237. * expressions: ['a/b', 'b+a']
  238. *
  239. * @param {object} input
  240. * @returns {Array}
  241. */
  242. extractExpressions: function (input) {
  243. var pattern = this.get('EXPRESSION_REGEX'),
  244. expressions = [],
  245. match;
  246. while (match = pattern.exec(input.value)) {
  247. expressions.push(match[1]);
  248. }
  249. return expressions;
  250. },
  251. /**
  252. * compute expression
  253. * @param expressions
  254. * @param metrics
  255. * @returns {object}
  256. */
  257. computeExpression: function (expressions, metrics) {
  258. var result = {};
  259. expressions.forEach(function (_expression) {
  260. var validExpression = true;
  261. var value = "";
  262. //replace values with metrics data
  263. var beforeCompute = _expression.replace(this.get('VALUE_NAME_REGEX'), function (match) {
  264. if (metrics.someProperty('name', match)) {
  265. return metrics.findProperty('name', match).data;
  266. } else {
  267. validExpression = false;
  268. console.warn('Metrics not found to compute expression');
  269. }
  270. });
  271. if (validExpression) {
  272. //check for correct math expression
  273. validExpression = this.get('MATH_EXPRESSION_REGEX').test(beforeCompute);
  274. !validExpression && console.warn('Value is not correct mathematical expression');
  275. }
  276. result['${' + _expression + '}'] = (validExpression) ? Number(window.eval(beforeCompute)).toString() : value;
  277. }, this);
  278. return result;
  279. },
  280. /*
  281. * make call when clicking on "remove icon" on widget
  282. */
  283. hideWidget: function () {
  284. },
  285. /*
  286. * make call when clicking on "clone icon" on widget
  287. */
  288. cloneWidget: function () {
  289. },
  290. /*
  291. * make call when clicking on "edit icon" on widget
  292. */
  293. editWidget: function () {
  294. }
  295. });
  296. App.WidgetPreviewMixin = Ember.Mixin.create({
  297. beforeRender: Em.K,
  298. isLoaded: true,
  299. metrics: [],
  300. content: Em.Object.create({
  301. widgetName: 'mock-widget',
  302. values: []
  303. }),
  304. drawWidget: function () {
  305. this.loadMetrics();
  306. this.get('content').setProperties({
  307. 'values': this.get('controller.widgetValues'),
  308. 'properties': this.get('controller.widgetProperties'),
  309. 'displayName': this.get('controller.widgetName')
  310. });
  311. this._super();
  312. }.observes('controller.widgetProperties', 'controller.widgetValues', 'controller.widgetMetrics', 'controller.widgetName'),
  313. loadMetrics: function () {
  314. var metrics = [];
  315. this.get('controller.widgetMetrics').forEach(function (metric) {
  316. metrics.push({
  317. name: metric.name,
  318. data: this.get('MOCK_VALUE')
  319. });
  320. }, this);
  321. this.set('metrics', metrics);
  322. }
  323. });