controls_view.js 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066
  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. /**
  20. * Abstract view for config fields.
  21. * Add popover support to control
  22. */
  23. App.ServiceConfigPopoverSupport = Ember.Mixin.create({
  24. /**
  25. * Config object. It will instance of App.ServiceConfigProperty
  26. */
  27. serviceConfig: null,
  28. attributeBindings:['readOnly'],
  29. isPopoverEnabled: true,
  30. didInsertElement: function () {
  31. $('body').tooltip({
  32. selector: '[data-toggle=tooltip]',
  33. placement: 'top'
  34. });
  35. // if description for this serviceConfig not exist, then no need to show popover
  36. if (this.get('isPopoverEnabled') !== 'false' && this.get('serviceConfig.description')) {
  37. App.popover(this.$(), {
  38. title: Em.I18n.t('installer.controls.serviceConfigPopover.title').format(
  39. this.get('serviceConfig.displayName'),
  40. (this.get('serviceConfig.displayName') == this.get('serviceConfig.name')) ? '' : this.get('serviceConfig.name')
  41. ),
  42. content: this.get('serviceConfig.description'),
  43. placement: 'right',
  44. trigger: 'hover'
  45. });
  46. }
  47. },
  48. willDestroyElement: function() {
  49. this.$().popover('destroy');
  50. },
  51. readOnly: function () {
  52. return !this.get('serviceConfig.isEditable');
  53. }.property('serviceConfig.isEditable')
  54. });
  55. /**
  56. * if config value contains &|<|>|"|'
  57. * input field converts it to &|<|>|"|'
  58. * this mixin helps to aviod such convertation and show values as is.
  59. */
  60. App.SkipXmlEscapingSupport = Ember.Mixin.create({
  61. didInsertElement: function() {
  62. this._super();
  63. if (this.get('serviceConfig.value').match(/(&amp;|&lt;|&gt;|&quot;|&apos;)/g)) {
  64. this.set('value', this.get('serviceConfig.value').replace('&','&amp;'));
  65. this.set('value', this.get('value').replace('&amp;','&'));
  66. }
  67. }
  68. });
  69. /**
  70. * Default input control
  71. * @type {*}
  72. */
  73. App.ServiceConfigTextField = Ember.TextField.extend(App.ServiceConfigPopoverSupport, App.SkipXmlEscapingSupport, {
  74. valueBinding: 'serviceConfig.value',
  75. classNameBindings: 'textFieldClassName',
  76. placeholderBinding: 'serviceConfig.defaultValue',
  77. keyPress: function (event) {
  78. if (event.keyCode == 13) {
  79. return false;
  80. }
  81. },
  82. //Set editDone true for last edited config text field parameter
  83. focusOut: function (event) {
  84. this.get('serviceConfig').set("editDone", true);
  85. },
  86. //Set editDone false for all current category config text field parameter
  87. focusIn: function (event) {
  88. if (!this.get('serviceConfig.isOverridden') && !this.get('serviceConfig.isComparison')) {
  89. this.get("parentView.categoryConfigsAll").setEach("editDone", false);
  90. }
  91. },
  92. textFieldClassName: function () {
  93. if (this.get('serviceConfig.unit')) {
  94. return ['input-small'];
  95. } else if (this.get('serviceConfig.displayType') === 'principal') {
  96. return ['span12'];
  97. } else {
  98. return ['span9'];
  99. }
  100. }.property('serviceConfig.displayType', 'serviceConfig.unit')
  101. });
  102. /**
  103. * Customized input control with Utits type specified
  104. * @type {*}
  105. */
  106. App.ServiceConfigTextFieldWithUnit = Ember.View.extend(App.ServiceConfigPopoverSupport, {
  107. valueBinding: 'serviceConfig.value',
  108. classNames: ['input-append', 'with-unit'],
  109. placeholderBinding: 'serviceConfig.defaultValue',
  110. templateName: require('templates/wizard/controls_service_config_textfield_with_unit')
  111. });
  112. /**
  113. * Password control
  114. * @type {*}
  115. */
  116. App.ServiceConfigPasswordField = Ember.TextField.extend({
  117. serviceConfig: null,
  118. type: 'password',
  119. attributeBindings:['readOnly'],
  120. valueBinding: 'serviceConfig.value',
  121. classNames: [ 'span4' ],
  122. placeholder: Em.I18n.t('form.item.placeholders.typePassword'),
  123. template: Ember.Handlebars.compile('{{view view.retypePasswordView}}'),
  124. keyPress: function (event) {
  125. if (event.keyCode == 13) {
  126. return false;
  127. }
  128. },
  129. retypePasswordView: Ember.TextField.extend({
  130. placeholder: Em.I18n.t('form.passwordRetype'),
  131. attributeBindings:['readOnly'],
  132. type: 'password',
  133. classNames: [ 'span4', 'retyped-password' ],
  134. keyPress: function (event) {
  135. if (event.keyCode == 13) {
  136. return false;
  137. }
  138. },
  139. valueBinding: 'parentView.serviceConfig.retypedPassword',
  140. readOnly: function () {
  141. return !this.get('parentView.serviceConfig.isEditable');
  142. }.property('parentView.serviceConfig.isEditable')
  143. }),
  144. readOnly: function () {
  145. return !this.get('serviceConfig.isEditable');
  146. }.property('serviceConfig.isEditable')
  147. });
  148. /**
  149. * Textarea control
  150. * @type {*}
  151. */
  152. App.ServiceConfigTextArea = Ember.TextArea.extend(App.ServiceConfigPopoverSupport, App.SkipXmlEscapingSupport, {
  153. valueBinding: 'serviceConfig.value',
  154. rows: 4,
  155. classNames: ['span9', 'directories']
  156. });
  157. /**
  158. * Textarea control for content type
  159. * @type {*}
  160. */
  161. App.ServiceConfigTextAreaContent = Ember.TextArea.extend(App.ServiceConfigPopoverSupport, App.SkipXmlEscapingSupport, {
  162. valueBinding: 'serviceConfig.value',
  163. rows: 20,
  164. classNames: ['span10']
  165. });
  166. /**
  167. * Textarea control with bigger height
  168. * @type {*}
  169. */
  170. App.ServiceConfigBigTextArea = App.ServiceConfigTextArea.extend({
  171. rows: 10
  172. });
  173. /**
  174. * Checkbox control
  175. * @type {*}
  176. */
  177. App.ServiceConfigCheckbox = Ember.Checkbox.extend(App.ServiceConfigPopoverSupport, {
  178. checkedBinding: 'serviceConfig.value',
  179. disabled: function () {
  180. return !this.get('serviceConfig.isEditable');
  181. }.property('serviceConfig.isEditable')
  182. });
  183. App.ServiceConfigRadioButtons = Ember.View.extend({
  184. templateName: require('templates/wizard/controls_service_config_radio_buttons'),
  185. didInsertElement: function () {
  186. // on page render, automatically populate JDBC URLs only for default database settings
  187. // so as to not lose the user's customizations on these fields
  188. if (['addServiceController', 'installerController'].contains(App.clusterStatus.wizardControllerName) && ['New PostgreSQL Database', 'New MySQL Database', 'New Derby Database'].contains(this.get('serviceConfig.value'))) {
  189. this.onOptionsChange();
  190. }
  191. },
  192. configs: function () {
  193. if (this.get('controller.name') == 'mainServiceInfoConfigsController') return this.get('categoryConfigsAll');
  194. return this.get('categoryConfigsAll').filterProperty('isObserved', true);
  195. }.property('categoryConfigsAll'),
  196. serviceConfig: null,
  197. categoryConfigsAll: null,
  198. onOptionsChange: function () {
  199. // The following if condition will be satisfied only for installer wizard flow
  200. if (this.get('configs').length) {
  201. var connectionUrl = this.get('connectionUrl');
  202. var dbClass = this.get('dbClass');
  203. if (connectionUrl) {
  204. if (this.get('serviceConfig.serviceName') === 'HIVE') {
  205. switch (this.get('serviceConfig.value')) {
  206. case 'New MySQL Database':
  207. case 'Existing MySQL Database':
  208. connectionUrl.set('value', "jdbc:mysql://" + this.get('hostName') + "/" + this.get('databaseName') + "?createDatabaseIfNotExist=true");
  209. dbClass.set('value', "com.mysql.jdbc.Driver");
  210. break;
  211. case 'New PostgreSQL Database':
  212. case Em.I18n.t('services.service.config.hive.oozie.postgresql'):
  213. connectionUrl.set('value', "jdbc:postgresql://" + this.get('hostName') + ":5432/" + this.get('databaseName'));
  214. dbClass.set('value', "org.postgresql.Driver");
  215. break;
  216. case 'Existing Oracle Database':
  217. connectionUrl.set('value', "jdbc:oracle:thin:@//" + this.get('hostName') + ":1521/" + this.get('databaseName'));
  218. dbClass.set('value', "oracle.jdbc.driver.OracleDriver");
  219. break;
  220. }
  221. } else if (this.get('serviceConfig.serviceName') === 'OOZIE') {
  222. switch (this.get('serviceConfig.value')) {
  223. case 'New Derby Database':
  224. connectionUrl.set('value', "jdbc:derby:${oozie.data.dir}/${oozie.db.schema.name}-db;create=true");
  225. dbClass.set('value', "org.apache.derby.jdbc.EmbeddedDriver");
  226. break;
  227. case 'Existing MySQL Database':
  228. connectionUrl.set('value', "jdbc:mysql://" + this.get('hostName') + "/" + this.get('databaseName'));
  229. dbClass.set('value', "com.mysql.jdbc.Driver");
  230. break;
  231. case Em.I18n.t('services.service.config.hive.oozie.postgresql'):
  232. connectionUrl.set('value', "jdbc:postgresql://" + this.get('hostName') + ":5432/" + this.get('databaseName'));
  233. dbClass.set('value', "org.postgresql.Driver");
  234. break;
  235. case 'Existing Oracle Database':
  236. connectionUrl.set('value', "jdbc:oracle:thin:@//" + this.get('hostName') + ":1521/" + this.get('databaseName'));
  237. dbClass.set('value', "oracle.jdbc.driver.OracleDriver");
  238. break;
  239. }
  240. }
  241. connectionUrl.set('defaultValue', connectionUrl.get('value'));
  242. }
  243. }
  244. }.observes('databaseName', 'hostName', 'connectionUrl','dbClass'),
  245. nameBinding: 'serviceConfig.radioName',
  246. databaseName: function () {
  247. switch (this.get('serviceConfig.serviceName')) {
  248. case 'HIVE':
  249. return this.get('categoryConfigsAll').findProperty('name', 'ambari.hive.db.schema.name').get('value');
  250. case 'OOZIE':
  251. return this.get('categoryConfigsAll').findProperty('name', 'oozie.db.schema.name').get('value');
  252. default:
  253. return null;
  254. }
  255. }.property('configs.@each.value', 'serviceConfig.serviceName'),
  256. hostName: function () {
  257. var value = this.get('serviceConfig.value');
  258. var returnValue;
  259. var hostname;
  260. if (this.get('serviceConfig.serviceName') === 'HIVE') {
  261. switch (value) {
  262. case 'New MySQL Database':
  263. case 'New PostgreSQL Database':
  264. hostname = this.get('categoryConfigsAll').findProperty('name', 'hive_ambari_host');
  265. break;
  266. case 'Existing MySQL Database':
  267. hostname = this.get('categoryConfigsAll').findProperty('name', 'hive_existing_mysql_host');
  268. break;
  269. case Em.I18n.t('services.service.config.hive.oozie.postgresql'):
  270. hostname = this.get('categoryConfigsAll').findProperty('name', 'hive_existing_postgresql_host');
  271. break;
  272. case 'Existing Oracle Database':
  273. hostname = this.get('categoryConfigsAll').findProperty('name', 'hive_existing_oracle_host');
  274. break;
  275. }
  276. if (hostname) {
  277. returnValue = hostname.get('value');
  278. } else {
  279. returnValue = this.get('categoryConfigsAll').findProperty('name', 'hive_hostname').get('value');
  280. }
  281. } else if (this.get('serviceConfig.serviceName') === 'OOZIE') {
  282. switch (value) {
  283. case 'New Derby Database':
  284. hostname = this.get('categoryConfigsAll').findProperty('name', 'oozie_ambari_host');
  285. break;
  286. case 'Existing MySQL Database':
  287. hostname = this.get('categoryConfigsAll').findProperty('name', 'oozie_existing_mysql_host');
  288. break;
  289. case Em.I18n.t('services.service.config.hive.oozie.postgresql'):
  290. hostname = this.get('categoryConfigsAll').findProperty('name', 'oozie_existing_postgresql_host');
  291. break;
  292. case 'Existing Oracle Database':
  293. hostname = this.get('categoryConfigsAll').findProperty('name', 'oozie_existing_oracle_host');
  294. break;
  295. }
  296. if (hostname) {
  297. returnValue = hostname.get('value');
  298. } else {
  299. returnValue = this.get('categoryConfigsAll').findProperty('name', 'oozie_hostname').get('value');
  300. }
  301. }
  302. return returnValue;
  303. }.property('serviceConfig.serviceName', 'serviceConfig.value', 'configs.@each.value'),
  304. connectionUrl: function () {
  305. if (this.get('serviceConfig.serviceName') === 'HIVE') {
  306. return this.get('categoryConfigsAll').findProperty('name', 'javax.jdo.option.ConnectionURL');
  307. } else {
  308. return this.get('categoryConfigsAll').findProperty('name', 'oozie.service.JPAService.jdbc.url');
  309. }
  310. }.property('serviceConfig.serviceName'),
  311. dbClass: function () {
  312. if (this.get('serviceConfig.serviceName') === 'HIVE') {
  313. return this.get('categoryConfigsAll').findProperty('name', 'javax.jdo.option.ConnectionDriverName');
  314. } else {
  315. return this.get('categoryConfigsAll').findProperty('name', 'oozie.service.JPAService.jdbc.driver');
  316. }
  317. }.property('serviceConfig.serviceName'),
  318. /**
  319. * `Observer` that add <code>additionalView</code> to <code>App.ServiceConfigProperty</code>
  320. * that responsible for (if existing db selected)
  321. * 1. checking database connection
  322. * 2. showing jdbc driver setup warning msg.
  323. *
  324. * @method handleDBConnectionProperty
  325. **/
  326. handleDBConnectionProperty: function() {
  327. if (!['addServiceController', 'installerController'].contains(App.clusterStatus.wizardControllerName)) return;
  328. var handledProperties = ['oozie_database', 'hive_database'];
  329. var currentValue = this.get('serviceConfig.value');
  330. var databases = /MySQL|PostgreSQL|Oracle|Derby/gi;
  331. var currentDB = currentValue.match(databases)[0];
  332. var databasesTypes = /MySQL|PostgreS|Oracle|Derby/gi;
  333. var currentDBType = currentValue.match(databasesTypes)[0];
  334. var existingDatabase = /existing/gi.test(currentValue);
  335. // db connection check button show up if existed db selected
  336. if (App.supports.databaseConnection) {
  337. var propertyAppendTo1 = this.get('categoryConfigsAll').findProperty('displayName', 'Database URL');
  338. if (currentDB && existingDatabase) {
  339. if (handledProperties.contains(this.get('serviceConfig.name'))) {
  340. if (propertyAppendTo1) propertyAppendTo1.set('additionalView', App.CheckDBConnectionView.extend({databaseName: currentDB}));
  341. }
  342. } else {
  343. propertyAppendTo1.set('additionalView', null);
  344. }
  345. }
  346. // warning msg under database type radio buttons, to warn the user to setup jdbc driver if existed db selected
  347. var propertyHive = this.get('categoryConfigsAll').findProperty('displayName', 'Hive Database');
  348. var propertyOozie = this.get('categoryConfigsAll').findProperty('displayName', 'Oozie Database');
  349. var propertyAppendTo2 = propertyHive ? propertyHive : propertyOozie;
  350. if (currentDB && existingDatabase) {
  351. if (handledProperties.contains(this.get('serviceConfig.name'))) {
  352. if (propertyAppendTo2) {
  353. propertyAppendTo2.set('additionalView', Ember.View.extend({
  354. template: Ember.Handlebars.compile('<div class="alert">{{{view.message}}}</div>'),
  355. message: Em.I18n.t('services.service.config.database.msg.jdbcSetup').format(currentDBType.toLowerCase(), currentDBType.toLowerCase())
  356. }));
  357. }
  358. }
  359. } else {
  360. propertyAppendTo2.set('additionalView', null);
  361. }
  362. }.observes('serviceConfig.value', 'configs.@each.value'),
  363. optionsBinding: 'serviceConfig.options'
  364. });
  365. App.ServiceConfigRadioButton = Ember.Checkbox.extend({
  366. tagName: 'input',
  367. attributeBindings: ['type', 'name', 'value', 'checked'],
  368. checked: false,
  369. type: 'radio',
  370. name: null,
  371. value: null,
  372. didInsertElement: function () {
  373. console.debug('App.ServiceConfigRadioButton.didInsertElement');
  374. if (this.get('parentView.serviceConfig.value') === this.get('value')) {
  375. console.debug(this.get('name') + ":" + this.get('value') + ' is checked');
  376. this.set('checked', true);
  377. }
  378. },
  379. click: function () {
  380. this.set('checked', true);
  381. console.debug('App.ServiceConfigRadioButton.click');
  382. this.onChecked();
  383. },
  384. onChecked: function () {
  385. // Wrapping the call with Ember.run.next prevents a problem where setting isVisible on component
  386. // causes JS error due to re-rendering. For example, this occurs when switching the Config Group
  387. // in Service Config page
  388. Em.run.next(this, function() {
  389. console.debug('App.ServiceConfigRadioButton.onChecked');
  390. this.set('parentView.serviceConfig.value', this.get('value'));
  391. var components = this.get('parentView.serviceConfig.options');
  392. if (components) {
  393. components.forEach(function (_component) {
  394. if (_component.foreignKeys) {
  395. _component.foreignKeys.forEach(function (_componentName) {
  396. if (this.get('parentView.categoryConfigsAll').someProperty('name', _componentName)) {
  397. var component = this.get('parentView.categoryConfigsAll').findProperty('name', _componentName);
  398. component.set('isVisible', _component.displayName === this.get('value'));
  399. }
  400. }, this);
  401. }
  402. }, this);
  403. }
  404. });
  405. }.observes('checked'),
  406. disabled: function () {
  407. return !this.get('parentView.serviceConfig.isEditable');
  408. }.property('parentView.serviceConfig.isEditable')
  409. });
  410. App.ServiceConfigComboBox = Ember.Select.extend(App.ServiceConfigPopoverSupport, {
  411. contentBinding: 'serviceConfig.options',
  412. selectionBinding: 'serviceConfig.value',
  413. placeholderBinding: 'serviceConfig.defaultValue',
  414. classNames: [ 'span3' ]
  415. });
  416. /**
  417. * Base component for host config with popover support
  418. */
  419. App.ServiceConfigHostPopoverSupport = Ember.Mixin.create({
  420. /**
  421. * Config object. It will instance of App.ServiceConfigProperty
  422. */
  423. serviceConfig: null,
  424. didInsertElement: function () {
  425. App.popover(this.$(), {
  426. title: this.get('serviceConfig.displayName'),
  427. content: this.get('serviceConfig.description'),
  428. placement: 'right',
  429. trigger: 'hover'
  430. });
  431. }
  432. });
  433. /**
  434. * Master host component.
  435. * Show hostname without ability to edit it
  436. * @type {*}
  437. */
  438. App.ServiceConfigMasterHostView = Ember.View.extend(App.ServiceConfigHostPopoverSupport, {
  439. classNames: ['master-host', 'span6'],
  440. valueBinding: 'serviceConfig.value',
  441. template: Ember.Handlebars.compile('{{value}}')
  442. });
  443. /**
  444. * Show value as plain label in italics
  445. * @type {*}
  446. */
  447. App.ServiceConfigLabelView = Ember.View.extend(App.ServiceConfigHostPopoverSupport, {
  448. classNames: ['master-host', 'span6'],
  449. valueBinding: 'serviceConfig.value',
  450. template: Ember.Handlebars.compile('<i>{{view.value}}</i>')
  451. });
  452. /**
  453. * Base component to display Multiple hosts
  454. * @type {*}
  455. */
  456. App.ServiceConfigMultipleHostsDisplay = Ember.Mixin.create(App.ServiceConfigHostPopoverSupport, {
  457. hasNoHosts: function () {
  458. console.log('view', this.get('viewName')); //to know which View cause errors
  459. console.log('controller', this.get('controller').name); //should be slaveComponentGroupsController
  460. if (!this.get('value')) {
  461. return true;
  462. }
  463. return this.get('value').length === 0;
  464. }.property('value'),
  465. hasOneHost: function () {
  466. return this.get('value').length === 1;
  467. }.property('value'),
  468. hasMultipleHosts: function () {
  469. return this.get('value').length > 1;
  470. }.property('value'),
  471. otherLength: function () {
  472. var len = this.get('value').length;
  473. if (len > 2) {
  474. return Em.I18n.t('installer.controls.serviceConfigMultipleHosts.others').format(len - 1);
  475. } else {
  476. return Em.I18n.t('installer.controls.serviceConfigMultipleHosts.other');
  477. }
  478. }.property('value')
  479. });
  480. /**
  481. * Multiple master host component.
  482. * Show hostnames without ability to edit it
  483. * @type {*}
  484. */
  485. App.ServiceConfigMasterHostsView = Ember.View.extend(App.ServiceConfigMultipleHostsDisplay, {
  486. viewName: "serviceConfigMasterHostsView",
  487. valueBinding: 'serviceConfig.value',
  488. classNames: ['master-hosts', 'span6'],
  489. templateName: require('templates/wizard/master_hosts'),
  490. /**
  491. * Onclick handler for link
  492. */
  493. showHosts: function () {
  494. var serviceConfig = this.get('serviceConfig');
  495. App.ModalPopup.show({
  496. header: Em.I18n.t('installer.controls.serviceConfigMasterHosts.header').format(serviceConfig.category),
  497. bodyClass: Ember.View.extend({
  498. serviceConfig: serviceConfig,
  499. templateName: require('templates/wizard/master_hosts_popup')
  500. }),
  501. secondary: null
  502. });
  503. }
  504. });
  505. /**
  506. * Show tabs list for slave hosts
  507. * @type {*}
  508. */
  509. App.SlaveComponentGroupsMenu = Em.CollectionView.extend({
  510. content: function () {
  511. return this.get('controller.componentGroups');
  512. }.property('controller.componentGroups'),
  513. tagName: 'ul',
  514. classNames: ["nav", "nav-tabs"],
  515. itemViewClass: Em.View.extend({
  516. classNameBindings: ["active"],
  517. active: function () {
  518. return this.get('content.active');
  519. }.property('content.active'),
  520. errorCount: function () {
  521. return this.get('content.properties').filterProperty('isValid', false).filterProperty('isVisible', true).get('length');
  522. }.property('content.properties.@each.isValid', 'content.properties.@each.isVisible'),
  523. templateName: require('templates/wizard/controls_slave_component_groups_menu')
  524. })
  525. });
  526. /**
  527. * <code>Add group</code> button
  528. * @type {*}
  529. */
  530. App.AddSlaveComponentGroupButton = Ember.View.extend({
  531. tagName: 'span',
  532. slaveComponentName: null,
  533. didInsertElement: function () {
  534. App.popover(this.$(), {
  535. title: Em.I18n.t('installer.controls.addSlaveComponentGroupButton.title').format(this.get('slaveComponentName')),
  536. content: Em.I18n.t('installer.controls.addSlaveComponentGroupButton.content').format(this.get('slaveComponentName'), this.get('slaveComponentName'), this.get('slaveComponentName')),
  537. placement: 'right',
  538. trigger: 'hover'
  539. });
  540. }
  541. });
  542. /**
  543. * Multiple Slave Hosts component
  544. * @type {*}
  545. */
  546. App.ServiceConfigSlaveHostsView = Ember.View.extend(App.ServiceConfigMultipleHostsDisplay, {
  547. viewName: 'serviceConfigSlaveHostsView',
  548. classNames: ['slave-hosts', 'span6'],
  549. valueBinding: 'serviceConfig.value',
  550. templateName: require('templates/wizard/slave_hosts'),
  551. /**
  552. * Onclick handler for link
  553. */
  554. showHosts: function () {
  555. var serviceConfig = this.get('serviceConfig');
  556. App.ModalPopup.show({
  557. header: Em.I18n.t('installer.controls.serviceConfigMasterHosts.header').format(serviceConfig.category),
  558. bodyClass: Ember.View.extend({
  559. serviceConfig: serviceConfig,
  560. templateName: require('templates/wizard/master_hosts_popup')
  561. }),
  562. secondary: null
  563. });
  564. }
  565. });
  566. /**
  567. * properties for present active slave group
  568. * @type {*}
  569. */
  570. App.SlaveGroupPropertiesView = Ember.View.extend({
  571. viewName: 'serviceConfigSlaveHostsView',
  572. group: function () {
  573. return this.get('controller.activeGroup');
  574. }.property('controller.activeGroup'),
  575. groupConfigs: function () {
  576. console.log("************************************************************************");
  577. console.log("The value of group is: " + this.get('group'));
  578. console.log("************************************************************************");
  579. return this.get('group.properties');
  580. }.property('group.properties.@each').cacheable(),
  581. errorCount: function () {
  582. return this.get('group.properties').filterProperty('isValid', false).filterProperty('isVisible', true).get('length');
  583. }.property('configs.@each.isValid', 'configs.@each.isVisible')
  584. });
  585. /**
  586. * DropDown component for <code>select hosts for groups</code> popup
  587. * @type {*}
  588. */
  589. App.SlaveComponentDropDownGroupView = Ember.View.extend({
  590. viewName: "slaveComponentDropDownGroupView",
  591. /**
  592. * On change handler for <code>select hosts for groups</code> popup
  593. * @param event
  594. */
  595. changeGroup: function (event) {
  596. var host = this.get('content');
  597. var groupName = $('#' + this.get('elementId') + ' select').val();
  598. this.get('controller').changeHostGroup(host, groupName);
  599. },
  600. optionTag: Ember.View.extend({
  601. /**
  602. * Whether current value(OptionTag value) equals to host value(assigned to SlaveComponentDropDownGroupView.content)
  603. */
  604. selected: function () {
  605. return this.get('parentView.content.group') === this.get('content');
  606. }.property('content')
  607. })
  608. });
  609. /**
  610. * Show info about current group
  611. * @type {*}
  612. */
  613. App.SlaveComponentChangeGroupNameView = Ember.View.extend({
  614. contentBinding: 'controller.activeGroup',
  615. classNames: ['control-group'],
  616. classNameBindings: 'error',
  617. error: false,
  618. setError: function () {
  619. this.set('error', false);
  620. }.observes('controller.activeGroup'),
  621. errorMessage: function () {
  622. return this.get('error') ? Em.I18n.t('installer.controls.slaveComponentChangeGroupName.error') : '';
  623. }.property('error'),
  624. /**
  625. * Onclick handler for saving updated group name
  626. * @param event
  627. */
  628. changeGroupName: function (event) {
  629. var inputVal = $('#' + this.get('elementId') + ' input[type="text"]').val();
  630. if (inputVal !== this.get('content.name')) {
  631. var result = this.get('controller').changeSlaveGroupName(this.get('content'), inputVal);
  632. this.set('error', result);
  633. }
  634. }
  635. });
  636. /**
  637. * View for testing connection to database.
  638. **/
  639. App.CheckDBConnectionView = Ember.View.extend({
  640. templateName: require('templates/common/form/check_db_connection'),
  641. /** @property {string} btnCaption - text for button **/
  642. btnCaption: Em.I18n.t('services.service.config.database.btn.idle'),
  643. /** @property {string} responseCaption - text for status link **/
  644. responseCaption: null,
  645. /** @property {boolean} isConnecting - is request to server activated **/
  646. isConnecting: false,
  647. /** @property {boolean} isValidationPassed - check validation for required fields **/
  648. isValidationPassed: null,
  649. /** @property {string} databaseName- name of current database **/
  650. databaseName: null,
  651. /** @property {boolean} isRequestResolved - check for finished request to server **/
  652. isRequestResolved: false,
  653. /** @property {boolean} isConnectionSuccess - check for successful connection to database **/
  654. isConnectionSuccess: null,
  655. /** @property {string} responseFromServer - message from server response **/
  656. responseFromServer: null,
  657. /** @property {Object} ambariRequiredProperties - properties that need for custom action request **/
  658. ambariRequiredProperties: null,
  659. /** @property {Number} currentRequestId - current custom action request id **/
  660. currentRequestId: null,
  661. /** @property {Number} currentTaskId - current custom action task id **/
  662. currentTaskId: null,
  663. /** @property {jQuery.Deferred} request - current $.ajax request **/
  664. request: null,
  665. /** @property {Number} pollInterval - timeout interval for ajax polling **/
  666. pollInterval: 3000,
  667. /** @property {string} hostNameProperty - host name property based on service and database names **/
  668. hostNameProperty: function() {
  669. if (!/wizard/i.test(this.get('controller.name')) && this.get('parentView.service.serviceName') === 'HIVE') {
  670. return this.get('parentView.service.serviceName').toLowerCase() + '_hostname';
  671. }
  672. return '{0}_existing_{1}_host'.format(this.get('parentView.service.serviceName').toLowerCase(), this.get('databaseName').toLowerCase());
  673. }.property('databaseName'),
  674. /** @property {boolean} isBtnDisabled - disable button on failed validation or active request **/
  675. isBtnDisabled: function() {
  676. return !this.get('isValidationPassed') || this.get('isConnecting');
  677. }.property('isValidationPassed', 'isConnecting'),
  678. /** @property {object} requiredProperties - properties that necessary for database connection **/
  679. requiredProperties: function() {
  680. var propertiesMap = {
  681. OOZIE: ['oozie.db.schema.name','oozie.service.JPAService.jdbc.username','oozie.service.JPAService.jdbc.password','oozie.service.JPAService.jdbc.driver','oozie.service.JPAService.jdbc.url'],
  682. HIVE: ['ambari.hive.db.schema.name','javax.jdo.option.ConnectionUserName','javax.jdo.option.ConnectionPassword','javax.jdo.option.ConnectionDriverName','javax.jdo.option.ConnectionURL']
  683. };
  684. return propertiesMap[this.get('parentView.service.serviceName')];
  685. }.property(),
  686. /** @property {Object} propertiesPattern - check pattern according to type of connection properties **/
  687. propertiesPattern: function() {
  688. return {
  689. user_name: /username$/ig,
  690. user_passwd: /password$/ig,
  691. db_connection_url: /jdbc\.url|connectionurl/ig
  692. }
  693. }.property(),
  694. /** @property {String} masterHostName - host name location of Master Component related to Service **/
  695. masterHostName: function() {
  696. var serviceMasterMap = {
  697. 'OOZIE': 'oozieserver_host',
  698. 'HIVE': 'hivemetastore_host'
  699. };
  700. return this.get('parentView.categoryConfigsAll').findProperty('name', serviceMasterMap[this.get('parentView.service.serviceName')]).get('value');
  701. }.property(),
  702. /** @property {Object} connectionProperties - service specific config values mapped for custom action request **/
  703. connectionProperties: function() {
  704. var propObj = {};
  705. for (var key in this.get('propertiesPattern')) {
  706. propObj[key] = this.getConnectionProperty(this.get('propertiesPattern')[key]);
  707. }
  708. return propObj;
  709. }.property('parentView.categoryConfigsAll.@each.value'),
  710. /**
  711. * Properties that stores in local storage used for handling
  712. * last success connection.
  713. *
  714. * @property {Object} preparedDBProperties
  715. **/
  716. preparedDBProperties: function() {
  717. var propObj = {};
  718. for (var key in this.get('propertiesPattern')) {
  719. var propName = this.getConnectionProperty(this.get('propertiesPattern')[key], true);
  720. propObj[propName] = this.get('parentView.categoryConfigsAll').findProperty('name', propName).get('value');
  721. }
  722. return propObj;
  723. }.property(),
  724. /** Check validation and load ambari properties **/
  725. didInsertElement: function() {
  726. this.handlePropertiesValidation();
  727. this.getAmbariProperties();
  728. },
  729. /** On view destroy **/
  730. willDestroyElement: function() {
  731. this.set('isConnecting', false);
  732. this._super();
  733. },
  734. /**
  735. * Observer that take care about enabling/disabling button based on required properties validation.
  736. *
  737. * @method handlePropertiesValidation
  738. **/
  739. handlePropertiesValidation: function() {
  740. this.restore();
  741. var isValid = true;
  742. var properties = [].concat(this.get('requiredProperties'));
  743. properties.push(this.get('hostNameProperty'));
  744. properties.forEach(function(propertyName) {
  745. var property = this.get('parentView.categoryConfigsAll').findProperty('name', propertyName);
  746. if(property && !property.get('isValid')) isValid = false;
  747. }, this);
  748. this.set('isValidationPassed', isValid);
  749. }.observes('parentView.categoryConfigsAll.@each.isValid', 'parentView.categoryConfigsAll.@each.value', 'databaseName'),
  750. getConnectionProperty: function(regexp, isGetName) {
  751. var _this = this;
  752. var propertyName = _this.get('requiredProperties').filter(function(item) {
  753. return regexp.test(item);
  754. })[0];
  755. return (isGetName) ? propertyName : _this.get('parentView.categoryConfigsAll').findProperty('name', propertyName).get('value');
  756. },
  757. /**
  758. * Set up ambari properties required for custom action request
  759. *
  760. * @method getAmbariProperties
  761. **/
  762. getAmbariProperties: function() {
  763. var clusterController = App.router.get('clusterController');
  764. var _this = this;
  765. if (!App.isEmptyObject(App.db.get('tmp', 'ambariProperties')) && !this.get('ambariProperties')) {
  766. this.set('ambariProperties', App.db.get('tmp', 'ambariProperties'));
  767. return;
  768. }
  769. if (App.isEmptyObject(clusterController.get('ambariProperties'))) {
  770. clusterController.loadAmbariProperties().done(function(data) {
  771. _this.formatAmbariProperties(data.RootServiceComponents.properties);
  772. });
  773. } else {
  774. this.formatAmbariProperties(clusterController.get('ambariProperties'));
  775. }
  776. },
  777. formatAmbariProperties: function(properties) {
  778. var defaults = {
  779. threshold: "60",
  780. ambari_server_host: location.hostname,
  781. check_execute_list : "db_connection_check"
  782. };
  783. var properties = App.permit(properties, ['jdk.name','jdk_location','java.home']);
  784. var renameKey = function(oldKey, newKey) {
  785. if (properties[oldKey]) {
  786. defaults[newKey] = properties[oldKey];
  787. delete properties[oldKey];
  788. }
  789. };
  790. renameKey('java.home', 'java_home');
  791. renameKey('jdk.name', 'jdk_name');
  792. $.extend(properties, defaults);
  793. App.db.set('tmp', 'ambariProperties', properties);
  794. this.set('ambariProperties', properties);
  795. },
  796. /**
  797. * `Action` method for starting connect to current database.
  798. *
  799. * @method connectToDatabase
  800. **/
  801. connectToDatabase: function() {
  802. if (this.get('isBtnDisabled')) return false;
  803. var self = this;
  804. self.set('isRequestResolved', false);
  805. App.db.set('tmp', this.get('parentView.service.serviceName') + '_connection', {});
  806. this.setConnectingStatus(true);
  807. this.createCustomAction();
  808. },
  809. /**
  810. * Run custom action for database connection.
  811. *
  812. * @method createCustomAction
  813. **/
  814. createCustomAction: function() {
  815. var dbName = this.get('databaseName').toLowerCase() === 'postgresql' ? 'postgres' : this.get('databaseName').toLowerCase();
  816. var params = $.extend(true, {}, { db_name: dbName }, this.get('connectionProperties'), this.get('ambariProperties'));
  817. App.ajax.send({
  818. name: 'custom_action.create',
  819. sender: this,
  820. data: {
  821. requestInfo: {
  822. parameters: params
  823. },
  824. filteredHosts: [this.get('masterHostName')]
  825. },
  826. success: 'onCreateActionSuccess',
  827. error: 'onCreateActionError'
  828. });
  829. },
  830. /**
  831. * Run updater if task is created successfully.
  832. *
  833. * @method onConnectActionS
  834. **/
  835. onCreateActionSuccess: function(data) {
  836. this.set('currentRequestId', data.Requests.id);
  837. App.ajax.send({
  838. name: 'custom_action.request',
  839. sender: this,
  840. data: {
  841. requestId: this.get('currentRequestId')
  842. },
  843. success: 'setCurrentTaskId'
  844. });
  845. },
  846. setCurrentTaskId: function(data) {
  847. this.set('currentTaskId', data.items[0].Tasks.id);
  848. this.startPolling();
  849. },
  850. startPolling: function() {
  851. if (this.get('isConnecting'))
  852. this.getTaskInfo();
  853. },
  854. getTaskInfo: function() {
  855. var request = App.ajax.send({
  856. name: 'custom_action.request',
  857. sender: this,
  858. data: {
  859. requestId: this.get('currentRequestId'),
  860. taskId: this.get('currentTaskId')
  861. },
  862. success: 'getTaskInfoSuccess'
  863. });
  864. this.set('request', request);
  865. },
  866. getTaskInfoSuccess: function(data) {
  867. var task = data.Tasks;
  868. if (task.status === 'COMPLETED') {
  869. var structuredOut = task.structured_out.db_connection_check;
  870. if (structuredOut.exit_code != 0) {
  871. this.set('responseFromServer', {
  872. stderr: task.stderr,
  873. stdout: task.stdout,
  874. structuredOut: structuredOut.message
  875. });
  876. this.setResponseStatus('failed');
  877. } else {
  878. App.db.set('tmp', this.get('parentView.service.serviceName') + '_connection', this.get('preparedDBProperties'));
  879. this.setResponseStatus('success');
  880. }
  881. }
  882. if (task.status === 'FAILED') {
  883. this.set('responseFromServer', {
  884. stderr: task.stderr,
  885. stdout: task.stdout
  886. });
  887. this.setResponseStatus('failed');
  888. }
  889. if (/PENDING|QUEUED|IN_PROGRESS/.test(task.status)) {
  890. Em.run.later(this, function() {
  891. this.startPolling();
  892. }, this.get('pollInterval'));
  893. }
  894. },
  895. onCreateActionError: function(jqXhr, status, errorMessage) {
  896. this.setResponseStatus('failed');
  897. this.set('responseFromServer', errorMessage);
  898. },
  899. setResponseStatus: function(isSuccess) {
  900. var isSuccess = isSuccess == 'success';
  901. this.setConnectingStatus(false);
  902. this.set('responseCaption', isSuccess ? Em.I18n.t('services.service.config.database.connection.success') : Em.I18n.t('services.service.config.database.connection.failed'));
  903. this.set('isConnectionSuccess', isSuccess);
  904. this.set('isRequestResolved', true);
  905. },
  906. /**
  907. * Switch captions and statuses for active/non-active request.
  908. *
  909. * @method setConnectionStatus
  910. * @param {Boolean} [active]
  911. */
  912. setConnectingStatus: function(active) {
  913. if (active) {
  914. this.set('responseCaption', null);
  915. this.set('responseFromServer', null);
  916. }
  917. this.set('btnCaption', !!active ? Em.I18n.t('services.service.config.database.btn.connecting') : Em.I18n.t('services.service.config.database.btn.idle'));
  918. this.set('isConnecting', !!active);
  919. },
  920. /**
  921. * Set view to init status.
  922. *
  923. * @method restore
  924. **/
  925. restore: function() {
  926. if (this.get('request')) {
  927. this.get('request').abort();
  928. this.set('request', null);
  929. }
  930. this.set('responseCaption', null);
  931. this.set('responseFromServer', null);
  932. this.setConnectingStatus(false);
  933. this.set('isRequestResolved', false);
  934. },
  935. /**
  936. * `Action` method for showing response from server in popup.
  937. *
  938. * @method showLogsPopup
  939. **/
  940. showLogsPopup: function() {
  941. if (this.get('isConnectionSuccess')) return;
  942. var _this = this;
  943. var popup = App.showAlertPopup('Error: {0} connection'.format(this.get('databaseName')));
  944. if (typeof this.get('responseFromServer') == 'object') {
  945. popup.set('bodyClass', Em.View.extend({
  946. templateName: require('templates/common/error_log_body'),
  947. openedTask: _this.get('responseFromServer')
  948. }));
  949. } else {
  950. popup.set('body', this.get('responseFromServer'));
  951. }
  952. return popup;
  953. }
  954. });