controls_view.js 41 KB

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