controls_view.js 35 KB

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