alert_config.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649
  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. var validator = require('utils/validator');
  20. var numericUtils = require('utils/number_utils');
  21. App.AlertConfigProperty = Ember.Object.extend({
  22. /**
  23. * label to be shown for config property
  24. * @type {String}
  25. */
  26. label: '',
  27. /**
  28. * PORT|METRIC|AGGREGATE
  29. * @type {String}
  30. */
  31. type: '',
  32. /**
  33. * config property value
  34. * @type {*}
  35. */
  36. value: null,
  37. /**
  38. * property value cache to realise undo function
  39. * @type {*}
  40. */
  41. previousValue: null,
  42. /**
  43. * define either input is disabled or enabled
  44. * @type {Boolean}
  45. */
  46. isDisabled: false,
  47. /**
  48. * options that Select list will have
  49. * @type {Array}
  50. */
  51. options: [],
  52. /**
  53. * input displayType
  54. * one of 'textFields', 'textArea', 'select' or 'threshold'
  55. * @type {String}
  56. */
  57. displayType: '',
  58. /**
  59. * unit to be shown with value
  60. * @type {String}
  61. */
  62. unit: null,
  63. /**
  64. * space separated list of css class names to use
  65. * @type {String}
  66. */
  67. classNames: '',
  68. /**
  69. * define whether row with property should be shifted right
  70. * @type {Boolean}
  71. */
  72. isShifted: false,
  73. /**
  74. * name or names of properties related to config
  75. * may be either string for one property or array of strings for multiple properties
  76. * if this property is array, then <code>apiFormattedValue</code> should also be an array
  77. * example: <code>apiProperty[0]</code> relates to <code>apiFormattedValue[0]</code>
  78. * @type {String|Array}
  79. */
  80. apiProperty: '',
  81. /**
  82. * for some metrics properties may be set true or false
  83. * depending on what property is related to (JMX or Ganglia)
  84. */
  85. isJMXMetric: null,
  86. /**
  87. * define place to show label
  88. * if true - label is shown before input
  89. * if false - label is shown after input
  90. * @type {Boolean}
  91. */
  92. isPreLabeled: Em.computed.notExistsIn('displayType', ['radioButton']),
  93. /**
  94. * value converted to appropriate format for sending to server
  95. * should be computed property
  96. * should be defined in child class
  97. * @type {*}
  98. */
  99. apiFormattedValue: Em.computed.alias('value'),
  100. /**
  101. * define if property was changed by user
  102. * @type {Boolean}
  103. */
  104. wasChanged: function () {
  105. return this.get('previousValue') !== null && this.get('value') !== this.get('previousValue');
  106. }.property('value', 'previousValue'),
  107. /**
  108. * view class according to <code>displayType</code>
  109. * @type {Em.View}
  110. */
  111. viewClass: function () {
  112. var displayType = this.get('displayType');
  113. switch (displayType) {
  114. case 'textField':
  115. return App.AlertConfigTextFieldView;
  116. case 'textArea':
  117. return App.AlertConfigTextAreaView;
  118. case 'select':
  119. return App.AlertConfigSelectView;
  120. case 'threshold':
  121. return App.AlertConfigThresholdView;
  122. case 'radioButton':
  123. return App.AlertConfigRadioButtonView;
  124. case 'parameter':
  125. return App.AlertConfigParameterView;
  126. default:
  127. }
  128. }.property('displayType'),
  129. /**
  130. * Define whether property is valid
  131. * Computed property
  132. * Should be defined in child class
  133. * @type {Boolean}
  134. */
  135. isValid: function () {
  136. return true;
  137. }.property()
  138. });
  139. App.AlertConfigProperties = {
  140. AlertName: App.AlertConfigProperty.extend({
  141. name: 'alert_name',
  142. label: 'Alert Name',
  143. displayType: 'textField',
  144. classNames: 'alert-text-input',
  145. apiProperty: 'name'
  146. }),
  147. AlertNameSelected: App.AlertConfigProperty.extend({
  148. name: 'alert_name',
  149. label: 'Alert Name',
  150. displayType: 'select',
  151. apiProperty: 'name'
  152. }),
  153. ServiceAlertType: App.AlertConfigProperty.extend({
  154. name: 'alert_type_service',
  155. label: 'Service Alert Definition',
  156. displayType: 'radioButton',
  157. group: 'alert_type'
  158. }),
  159. HostAlertType: App.AlertConfigProperty.extend({
  160. name: 'alert_type_host',
  161. label: 'Host Alert Definition',
  162. displayType: 'radioButton',
  163. group: 'alert_type'
  164. }),
  165. Service: App.AlertConfigProperty.extend({
  166. name: 'service',
  167. label: 'Service',
  168. displayType: 'select',
  169. apiProperty: 'service_name',
  170. apiFormattedValue: function () {
  171. return App.StackService.find().findProperty('displayName', this.get('value')).get('serviceName');
  172. }.property('value')
  173. }),
  174. Component: App.AlertConfigProperty.extend({
  175. name: 'component',
  176. label: 'Component',
  177. displayType: 'select',
  178. apiProperty: 'component_name',
  179. apiFormattedValue: function () {
  180. return App.StackServiceComponent.find().findProperty('displayName', this.get('value')).get('componentName');
  181. }.property('value')
  182. }),
  183. Scope: App.AlertConfigProperty.extend({
  184. name: 'scope',
  185. label: 'Scope',
  186. displayType: 'select',
  187. apiProperty: 'scope',
  188. apiFormattedValue: function () {
  189. return this.get('value').toUpperCase();
  190. }.property('value')
  191. }),
  192. Description: App.AlertConfigProperty.extend({
  193. name: 'description',
  194. label: 'Description',
  195. displayType: 'textArea',
  196. classNames: 'alert-config-text-area',
  197. // todo: check value after API will be provided
  198. apiProperty: 'description'
  199. }),
  200. Interval: App.AlertConfigProperty.extend({
  201. name: 'interval',
  202. label: 'Check Interval',
  203. displayType: 'textField',
  204. unit: 'Minute',
  205. classNames: 'alert-interval-input',
  206. apiProperty: 'interval',
  207. isValid: function () {
  208. var value = this.get('value');
  209. if (!value) return false;
  210. return String(value) === String(parseInt(value, 10)) && value >= 1;
  211. }.property('value')
  212. }),
  213. /**
  214. * Implements threshold
  215. * Main difference from other alertConfigProperties:
  216. * it has two editable parts - <code>value</code> and <code>text</code>
  217. * User may configure it to edit only one of them (use flags <code>showInputForValue</code> and <code>showInputForText</code>)
  218. * This flags also determines update value and text in the API-request or not (see <code>App.AlertConfigProperties.Thresholds</code> for more examples)
  219. *
  220. * @type {App.AlertConfigProperty.Threshold}
  221. */
  222. Threshold: App.AlertConfigProperty.extend({
  223. name: 'threshold',
  224. /**
  225. * Property text cache to realise undo function
  226. * @type {*}
  227. */
  228. previousText: null,
  229. label: '',
  230. /**
  231. * OK|WARNING|CRITICAL
  232. * @type {string}
  233. */
  234. badge: '',
  235. /**
  236. * threshold-value
  237. * @type {string}
  238. */
  239. value: '',
  240. /**
  241. * Type of value. This will be a fixed set of types (like %).
  242. */
  243. valueMetric: null,
  244. /**
  245. * Value actually displayed to the user. This value is transformed
  246. * based on the limited types of 'valueMetric's. Mappings from
  247. * 'value' to 'displayValue' is handled by observers.
  248. */
  249. displayValue: '',
  250. /**
  251. * threshold-text
  252. * @type {string}
  253. */
  254. text: '',
  255. displayType: 'threshold',
  256. classNames: 'alert-thresholds-input',
  257. apiProperty: [],
  258. init: function () {
  259. this.set('displayValue', this.getNewValue());
  260. this._super();
  261. },
  262. /**
  263. * @type {string[]}
  264. */
  265. apiFormattedValue: function () {
  266. var ret = [];
  267. if (this.get('showInputForValue')) {
  268. ret.push(this.get('value'));
  269. }
  270. if (this.get('showInputForText')) {
  271. ret.push(this.get('text'));
  272. }
  273. return ret;
  274. }.property('value', 'text', 'showInputForValue', 'showInputForText'),
  275. /**
  276. * Determines if <code>value</code> should be visible and editable (if not - won't update API-value)
  277. * @type {bool}
  278. */
  279. showInputForValue: true,
  280. /**
  281. * Determines if <code>text</code> should be visible and editable (if not - won't update API-text)
  282. * @type {bool}
  283. */
  284. showInputForText: true,
  285. /**
  286. * Custom css-class for different badges
  287. * type {string}
  288. */
  289. badgeCssClass: Em.computed.format('alert-state-{0}', 'badge'),
  290. /**
  291. * Determines if <code>value</code> or <code>text</code> were changed
  292. * @type {bool}
  293. */
  294. wasChanged: function () {
  295. return this.get('previousValue') !== null && this.get('value') !== this.get('previousValue') ||
  296. this.get('previousText') !== null && this.get('text') !== this.get('previousText');
  297. }.property('value', 'text', 'previousValue', 'previousText'),
  298. /**
  299. * May be redefined in child-models, mixins etc
  300. * @method getValue
  301. * @returns {string}
  302. */
  303. getNewValue: function () {
  304. return this.get('value');
  305. },
  306. valueWasChanged: function () {
  307. var displayValue = this.get('displayValue');
  308. var newDisplayValue = this.getNewValue();
  309. if (newDisplayValue !== displayValue && !(isNaN(newDisplayValue) ||isNaN(displayValue))) {
  310. this.set('displayValue', newDisplayValue);
  311. }
  312. }.observes('value'),
  313. /**
  314. * May be redefined in child-models, mixins etc
  315. * @method getDisplayValue
  316. * @returns {string}
  317. */
  318. getNewDisplayValue: function () {
  319. return this.get('displayValue');
  320. },
  321. displayValueWasChanged: function () {
  322. var value = this.get('value');
  323. var newValue = this.getNewDisplayValue();
  324. if (newValue !== value && !(isNaN(newValue) ||isNaN(value))) {
  325. this.set('value', newValue);
  326. }
  327. }.observes('displayValue'),
  328. /**
  329. * Check if <code>displayValue</code> is valid float number
  330. * If this value isn't shown (see <code>showInputForValue</code>), result is always true
  331. * @return {boolean}
  332. */
  333. isValid: function () {
  334. if (!this.get('showInputForValue')) {
  335. return true;
  336. }
  337. var value = this.get('displayValue');
  338. if (Em.isNone(value)) {
  339. return false;
  340. }
  341. value = ('' + value).trim();
  342. //only allow 1/10th of a second
  343. if (numericUtils.getFloatDecimals(value) > 1) {
  344. return false;
  345. }
  346. return validator.isValidFloat(value);
  347. }.property('displayValue', 'showInputForValue')
  348. }),
  349. URI: App.AlertConfigProperty.extend({
  350. name: 'uri',
  351. label: 'URI',
  352. displayType: 'textField',
  353. classNames: 'alert-text-input',
  354. apiProperty: 'source.uri'
  355. }),
  356. URIExtended: App.AlertConfigProperty.extend({
  357. name: 'uri',
  358. label: 'URI',
  359. displayType: 'textArea',
  360. classNames: 'alert-config-text-area',
  361. apiProperty: 'source.uri',
  362. apiFormattedValue: function () {
  363. var result = {};
  364. try {
  365. result = JSON.parse(this.get('value'));
  366. } catch (e) {
  367. console.error('Wrong format of URI');
  368. }
  369. return result;
  370. }.property('value')
  371. }),
  372. DefaultPort: App.AlertConfigProperty.extend({
  373. name: 'default_port',
  374. label: 'Default Port',
  375. displayType: 'textField',
  376. classNames: 'alert-port-input',
  377. apiProperty: 'source.default_port'
  378. }),
  379. Path: App.AlertConfigProperty.extend({
  380. name: 'path',
  381. label: 'Path',
  382. displayType: 'textField',
  383. classNames: 'alert-text-input',
  384. apiProperty: 'source.path'
  385. }),
  386. Metrics: App.AlertConfigProperty.extend({
  387. name: 'metrics',
  388. label: 'JMX/Ganglia Metrics',
  389. displayType: 'textArea',
  390. classNames: 'alert-config-text-area',
  391. apiProperty: Em.computed.ifThenElse('isJMXMetric', 'source.jmx.property_list', 'source.ganglia.property_list'),
  392. apiFormattedValue: function () {
  393. return this.get('value').split(',\n');
  394. }.property('value')
  395. }),
  396. FormatString: App.AlertConfigProperty.extend({
  397. name: 'metrics_string',
  398. label: 'Format String',
  399. displayType: 'textArea',
  400. classNames: 'alert-config-text-area',
  401. apiProperty: Em.computed.ifThenElse('isJMXMetric', 'source.jmx.value', 'source.ganglia.value')
  402. }),
  403. Parameter: App.AlertConfigProperty.extend({
  404. name: 'parameter',
  405. displayType: 'parameter',
  406. badge: Em.computed.alias('threshold'),
  407. thresholdExists: Em.computed.bool('threshold'),
  408. thresholdNotExists: Em.computed.empty('threshold'),
  409. /**
  410. * Custom css-class for different badges
  411. * type {string}
  412. */
  413. badgeCssClass: Em.computed.format('alert-state-{0}', 'badge'),
  414. })
  415. };
  416. App.AlertConfigProperties.Parameters = {
  417. StringMixin: Em.Mixin.create({
  418. isValid: function () {
  419. var value = this.get('value');
  420. return String(value).trim() !== '';
  421. }.property('value')
  422. }),
  423. NumericMixin: Em.Mixin.create({
  424. isValid: function () {
  425. var value = this.get('value');
  426. if (!value) {
  427. return false;
  428. }
  429. value = ('' + value).trim();
  430. if (!numericUtils.isPositiveNumber(value)) {
  431. return false;
  432. }
  433. value = parseFloat(value);
  434. return !isNaN(value);
  435. }.property('value')
  436. }),
  437. PercentageMixin: Em.Mixin.create({
  438. isValid: function () {
  439. var value = this.get('value');
  440. if (!value) {
  441. return false;
  442. }
  443. if (!validator.isValidFloat(value) || !numericUtils.isPositiveNumber(value)) {
  444. return false;
  445. }
  446. value = String(value).trim();
  447. value = parseFloat(value);
  448. return !isNaN(value) && value > 0;
  449. }.property('value')
  450. })
  451. };
  452. App.AlertConfigProperties.Thresholds = {
  453. OkThreshold: App.AlertConfigProperties.Threshold.extend({
  454. badge: 'OK',
  455. name: 'ok_threshold',
  456. apiProperty: function () {
  457. var ret = [];
  458. if (this.get('showInputForValue')) {
  459. ret.push('source.reporting.ok.value');
  460. }
  461. if (this.get('showInputForText')) {
  462. ret.push('source.reporting.ok.text');
  463. }
  464. return ret;
  465. }.property('showInputForValue', 'showInputForText')
  466. }),
  467. WarningThreshold: App.AlertConfigProperties.Threshold.extend({
  468. badge: 'WARNING',
  469. name: 'warning_threshold',
  470. apiProperty: function () {
  471. var ret = [];
  472. if (this.get('showInputForValue')) {
  473. ret.push('source.reporting.warning.value');
  474. }
  475. if (this.get('showInputForText')) {
  476. ret.push('source.reporting.warning.text');
  477. }
  478. return ret;
  479. }.property('showInputForValue', 'showInputForText')
  480. }),
  481. CriticalThreshold: App.AlertConfigProperties.Threshold.extend({
  482. badge: 'CRITICAL',
  483. name: 'critical_threshold',
  484. apiProperty: function () {
  485. var ret = [];
  486. if (this.get('showInputForValue')) {
  487. ret.push('source.reporting.critical.value');
  488. }
  489. if (this.get('showInputForText')) {
  490. ret.push('source.reporting.critical.text');
  491. }
  492. return ret;
  493. }.property('showInputForValue', 'showInputForText')
  494. }),
  495. /**
  496. * Mixin for <code>App.AlertConfigProperties.Threshold</code>
  497. * Used to validate values in percentage range (0..1]
  498. * @type {Em.Mixin}
  499. */
  500. PercentageMixin: Em.Mixin.create({
  501. isValid: function () {
  502. var value = this.get('displayValue');
  503. if (!value) {
  504. return false;
  505. }
  506. value = ('' + value).trim();
  507. if (numericUtils.getFloatDecimals(value)) {
  508. return false;
  509. }
  510. value = parseFloat(value);
  511. //do not allow float values
  512. if (parseInt(value, 10) !== value) {
  513. return false;
  514. }
  515. return this.get('showInputForValue') ? !isNaN(value) && value > 0 : true;
  516. }.property('displayValue', 'showInputForValue')
  517. }),
  518. /**
  519. * Mixin for <code>App.AlertConfigProperties.Threshold</code>
  520. * Used to validate values that should be greater than 0
  521. * @type {Em.Mixin}
  522. */
  523. PositiveMixin: Em.Mixin.create({
  524. isValid: function () {
  525. if (!this.get('showInputForValue')) {
  526. return true;
  527. }
  528. var value = this.get('displayValue');
  529. if (!value) {
  530. return false;
  531. }
  532. //only allow 1/10th of a second
  533. if (numericUtils.getFloatDecimals(value) > 1) {
  534. return false;
  535. }
  536. value = ('' + value).trim();
  537. value = parseFloat(value);
  538. return !isNaN(value) && value > 0;
  539. }.property('displayValue', 'showInputForValue')
  540. })
  541. };