controls_view.js 36 KB

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