controls_view.js 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414
  1. /**
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. var App = require('app');
  19. var validator = require('utils/validator');
  20. var dbInfo = require('data/db_properties_info') || {};
  21. var delay = (function(){
  22. var timer = 0;
  23. return function(callback, ms){
  24. clearTimeout (timer);
  25. timer = setTimeout(callback, ms);
  26. };
  27. })();
  28. /**
  29. * Abstract view for config fields.
  30. * Add popover support to control
  31. */
  32. App.ServiceConfigPopoverSupport = Ember.Mixin.create({
  33. /**
  34. * Config object. It will instance of App.ServiceConfigProperty
  35. */
  36. serviceConfig: null,
  37. attributeBindings:['readOnly'],
  38. isPopoverEnabled: true,
  39. popoverPlacement: 'right',
  40. didInsertElement: function () {
  41. App.tooltip($('body'), {
  42. selector: '[data-toggle=tooltip]',
  43. placement: 'top'
  44. });
  45. // if description for this serviceConfig not exist, then no need to show popover
  46. if (this.get('isPopoverEnabled') !== 'false' && this.get('serviceConfig.description')) {
  47. App.popover(this.$(), {
  48. title: Em.I18n.t('installer.controls.serviceConfigPopover.title').format(
  49. this.get('serviceConfig.displayName'),
  50. (this.get('serviceConfig.displayName') == this.get('serviceConfig.name')) ? '' : this.get('serviceConfig.name')
  51. ),
  52. content: this.get('serviceConfig.description'),
  53. placement: this.get('popoverPlacement'),
  54. trigger: 'hover'
  55. });
  56. }
  57. },
  58. willDestroyElement: function() {
  59. this.$().popover('destroy');
  60. },
  61. readOnly: function () {
  62. return !this.get('serviceConfig.isEditable');
  63. }.property('serviceConfig.isEditable')
  64. });
  65. App.SupportsDependentConfigs = Ember.Mixin.create({
  66. /**
  67. * do not apply recommended value if user change value by himself.
  68. */
  69. keyUp: function() {
  70. this.get('controller').removeCurrentFromDependentList(this.get('serviceConfig') || this.get('config'));
  71. },
  72. /**
  73. * method send request to check if some of dependent configs was changes
  74. * and in case there was changes shows popup with info about changed configs
  75. *
  76. * @param {App.ServiceConfigProperty} config
  77. * @returns {$.Deferred}
  78. */
  79. sendRequestRorDependentConfigs: function(config) {
  80. if (!config || !config.get('isValid')) return $.Deferred().resolve().promise();
  81. if (['mainServiceInfoConfigsController','wizardStep7Controller'].contains(this.get('controller.name'))) {
  82. var name = config.get('name');
  83. var saveRecommended = (this.get('config.value') === this.get('config.recommendedValue'));
  84. var controller = this.get('controller');
  85. var type = App.config.getConfigTagFromFileName(config.get('filename'));
  86. var p = App.StackConfigProperty.find(App.config.configId(name, type));
  87. if ((p && p.get('propertyDependedBy.length') > 0 || p.get('displayType') === 'user') && config.get('oldValue') !== config.get('value')) {
  88. var old = config.get('oldValue');
  89. config.set('oldValue', config.get('value'));
  90. return controller.getRecommendationsForDependencies([{
  91. "type": type,
  92. "name": name,
  93. "old_value": Em.isNone(old) ? config.get('initialValue') : old
  94. }], false, function() {
  95. controller.removeCurrentFromDependentList(config, saveRecommended);
  96. });
  97. } else {
  98. controller.removeCurrentFromDependentList(config, saveRecommended);
  99. }
  100. }
  101. return $.Deferred().resolve().promise();
  102. },
  103. /**
  104. * Restore values for dependent configs by parent config info.
  105. * NOTE: If dependent config inherited from multiply configs its
  106. * value will be restored only when all parent configs are being restored.
  107. *
  108. * @param {App.ServiceConfigProperty} parentConfig
  109. */
  110. restoreDependentConfigs: function(parentConfig) {
  111. var controller = this.get('controller');
  112. var dependentConfigs = controller.get('_dependentConfigValues');
  113. if (controller.updateDependentConfigs) {
  114. controller.updateDependentConfigs();
  115. controller.set('_dependentConfigValues', dependentConfigs.reject(function(item) {
  116. if (item.parentConfigs.contains(parentConfig.get('name'))) {
  117. if (item.parentConfigs.length > 1) {
  118. item.parentConfigs.removeObject(parentConfig.get('name'));
  119. } else {
  120. // reset property value
  121. var property = controller.findConfigProperty(item.propertyName, App.config.getOriginalFileName(item.fileName));
  122. if (property) {
  123. property.set('value', property.get('savedValue') || property.get('initialValue'));
  124. }
  125. return true;
  126. }
  127. }
  128. return false;
  129. }));
  130. }
  131. }
  132. });
  133. /**
  134. * mixin set class that serve as unique element identifier,
  135. * id not used in order to avoid collision with ember ids
  136. */
  137. App.ServiceConfigCalculateId = Ember.Mixin.create({
  138. idClass: Ember.computed(function () {
  139. var config = this.get('config') && this.get('config.widget') ? this.get('config') : this.get('serviceConfig') || {};
  140. var label = Em.get(config, 'name') ? Em.get(config, 'name').toLowerCase().replace(/\./g, '-') : '',
  141. fileName = Em.get(config, 'filename') ? Em.get(config, 'filename').toLowerCase().replace(/\./g, '-') : '',
  142. group = Em.get(config, 'group.name') || 'default',
  143. isOrigin = Em.getWithDefault(config, 'compareConfigs.length', 0) > 0 ? '-origin' : '';
  144. return 'service-config-' + label + '-' + fileName + '-' + group + isOrigin;
  145. }),
  146. classNameBindings: 'idClass'
  147. });
  148. /**
  149. * Default input control
  150. * @type {*}
  151. */
  152. App.ServiceConfigTextField = Ember.TextField.extend(App.ServiceConfigPopoverSupport, App.ServiceConfigCalculateId, App.SupportsDependentConfigs, {
  153. valueBinding: 'serviceConfig.value',
  154. classNameBindings: 'textFieldClassName',
  155. placeholderBinding: 'serviceConfig.savedValue',
  156. onValueUpdate: function () {
  157. var self = this;
  158. delay(function(){
  159. self.sendRequestRorDependentConfigs(self.get('serviceConfig'));
  160. }, 500);
  161. }.observes('serviceConfig.value'),
  162. //Set editDone true for last edited config text field parameter
  163. focusOut: function () {
  164. this.get('serviceConfig').set("editDone", true);
  165. },
  166. //Set editDone false for all current category config text field parameter
  167. focusIn: function () {
  168. if (!this.get('serviceConfig.isOverridden') && !this.get('serviceConfig.isComparison')) {
  169. if (this.get('parentView.categoryConfigsAll')) {
  170. this.get("parentView.categoryConfigsAll").setEach("editDone", false);
  171. }
  172. }
  173. },
  174. textFieldClassName: function () {
  175. if (this.get('serviceConfig.unit')) {
  176. return ['input-small'];
  177. } else if (this.get('serviceConfig.displayType') === 'principal') {
  178. return ['span12'];
  179. } else {
  180. return ['span9'];
  181. }
  182. }.property('serviceConfig.displayType', 'serviceConfig.unit')
  183. });
  184. /**
  185. * Customized input control with Units type specified
  186. * @type {Em.View}
  187. */
  188. App.ServiceConfigTextFieldWithUnit = Ember.View.extend(App.ServiceConfigPopoverSupport, App.SupportsDependentConfigs, {
  189. valueBinding: 'serviceConfig.value',
  190. classNames: ['input-append', 'with-unit'],
  191. placeholderBinding: 'serviceConfig.savedValue',
  192. onValueUpdate: function () {
  193. var self = this;
  194. delay(function(){
  195. self.sendRequestRorDependentConfigs(self.get('serviceConfig'));
  196. }, 500);
  197. }.observes('serviceConfig.value'),
  198. templateName: require('templates/wizard/controls_service_config_textfield_with_unit')
  199. });
  200. /**
  201. * Password control
  202. * @type {*}
  203. */
  204. App.ServiceConfigPasswordField = Ember.TextField.extend({
  205. serviceConfig: null,
  206. type: 'password',
  207. attributeBindings:['readOnly'],
  208. valueBinding: 'serviceConfig.value',
  209. classNames: [ 'span4' ],
  210. placeholder: Em.I18n.t('form.item.placeholders.typePassword'),
  211. template: Ember.Handlebars.compile('{{view view.retypePasswordView}}'),
  212. keyPress: function (event) {
  213. if (event.keyCode == 13) {
  214. return false;
  215. }
  216. },
  217. retypePasswordView: Ember.TextField.extend({
  218. placeholder: Em.I18n.t('form.passwordRetype'),
  219. attributeBindings:['readOnly'],
  220. type: 'password',
  221. classNames: [ 'span4', 'retyped-password' ],
  222. keyPress: function (event) {
  223. if (event.keyCode == 13) {
  224. return false;
  225. }
  226. },
  227. valueBinding: 'parentView.serviceConfig.retypedPassword',
  228. readOnly: function () {
  229. return !this.get('parentView.serviceConfig.isEditable');
  230. }.property('parentView.serviceConfig.isEditable')
  231. }),
  232. readOnly: function () {
  233. return !this.get('serviceConfig.isEditable');
  234. }.property('serviceConfig.isEditable')
  235. });
  236. /**
  237. * Textarea control
  238. * @type {*}
  239. */
  240. App.ServiceConfigTextArea = Ember.TextArea.extend(App.ServiceConfigPopoverSupport, App.ServiceConfigCalculateId, App.SupportsDependentConfigs, {
  241. onValueUpdate: function () {
  242. var self = this;
  243. delay(function(){
  244. self.sendRequestRorDependentConfigs(self.get('serviceConfig'));
  245. }, 500);
  246. }.observes('serviceConfig.value'),
  247. valueBinding: 'serviceConfig.value',
  248. rows: 4,
  249. classNames: ['directories'],
  250. classNameBindings: ['widthClass'],
  251. widthClass: 'span9'
  252. });
  253. /**
  254. * Textarea control for content type
  255. * @type {*}
  256. */
  257. App.ServiceConfigTextAreaContent = Ember.TextArea.extend(App.ServiceConfigPopoverSupport, App.ServiceConfigCalculateId, App.SupportsDependentConfigs, {
  258. valueBinding: 'serviceConfig.value',
  259. rows: 20,
  260. classNames: ['span10']
  261. });
  262. /**
  263. * Textarea control with bigger height
  264. * @type {*}
  265. */
  266. App.ServiceConfigBigTextArea = App.ServiceConfigTextArea.extend(App.ServiceConfigCalculateId, {
  267. rows: 10
  268. });
  269. /**
  270. * Checkbox control
  271. * @type {*}
  272. */
  273. App.ServiceConfigCheckbox = Ember.Checkbox.extend(App.ServiceConfigPopoverSupport, App.ServiceConfigCalculateId, App.SupportsDependentConfigs, {
  274. allowedPairs: {
  275. 'trueFalse': ["true", "false"],
  276. 'YesNo': ["Yes", "No"],
  277. 'YESNO': ["YES", "NO"],
  278. 'yesNo': ["yes", "no"]
  279. },
  280. trueValue: true,
  281. falseValue: false,
  282. checked: false,
  283. /**
  284. * set appropriate config values pair
  285. * to define which value is positive (checked) property
  286. * and what value is negative (unchecked) proeprty
  287. */
  288. didInsertElement: function() {
  289. this._super();
  290. this.addObserver('serviceConfig.value', this, 'toggleChecker');
  291. Object.keys(this.get('allowedPairs')).forEach(function(key) {
  292. if (this.get('allowedPairs')[key].contains(this.get('serviceConfig.value'))) {
  293. this.set('trueValue', this.get('allowedPairs')[key][0]);
  294. this.set('falseValue', this.get('allowedPairs')[key][1]);
  295. }
  296. }, this);
  297. this.set('checked', this.get('serviceConfig.value') === this.get('trueValue'))
  298. },
  299. willDestroyElement: function() {
  300. this.removeObserver('serviceConfig.value', this, 'checkedBinding');
  301. },
  302. /***
  303. * defines if checkbox value appropriate to the config value
  304. * @returns {boolean}
  305. */
  306. isNotAppropriateValue: function() {
  307. return this.get('serviceConfig.value') !== this.get(this.get('checked') + 'Value');
  308. },
  309. /**
  310. * change service config value if click on checkbox
  311. */
  312. toggleValue: function() {
  313. if (this.isNotAppropriateValue()){
  314. this.set('serviceConfig.value', this.get(this.get('checked') + 'Value'));
  315. this.get('serviceConfig').set("editDone", true);
  316. this.sendRequestRorDependentConfigs(this.get('serviceConfig'));
  317. }
  318. }.observes('checked'),
  319. /**
  320. * change checkbox value if click on undo
  321. */
  322. toggleChecker: function() {
  323. if (this.isNotAppropriateValue())
  324. this.set('checked', !this.get('checked'));
  325. },
  326. disabled: function () {
  327. return !this.get('serviceConfig.isEditable');
  328. }.property('serviceConfig.isEditable'),
  329. //Set editDone false for all current category config text field parameter
  330. focusIn: function (event) {
  331. if (!this.get('serviceConfig.isOverridden') && !this.get('serviceConfig.isComparison')) {
  332. this.get("parentView.categoryConfigsAll").setEach("editDone", false);
  333. }
  334. }
  335. });
  336. /**
  337. * Checkbox control which can hide or show dependent properties
  338. * @type {*|void}
  339. */
  340. App.ServiceConfigCheckboxWithDependencies = App.ServiceConfigCheckbox.extend({
  341. toggleDependentConfigs: function() {
  342. if (this.get('serviceConfig.dependentConfigPattern')) {
  343. if (this.get('serviceConfig.dependentConfigPattern') === "CATEGORY") {
  344. this.disableEnableCategoryConfigs();
  345. } else {
  346. this.showHideDependentConfigs();
  347. }
  348. }
  349. }.observes('checked'),
  350. disableEnableCategoryConfigs: function () {
  351. this.get('categoryConfigsAll').setEach('isEditable', this.get('checked'));
  352. this.set('serviceConfig.isEditable', true);
  353. },
  354. showHideDependentConfigs: function () {
  355. this.get('categoryConfigsAll').forEach(function (c) {
  356. if (c.get('name').match(this.get('serviceConfig.dependentConfigPattern')) && c.get('name') != this.get('serviceConfig.name'))
  357. c.set('isVisible', this.get('checked'))
  358. }, this);
  359. }
  360. });
  361. App.ServiceConfigRadioButtons = Ember.View.extend(App.ServiceConfigCalculateId, App.SupportsDependentConfigs, {
  362. templateName: require('templates/wizard/controls_service_config_radio_buttons'),
  363. didInsertElement: function () {
  364. // on page render, automatically populate JDBC URLs only for default database settings
  365. // so as to not lose the user's customizations on these fields
  366. if (['addServiceController', 'installerController'].contains(this.get('controller.wizardController.name')) && !App.StackService.find(this.get('serviceConfig.serviceName')).get('isInstalled')) {
  367. if (this.get('isNewDb') || this.get('dontUseHandleDbConnection').contains(this.get('serviceConfig.name'))) {
  368. this.onOptionsChange();
  369. } else {
  370. if ((App.get('isHadoopWindowsStack') && this.get('inMSSQLWithIA')) || this.get('serviceConfig.name') === 'DB_FLAVOR') {
  371. this.onOptionsChange();
  372. }
  373. this.handleDBConnectionProperty();
  374. }
  375. }
  376. },
  377. /**
  378. * Radio buttons that are not DB options and should not trigger any observer or change any other property's value
  379. * Ranger service -> "Authentication method" property is an example for non DB related radio button
  380. */
  381. nonDBRadioButtons: function() {
  382. return this.get('dontUseHandleDbConnection').without('DB_FLAVOR');
  383. }.property('dontUseHandleDbConnection'),
  384. /**
  385. * properties with these names don'use handleDBConnectionProperty method
  386. */
  387. dontUseHandleDbConnection: function () {
  388. var version = App.get('currentStackVersion').match(/(\d+)[\.,]?(\d+)?/),
  389. majorVersion = version?version[1]: 0,
  390. minorVersion = version? version[2]: 0;
  391. // functionality added in HDP 2.3
  392. // remove DB_FLAVOR so it can handle DB Connection checks
  393. if (App.get('currentStackName') == 'HDP' && majorVersion >= 2 && minorVersion>= 3) {
  394. return ['ranger.authentication.method'];
  395. }
  396. return ['DB_FLAVOR', 'authentication_method'];
  397. }.property('App.currentStackName'),
  398. serviceConfig: null,
  399. categoryConfigsAll: null,
  400. /**
  401. * defines if new db is selected;
  402. * @type {boolean}
  403. */
  404. isNewDb: function() {
  405. return /New /g.test(this.get('serviceConfig.value'));
  406. }.property('serviceConfig.serviceName', 'serviceConfig.value'),
  407. /**
  408. * defines if 'Existing MSSQL Server database with integrated authentication' is selected
  409. * in this case some properties can have different behaviour
  410. * @type {boolean}
  411. */
  412. inMSSQLWithIA: function() {
  413. return this.get('serviceConfig.value') === 'Existing MSSQL Server database with integrated authentication';
  414. }.property('serviceConfig.value'),
  415. /**
  416. * Radio button has very uncomfortable values for managing it's state
  417. * so it's better to use code values that easier to manipulate. Ex:
  418. * "Existing MySQL Database" transforms to "MYSQL"
  419. * @type {string}
  420. */
  421. getDbTypeFromRadioValue: function() {
  422. var currentValue = this.get('serviceConfig.value');
  423. /** TODO: Remove SQLA from the list of databases once Ranger DB_FLAVOR=SQLA is replaced with SQL Anywhere */
  424. var databases = /MySQL|Postgres|Oracle|Derby|MSSQL|SQLA|Anywhere/gi;
  425. if (this.get('inMSSQLWithIA')) {
  426. return 'MSSQL2';
  427. } else {
  428. var matches = currentValue.match(databases);
  429. if (matches) {
  430. return currentValue.match(databases)[0].toUpperCase();
  431. } else {
  432. return "MYSQL";
  433. }
  434. }
  435. }.property('serviceConfig.serviceName', 'serviceConfig.value'),
  436. onOptionsChange: function () {
  437. this.sendRequestRorDependentConfigs(this.get('serviceConfig'));
  438. if (this.get('hostNameProperty') && !this.get('nonDBRadioButtons').contains(this.get('serviceConfig.name'))) {
  439. /** if new db is selected host name must be same as master of selected service (and can't be changed)**/
  440. if (this.get('isNewDb')) {
  441. var initProperty = this.get('hostNameProperty.recommendedValue') || this.get('hostNameProperty.savedValue');
  442. this.get('hostNameProperty').set('value', initProperty.toString());
  443. this.get('hostNameProperty').set('isEditable', false);
  444. } else {
  445. this.get('hostNameProperty').set('isEditable', true);
  446. }
  447. this.setRequiredProperties(['driver', 'db_type']);
  448. if (this.getPropertyByType('connection_url')) {
  449. this.setConnectionUrl(this.get('hostNameProperty.value'), this.get('databaseProperty.value'));
  450. }
  451. this.handleSpecialUserPassProperties();
  452. }
  453. }.observes('databaseProperty.value', 'hostNameProperty.value', 'serviceConfig.value'),
  454. nameBinding: 'serviceConfig.radioName',
  455. /**
  456. * Just property object for database name
  457. * @type {App.ServiceConfigProperty}
  458. */
  459. databaseProperty: function () {
  460. return this.getPropertyByType('db_name');
  461. }.property('serviceConfig.serviceName'),
  462. /**
  463. * Just property object for host name
  464. * @type {App.ServiceConfigProperty}
  465. */
  466. hostNameProperty: function () {
  467. var host = this.getPropertyByType('host_name');
  468. if (host && !host.get('value')) {
  469. if (host.get('savedValue')) {
  470. host.set('value', host.get('savedValue'));
  471. } else if (host.get('recommendedValue')) {
  472. host.set('value', host.get('recommendedValue'));
  473. }
  474. }
  475. return host;
  476. }.property('serviceConfig.serviceName', 'serviceConfig.value'),
  477. /**
  478. * Just property object for database name
  479. * @type {App.ServiceConfigProperty}
  480. */
  481. userProperty: function () {
  482. return this.getPropertyByType('user_name');
  483. }.property('serviceConfig.serviceName'),
  484. /**
  485. * Just property object for database name
  486. * @type {App.ServiceConfigProperty}
  487. */
  488. passwordProperty: function () {
  489. return this.getPropertyByType('password');
  490. }.property('serviceConfig.serviceName'),
  491. /**
  492. *
  493. * @param propertyType
  494. * @returns {*}
  495. */
  496. getDefaultPropertyValue: function(propertyType) {
  497. var dbProperties = dbInfo.dpPropertiesMap[this.get('getDbTypeFromRadioValue')],
  498. serviceName = this.get('serviceConfig.serviceName');
  499. return dbProperties[serviceName] && dbProperties[serviceName][propertyType]
  500. ? dbProperties[serviceName][propertyType] : dbProperties[propertyType];
  501. },
  502. /**
  503. *
  504. * @param propertyType
  505. * @returns {*|Object}
  506. */
  507. getPropertyByType: function(propertyType) {
  508. if (dbInfo.dpPropertiesByServiceMap[this.get('serviceConfig.serviceName')]) {
  509. //@TODO: dbInfo.dpPropertiesByServiceMap has corresponding property name but does not have filenames with it. this can cause issue when there are multiple db properties with same name belonging to different files
  510. /** check if selected service has db properties**/
  511. return this.get('controller.selectedService.configs').findProperty('name', dbInfo.dpPropertiesByServiceMap[this.get('serviceConfig.serviceName')][propertyType]);
  512. }
  513. return null;
  514. },
  515. /**
  516. * This method update <code>connection_url<code> property, using template described in <code>dpPropertiesMap<code>
  517. * and sets hostName as dbName in appropriate position of <code>connection_url<code> string
  518. * @param {String} hostName
  519. * @param {String} dbName
  520. * @method setConnectionUrl
  521. */
  522. setConnectionUrl: function(hostName, dbName) {
  523. var connectionUrlProperty = this.getPropertyByType('connection_url');
  524. var connectionUrlTemplate = this.getDefaultPropertyValue('connection_url');
  525. try {
  526. var connectionUrlValue = connectionUrlTemplate.format(hostName, dbName);
  527. connectionUrlProperty.set('value', connectionUrlValue);
  528. connectionUrlProperty.set('recommendedValue', connectionUrlValue);
  529. } catch(e) {
  530. console.error('connection url property or connection url template is missing');
  531. }
  532. return connectionUrlProperty;
  533. },
  534. /**
  535. * This method sets recommended values for properties <code>propertiesToUpdate<code> when radio button is changed
  536. * @param {String[]} propertiesToUpdate - contains type of properties that should be updated;
  537. * @method setRequiredProperties
  538. * @returns App.ServiceConfigProperty[]
  539. */
  540. setRequiredProperties: function (propertiesToUpdate) {
  541. propertiesToUpdate.forEach(function(pType) {
  542. var property = this.getPropertyByType(pType);
  543. var value = this.getDefaultPropertyValue(pType);
  544. if (property && value) {
  545. property.set('value', value);
  546. property.set('recommendedValue', value);
  547. }
  548. }, this);
  549. },
  550. /**
  551. * This method hides properties <code>user_name<code> and <code>password<code> in case selected db is
  552. * "Existing MSSQL Server database with integrated authentication" or similar
  553. * @method handleSpecialUserPassProperties
  554. */
  555. handleSpecialUserPassProperties: function() {
  556. ['user_name', 'password'].forEach(function(pType) {
  557. var property = this.getPropertyByType(pType);
  558. if (property) {
  559. property.setProperties({
  560. 'isVisible': !this.get('inMSSQLWithIA'),
  561. 'isRequired': !this.get('inMSSQLWithIA')
  562. });
  563. }
  564. }, this);
  565. },
  566. /**
  567. * `Observer` that add <code>additionalView</code> to <code>App.ServiceConfigProperty</code>
  568. * that responsible for (if existing db selected)
  569. * 1. checking database connection
  570. * 2. showing jdbc driver setup warning msg.
  571. *
  572. * @method handleDBConnectionProperty
  573. **/
  574. handleDBConnectionProperty: function() {
  575. if (this.get('dontUseHandleDbConnection').contains(this.get('serviceConfig.name'))) {
  576. return;
  577. }
  578. var handledProperties = ['oozie_database', 'hive_database', 'DB_FLAVOR'];
  579. var currentValue = this.get('serviceConfig.value');
  580. /** TODO: Remove SQLA from the list of databases once Ranger DB_FLAVOR=SQLA is replaced with SQL Anywhere */
  581. var databases = /MySQL|PostgreSQL|Postgres|Oracle|Derby|MSSQL|SQLA|Anywhere/gi;
  582. var currentDB = currentValue.match(databases)[0];
  583. /** TODO: Remove SQLA from the list of databases once Ranger DB_FLAVOR=SQLA is replaced with SQL Anywhere */
  584. var databasesTypes = /MySQL|Postgres|Oracle|Derby|MSSQL|SQLA|Anywhere/gi;
  585. var currentDBType = currentValue.match(databasesTypes)[0];
  586. var checkDatabase = /existing/gi.test(currentValue);
  587. // db connection check button show up if existed db selected
  588. var propertyAppendTo1 = this.get('categoryConfigsAll').findProperty('displayName', 'Database URL');
  589. // warning msg under database type radio buttons, to warn the user to setup jdbc driver if existed db selected
  590. var propertyHive = this.get('categoryConfigsAll').findProperty('displayName', 'Hive Database');
  591. var propertyOozie = this.get('categoryConfigsAll').findProperty('displayName', 'Oozie Database');
  592. var propertyAppendTo2 = propertyHive ? propertyHive : propertyOozie;
  593. // RANGER specific
  594. if (this.get('serviceConfig.serviceName') === 'RANGER') {
  595. propertyAppendTo1 = this.get('categoryConfigsAll').findProperty('name', 'ranger.jpa.jdbc.url');
  596. propertyAppendTo2 = this.get('categoryConfigsAll').findProperty('name', 'DB_FLAVOR');
  597. // check for all db types when installing Ranger - not only for existing ones
  598. checkDatabase = true;
  599. }
  600. if (propertyAppendTo1) {
  601. propertyAppendTo1.set('additionalView', null);
  602. }
  603. if (propertyAppendTo2) {
  604. propertyAppendTo2.set('additionalView', null);
  605. }
  606. var shouldAdditionalViewsBeSet = currentDB && checkDatabase && handledProperties.contains(this.get('serviceConfig.name')),
  607. driver = this.getDefaultPropertyValue('sql_jar_connector') ? this.getDefaultPropertyValue('sql_jar_connector').split("/").pop() : 'driver.jar',
  608. dbType = this.getDefaultPropertyValue('db_type'),
  609. additionalView1 = shouldAdditionalViewsBeSet ? App.CheckDBConnectionView.extend({databaseName: dbType}) : null,
  610. additionalView2 = shouldAdditionalViewsBeSet ? Ember.View.extend({
  611. template: Ember.Handlebars.compile('<div class="alert">{{{view.message}}}</div>'),
  612. message: function() {
  613. return Em.I18n.t('services.service.config.database.msg.jdbcSetup').format(dbType, driver);
  614. }.property()
  615. }) : null;
  616. if (propertyAppendTo1) {
  617. Em.run.next(function () {
  618. propertyAppendTo1.set('additionalView', additionalView1);
  619. });
  620. }
  621. if (propertyAppendTo2) {
  622. Em.run.next(function () {
  623. propertyAppendTo2.set('additionalView', additionalView2);
  624. });
  625. }
  626. }.observes('serviceConfig.value'),
  627. optionsBinding: 'serviceConfig.options'
  628. });
  629. App.ServiceConfigRadioButton = Ember.Checkbox.extend({
  630. tagName: 'input',
  631. attributeBindings: ['type', 'name', 'value', 'checked', 'disabled'],
  632. checked: false,
  633. type: 'radio',
  634. name: null,
  635. value: null,
  636. didInsertElement: function () {
  637. console.debug('App.ServiceConfigRadioButton.didInsertElement');
  638. if (this.get('parentView.serviceConfig.value') === this.get('value')) {
  639. console.debug(this.get('name') + ":" + this.get('value') + ' is checked');
  640. this.set('checked', true);
  641. }
  642. },
  643. click: function () {
  644. this.set('checked', true);
  645. console.debug('App.ServiceConfigRadioButton.click');
  646. this.onChecked();
  647. },
  648. onChecked: function () {
  649. // Wrapping the call with Ember.run.next prevents a problem where setting isVisible on component
  650. // causes JS error due to re-rendering. For example, this occurs when switching the Config Group
  651. // in Service Config page
  652. Em.run.next(this, function() {
  653. console.debug('App.ServiceConfigRadioButton.onChecked');
  654. this.set('parentView.serviceConfig.value', this.get('value'));
  655. var components = this.get('parentView.serviceConfig.options');
  656. if (components && components.someProperty('foreignKeys')) {
  657. this.get('controller.stepConfigs').findProperty('serviceName', this.get('parentView.serviceConfig.serviceName')).propertyDidChange('errorCount');
  658. }
  659. });
  660. }.observes('checked'),
  661. disabled: function () {
  662. return !this.get('parentView.serviceConfig.isEditable') ||
  663. !['addServiceController', 'installerController'].contains(this.get('controller.wizardController.name')) && /^New\s\w+\sDatabase$/.test(this.get('value'));
  664. }.property('parentView.serviceConfig.isEditable')
  665. });
  666. App.ServiceConfigComboBox = Ember.Select.extend(App.ServiceConfigPopoverSupport, App.ServiceConfigCalculateId, App.SupportsDependentConfigs, {
  667. contentBinding: 'serviceConfig.options',
  668. selectionBinding: 'serviceConfig.value',
  669. placeholderBinding: 'serviceConfig.savedValue',
  670. classNames: [ 'span3' ]
  671. });
  672. /**
  673. * Base component for host config with popover support
  674. */
  675. App.ServiceConfigHostPopoverSupport = Ember.Mixin.create({
  676. /**
  677. * Config object. It will instance of App.ServiceConfigProperty
  678. */
  679. serviceConfig: null,
  680. didInsertElement: function () {
  681. App.popover(this.$(), {
  682. title: this.get('serviceConfig.displayName'),
  683. content: this.get('serviceConfig.description'),
  684. placement: 'right',
  685. trigger: 'hover'
  686. });
  687. }
  688. });
  689. /**
  690. * Master host component.
  691. * Show hostname without ability to edit it
  692. * @type {*}
  693. */
  694. App.ServiceConfigMasterHostView = Ember.View.extend(App.ServiceConfigHostPopoverSupport, App.ServiceConfigCalculateId, {
  695. classNames: ['master-host', 'span6'],
  696. valueBinding: 'serviceConfig.value',
  697. template: Ember.Handlebars.compile('{{value}}')
  698. });
  699. /**
  700. * text field property view that enables possibility
  701. * for check connection
  702. * @type {*}
  703. */
  704. App.checkConnectionView = App.ServiceConfigTextField.extend({
  705. didInsertElement: function() {
  706. this._super();
  707. var kdc = this.get('categoryConfigsAll').findProperty('name', 'kdc_type');
  708. var propertyAppendTo = this.get('categoryConfigsAll').findProperty('name', 'domains');
  709. if (propertyAppendTo) {
  710. try {
  711. propertyAppendTo.set('additionalView', App.CheckDBConnectionView.extend({databaseName: kdc && kdc.get('value')}));
  712. } catch (e) {
  713. console.error('error while adding "Test connection button"');
  714. }
  715. }
  716. }
  717. });
  718. /**
  719. * Show value as plain label in italics
  720. * @type {*}
  721. */
  722. App.ServiceConfigLabelView = Ember.View.extend(App.ServiceConfigHostPopoverSupport, App.ServiceConfigCalculateId, {
  723. classNames: ['master-host', 'span6'],
  724. valueBinding: 'serviceConfig.value',
  725. template: Ember.Handlebars.compile('<i>{{view.value}}</i>')
  726. });
  727. /**
  728. * Base component to display Multiple hosts
  729. * @type {*}
  730. */
  731. App.ServiceConfigMultipleHostsDisplay = Ember.Mixin.create(App.ServiceConfigHostPopoverSupport, App.ServiceConfigCalculateId, {
  732. hasNoHosts: function () {
  733. console.log('view', this.get('viewName')); //to know which View cause errors
  734. console.log('controller', this.get('controller').name); //should be slaveComponentGroupsController
  735. if (!this.get('value')) {
  736. return true;
  737. }
  738. return this.get('value').length === 0;
  739. }.property('value'),
  740. formatValue: function() {
  741. if (Em.isArray(this.get('value')) && this.get('value').length === 1) {
  742. return this.get('value.firstObject');
  743. } else {
  744. return this.get('value');
  745. }
  746. }.property('value'),
  747. hasOneHost: function () {
  748. return !Em.isArray(this.get('value')) || this.get('value').length === 1;
  749. }.property('value'),
  750. hasMultipleHosts: function () {
  751. return Em.isArray(this.get('value')) && this.get('value').length > 1;
  752. }.property('value'),
  753. otherLength: function () {
  754. var len = this.get('value').length;
  755. if (len > 2) {
  756. return Em.I18n.t('installer.controls.serviceConfigMultipleHosts.others').format(len - 1);
  757. } else {
  758. return Em.I18n.t('installer.controls.serviceConfigMultipleHosts.other');
  759. }
  760. }.property('value')
  761. });
  762. /**
  763. * Multiple Slave Hosts component
  764. * @type {*}
  765. */
  766. App.ServiceConfigComponentHostsView = Ember.View.extend(App.ServiceConfigMultipleHostsDisplay, App.ServiceConfigCalculateId, {
  767. viewName: 'serviceConfigSlaveHostsView',
  768. classNames: ['component-hosts', 'span6'],
  769. valueBinding: 'serviceConfig.value',
  770. templateName: require('templates/wizard/component_hosts'),
  771. /**
  772. * Onclick handler for link
  773. */
  774. showHosts: function () {
  775. var serviceConfig = this.get('serviceConfig');
  776. App.ModalPopup.show({
  777. header: Em.I18n.t('installer.controls.serviceConfigMasterHosts.header').format(serviceConfig.category),
  778. bodyClass: Ember.View.extend({
  779. serviceConfig: serviceConfig,
  780. templateName: require('templates/wizard/component_hosts_popup')
  781. }),
  782. secondary: null
  783. });
  784. }
  785. });
  786. /**
  787. * DropDown component for <code>select hosts for groups</code> popup
  788. * @type {*}
  789. */
  790. App.SlaveComponentDropDownGroupView = Ember.View.extend(App.ServiceConfigCalculateId, {
  791. viewName: "slaveComponentDropDownGroupView",
  792. /**
  793. * On change handler for <code>select hosts for groups</code> popup
  794. * @param event
  795. */
  796. changeGroup: function (event) {
  797. var host = this.get('content');
  798. var groupName = $('#' + this.get('elementId') + ' select').val();
  799. this.get('controller').changeHostGroup(host, groupName);
  800. },
  801. optionTag: Ember.View.extend({
  802. /**
  803. * Whether current value(OptionTag value) equals to host value(assigned to SlaveComponentDropDownGroupView.content)
  804. */
  805. selected: function () {
  806. return this.get('parentView.content.group') === this.get('content');
  807. }.property('content')
  808. })
  809. });
  810. /**
  811. * View for testing connection to database.
  812. **/
  813. App.CheckDBConnectionView = Ember.View.extend({
  814. templateName: require('templates/common/form/check_db_connection'),
  815. /** @property {string} btnCaption - text for button **/
  816. btnCaption: function() {
  817. return this.get('parentView.service.serviceName') === 'KERBEROS'
  818. ? Em.I18n.t('services.service.config.kdc.btn.idle')
  819. : Em.I18n.t('services.service.config.database.btn.idle')
  820. }.property('parentView.service.serviceName'),
  821. /** @property {string} responseCaption - text for status link **/
  822. responseCaption: null,
  823. /** @property {boolean} isConnecting - is request to server activated **/
  824. isConnecting: false,
  825. /** @property {boolean} isValidationPassed - check validation for required fields **/
  826. isValidationPassed: null,
  827. /** @property {string} databaseName- name of current database **/
  828. databaseName: null,
  829. /** @property {boolean} isRequestResolved - check for finished request to server **/
  830. isRequestResolved: false,
  831. /** @property {boolean} isConnectionSuccess - check for successful connection to database **/
  832. isConnectionSuccess: null,
  833. /** @property {string} responseFromServer - message from server response **/
  834. responseFromServer: null,
  835. /** @property {Object} ambariRequiredProperties - properties that need for custom action request **/
  836. ambariRequiredProperties: null,
  837. /** @property {Number} currentRequestId - current custom action request id **/
  838. currentRequestId: null,
  839. /** @property {Number} currentTaskId - current custom action task id **/
  840. currentTaskId: null,
  841. /** @property {jQuery.Deferred} request - current $.ajax request **/
  842. request: null,
  843. /** @property {Number} pollInterval - timeout interval for ajax polling **/
  844. pollInterval: 3000,
  845. /** @property {Object} logsPopup - popup with DB connection check info **/
  846. logsPopup: null,
  847. /** @property {string} hostNameProperty - host name property based on service and database names **/
  848. hostNameProperty: function() {
  849. if (!/wizard/i.test(this.get('controller.name')) && this.get('parentView.service.serviceName') === 'HIVE') {
  850. return this.get('parentView.service.serviceName').toLowerCase() + '_hostname';
  851. } else if (this.get('parentView.service.serviceName') === 'KERBEROS') {
  852. return 'kdc_host';
  853. } else if (this.get('parentView.service.serviceName') === 'RANGER') {
  854. return '{0}_{1}_host'.format(this.get('parentView.service.serviceName').toLowerCase(), this.get('databaseName').toLowerCase());
  855. }
  856. return '{0}_existing_{1}_host'.format(this.get('parentView.service.serviceName').toLowerCase(), this.get('databaseName').toLowerCase());
  857. }.property('databaseName'),
  858. /** @property {boolean} isBtnDisabled - disable button on failed validation or active request **/
  859. isBtnDisabled: function() {
  860. return !this.get('isValidationPassed') || this.get('isConnecting');
  861. }.property('isValidationPassed', 'isConnecting'),
  862. /** @property {object} requiredProperties - properties that necessary for database connection **/
  863. requiredProperties: function() {
  864. var propertiesMap = {
  865. 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'],
  866. HIVE: ['ambari.hive.db.schema.name', 'javax.jdo.option.ConnectionUserName', 'javax.jdo.option.ConnectionPassword', 'javax.jdo.option.ConnectionDriverName', 'javax.jdo.option.ConnectionURL'],
  867. KERBEROS: ['kdc_host'],
  868. RANGER: App.get('isHadoop23Stack') ? ['db_user', 'db_password', 'db_name', 'ranger.jpa.jdbc.url', 'ranger.jpa.jdbc.driver'] :
  869. ['db_user', 'db_password', 'db_name', 'ranger_jdbc_connection_url', 'ranger_jdbc_driver']
  870. };
  871. return propertiesMap[this.get('parentView.service.serviceName')];
  872. }.property('App.isHadoop23Stack'),
  873. /** @property {Object} propertiesPattern - check pattern according to type of connection properties **/
  874. propertiesPattern: function() {
  875. var patterns = {
  876. db_connection_url: /jdbc\.url|connection_url|connectionurl|kdc_host/ig
  877. };
  878. if (this.get('parentView.service.serviceName') != "KERBEROS") {
  879. patterns.user_name = /(username|dblogin|db_user)$/ig;
  880. patterns.user_passwd = /(dbpassword|password|db_password)$/ig;
  881. }
  882. return patterns;
  883. }.property('parentView.service.serviceName'),
  884. /** @property {String} masterHostName - host name location of Master Component related to Service **/
  885. masterHostName: function() {
  886. var serviceMasterMap = {
  887. 'OOZIE': 'oozie_server_hosts',
  888. 'HDFS': 'hadoop_host',
  889. 'HIVE': 'hive_metastore_hosts',
  890. 'KERBEROS': 'kdc_host',
  891. 'RANGER': 'ranger_server_hosts'
  892. };
  893. return this.get('parentView.categoryConfigsAll').findProperty('name', serviceMasterMap[this.get('parentView.service.serviceName')]).get('value');
  894. }.property('parentView.service.serviceName', 'parentView.categoryConfigsAll.@each.value'),
  895. /** @property {Object} connectionProperties - service specific config values mapped for custom action request **/
  896. connectionProperties: function() {
  897. var propObj = {};
  898. for (var key in this.get('propertiesPattern')) {
  899. propObj[key] = this.getConnectionProperty(this.get('propertiesPattern')[key]);
  900. }
  901. if (this.get('parentView.service.serviceName') === 'RANGER') {
  902. var dbFlavor = this.get('parentView.categoryConfigsAll').findProperty('name','DB_FLAVOR').get('value'),
  903. /** TODO: Remove SQLA from the list of databases once Ranger DB_FLAVOR=SQLA is replaced with SQL Anywhere */
  904. databasesTypes = /MYSQL|POSTGRES|ORACLE|MSSQL|SQLA|Anywhere/gi,
  905. dbType = dbFlavor.match(databasesTypes)?dbFlavor.match(databasesTypes)[0].toLowerCase():'';
  906. if (dbType==='oracle') {
  907. // fixes oracle SYSDBA issue
  908. propObj['user_name'] = "\'%@ as sysdba\'".fmt(propObj['user_name']);
  909. }
  910. }
  911. return propObj;
  912. }.property('parentView.categoryConfigsAll.@each.value'),
  913. /**
  914. * Properties that stores in local storage used for handling
  915. * last success connection.
  916. *
  917. * @property {Object} preparedDBProperties
  918. **/
  919. preparedDBProperties: function() {
  920. var propObj = {};
  921. for (var key in this.get('propertiesPattern')) {
  922. var propName = this.getConnectionProperty(this.get('propertiesPattern')[key], true);
  923. propObj[propName] = this.get('parentView.categoryConfigsAll').findProperty('name', propName).get('value');
  924. }
  925. return propObj;
  926. }.property(),
  927. /** Check validation and load ambari properties **/
  928. didInsertElement: function() {
  929. var kdc = this.get('parentView.categoryConfigsAll').findProperty('name', 'kdc_type');
  930. if (kdc) {
  931. var name = kdc.get('value') == 'Existing MIT KDC' ? 'KDC' : 'AD';
  932. App.popover(this.$(), {
  933. title: Em.I18n.t('services.service.config.database.btn.idle'),
  934. content: Em.I18n.t('installer.controls.checkConnection.popover').format(name),
  935. placement: 'right',
  936. trigger: 'hover'
  937. });
  938. }
  939. this.handlePropertiesValidation();
  940. this.getAmbariProperties();
  941. },
  942. /** On view destroy **/
  943. willDestroyElement: function() {
  944. this.set('isConnecting', false);
  945. this._super();
  946. },
  947. /**
  948. * Observer that take care about enabling/disabling button based on required properties validation.
  949. *
  950. * @method handlePropertiesValidation
  951. **/
  952. handlePropertiesValidation: function() {
  953. this.restore();
  954. var isValid = true;
  955. var properties = [].concat(this.get('requiredProperties'));
  956. properties.push(this.get('hostNameProperty'));
  957. properties.forEach(function(propertyName) {
  958. var property = this.get('parentView.categoryConfigsAll').findProperty('name', propertyName);
  959. if(property && !property.get('isValid')) isValid = false;
  960. }, this);
  961. this.set('isValidationPassed', isValid);
  962. }.observes('parentView.categoryConfigsAll.@each.isValid', 'parentView.categoryConfigsAll.@each.value', 'databaseName'),
  963. getConnectionProperty: function(regexp, isGetName) {
  964. var _this = this;
  965. var propertyName = _this.get('requiredProperties').filter(function(item) {
  966. return regexp.test(item);
  967. })[0];
  968. return (isGetName) ? propertyName : _this.get('parentView.categoryConfigsAll').findProperty('name', propertyName).get('value');
  969. },
  970. /**
  971. * Set up ambari properties required for custom action request
  972. *
  973. * @method getAmbariProperties
  974. **/
  975. getAmbariProperties: function() {
  976. var clusterController = App.router.get('clusterController');
  977. var _this = this;
  978. if (!App.isEmptyObject(App.db.get('tmp', 'ambariProperties')) && !this.get('ambariProperties')) {
  979. this.set('ambariProperties', App.db.get('tmp', 'ambariProperties'));
  980. return;
  981. }
  982. if (App.isEmptyObject(clusterController.get('ambariProperties'))) {
  983. clusterController.loadAmbariProperties().done(function(data) {
  984. _this.formatAmbariProperties(data.RootServiceComponents.properties);
  985. });
  986. } else {
  987. this.formatAmbariProperties(clusterController.get('ambariProperties'));
  988. }
  989. },
  990. formatAmbariProperties: function(properties) {
  991. var defaults = {
  992. threshold: "60",
  993. ambari_server_host: location.hostname,
  994. check_execute_list : "db_connection_check"
  995. };
  996. var properties = App.permit(properties, ['jdk.name','jdk_location','java.home']);
  997. var renameKey = function(oldKey, newKey) {
  998. if (properties[oldKey]) {
  999. defaults[newKey] = properties[oldKey];
  1000. delete properties[oldKey];
  1001. }
  1002. };
  1003. renameKey('java.home', 'java_home');
  1004. renameKey('jdk.name', 'jdk_name');
  1005. $.extend(properties, defaults);
  1006. App.db.set('tmp', 'ambariProperties', properties);
  1007. this.set('ambariProperties', properties);
  1008. },
  1009. /**
  1010. * `Action` method for starting connect to current database.
  1011. *
  1012. * @method connectToDatabase
  1013. **/
  1014. connectToDatabase: function() {
  1015. if (this.get('isBtnDisabled')) return;
  1016. this.set('isRequestResolved', false);
  1017. App.db.set('tmp', this.get('parentView.service.serviceName') + '_connection', {});
  1018. this.setConnectingStatus(true);
  1019. if (App.get('testMode')) {
  1020. this.startPolling();
  1021. } else {
  1022. this.runCheckConnection();
  1023. }
  1024. },
  1025. /**
  1026. * runs check connections methods depending on service
  1027. * @return {void}
  1028. * @method runCheckConnection
  1029. */
  1030. runCheckConnection: function() {
  1031. if (this.get('parentView.service.serviceName') === 'KERBEROS') {
  1032. this.runKDCCheck();
  1033. } else {
  1034. this.createCustomAction();
  1035. }
  1036. },
  1037. /**
  1038. * send ajax request to perforn kdc host check
  1039. * @return {App.ajax}
  1040. * @method runKDCCheck
  1041. */
  1042. runKDCCheck: function() {
  1043. return App.ajax.send({
  1044. name: 'admin.kerberos_security.test_connection',
  1045. sender: this,
  1046. data: {
  1047. kdcHostname: this.get('masterHostName')
  1048. },
  1049. success: 'onRunKDCCheckSuccess',
  1050. error: 'onCreateActionError'
  1051. });
  1052. },
  1053. /**
  1054. *
  1055. * @param data
  1056. */
  1057. onRunKDCCheckSuccess: function(data) {
  1058. var statusCode = {
  1059. success: 'REACHABLE',
  1060. failed: 'UNREACHABLE'
  1061. };
  1062. if (data == statusCode['success']) {
  1063. this.setResponseStatus('success');
  1064. } else {
  1065. this.setResponseStatus('failed');
  1066. }
  1067. this.set('responseFromServer', data);
  1068. },
  1069. /**
  1070. * Run custom action for database connection.
  1071. *
  1072. * @method createCustomAction
  1073. **/
  1074. createCustomAction: function() {
  1075. var params = $.extend(true, {}, { db_name: this.get('databaseName').toLowerCase() }, this.get('connectionProperties'), this.get('ambariProperties'));
  1076. App.ajax.send({
  1077. name: 'custom_action.create',
  1078. sender: this,
  1079. data: {
  1080. requestInfo: {
  1081. parameters: params
  1082. },
  1083. filteredHosts: [this.get('masterHostName')]
  1084. },
  1085. success: 'onCreateActionSuccess',
  1086. error: 'onCreateActionError'
  1087. });
  1088. },
  1089. /**
  1090. * Run updater if task is created successfully.
  1091. *
  1092. * @method onConnectActionS
  1093. **/
  1094. onCreateActionSuccess: function(data) {
  1095. this.set('currentRequestId', data.Requests.id);
  1096. App.ajax.send({
  1097. name: 'custom_action.request',
  1098. sender: this,
  1099. data: {
  1100. requestId: this.get('currentRequestId')
  1101. },
  1102. success: 'setCurrentTaskId'
  1103. });
  1104. },
  1105. setCurrentTaskId: function(data) {
  1106. this.set('currentTaskId', data.items[0].Tasks.id);
  1107. this.startPolling();
  1108. },
  1109. startPolling: function() {
  1110. if (this.get('isConnecting'))
  1111. this.getTaskInfo();
  1112. },
  1113. getTaskInfo: function() {
  1114. var request = App.ajax.send({
  1115. name: 'custom_action.request',
  1116. sender: this,
  1117. data: {
  1118. requestId: this.get('currentRequestId'),
  1119. taskId: this.get('currentTaskId')
  1120. },
  1121. success: 'getTaskInfoSuccess'
  1122. });
  1123. this.set('request', request);
  1124. },
  1125. getTaskInfoSuccess: function(data) {
  1126. var task = data.Tasks;
  1127. this.set('responseFromServer', {
  1128. stderr: task.stderr,
  1129. stdout: task.stdout
  1130. });
  1131. if (task.status === 'COMPLETED') {
  1132. var structuredOut = task.structured_out.db_connection_check;
  1133. if (structuredOut.exit_code != 0) {
  1134. this.set('responseFromServer', {
  1135. stderr: task.stderr,
  1136. stdout: task.stdout,
  1137. structuredOut: structuredOut.message
  1138. });
  1139. this.setResponseStatus('failed');
  1140. } else {
  1141. App.db.set('tmp', this.get('parentView.service.serviceName') + '_connection', this.get('preparedDBProperties'));
  1142. this.setResponseStatus('success');
  1143. }
  1144. }
  1145. if (task.status === 'FAILED') {
  1146. this.setResponseStatus('failed');
  1147. }
  1148. if (/PENDING|QUEUED|IN_PROGRESS/.test(task.status)) {
  1149. Em.run.later(this, function() {
  1150. this.startPolling();
  1151. }, this.get('pollInterval'));
  1152. }
  1153. },
  1154. onCreateActionError: function(jqXhr, status, errorMessage) {
  1155. this.setResponseStatus('failed');
  1156. this.set('responseFromServer', errorMessage);
  1157. },
  1158. setResponseStatus: function(isSuccess) {
  1159. var isSuccess = isSuccess == 'success';
  1160. this.setConnectingStatus(false);
  1161. this.set('responseCaption', isSuccess ? Em.I18n.t('services.service.config.database.connection.success') : Em.I18n.t('services.service.config.database.connection.failed'));
  1162. this.set('isConnectionSuccess', isSuccess);
  1163. this.set('isRequestResolved', true);
  1164. if (this.get('logsPopup')) {
  1165. var statusString = isSuccess ? 'common.success' : 'common.error';
  1166. this.set('logsPopup.header', Em.I18n.t('services.service.config.connection.logsPopup.header').format(this.get('databaseName'), Em.I18n.t(statusString)));
  1167. }
  1168. },
  1169. /**
  1170. * Switch captions and statuses for active/non-active request.
  1171. *
  1172. * @method setConnectionStatus
  1173. * @param {Boolean} [active]
  1174. */
  1175. setConnectingStatus: function(active) {
  1176. if (active) {
  1177. this.set('responseCaption', Em.I18n.t('services.service.config.database.connection.inProgress'));
  1178. }
  1179. this.set('controller.testConnectionInProgress', !!active);
  1180. this.set('btnCaption', !!active ? Em.I18n.t('services.service.config.database.btn.connecting') : Em.I18n.t('services.service.config.database.btn.idle'));
  1181. this.set('isConnecting', !!active);
  1182. },
  1183. /**
  1184. * Set view to init status.
  1185. *
  1186. * @method restore
  1187. **/
  1188. restore: function() {
  1189. if (this.get('request')) {
  1190. this.get('request').abort();
  1191. this.set('request', null);
  1192. }
  1193. this.set('responseCaption', null);
  1194. this.set('responseFromServer', null);
  1195. this.setConnectingStatus(false);
  1196. this.set('isRequestResolved', false);
  1197. },
  1198. /**
  1199. * `Action` method for showing response from server in popup.
  1200. *
  1201. * @method showLogsPopup
  1202. **/
  1203. showLogsPopup: function() {
  1204. if (this.get('isConnectionSuccess')) return;
  1205. var _this = this;
  1206. var statusString = this.get('isRequestResolved') ? 'common.error' : 'common.testing';
  1207. var popup = App.showAlertPopup(Em.I18n.t('services.service.config.connection.logsPopup.header').format(this.get('databaseName'), Em.I18n.t(statusString)), null, function () {
  1208. _this.set('logsPopup', null);
  1209. });
  1210. popup.reopen({
  1211. onClose: function () {
  1212. this._super();
  1213. _this.set('logsPopup', null);
  1214. }
  1215. });
  1216. if (typeof this.get('responseFromServer') == 'object') {
  1217. popup.set('bodyClass', Em.View.extend({
  1218. checkDBConnectionView: _this,
  1219. templateName: require('templates/common/error_log_body'),
  1220. openedTask: function () {
  1221. return this.get('checkDBConnectionView.responseFromServer');
  1222. }.property('checkDBConnectionView.responseFromServer.stderr', 'checkDBConnectionView.responseFromServer.stdout', 'checkDBConnectionView.responseFromServer.structuredOut')
  1223. }));
  1224. } else {
  1225. popup.set('body', this.get('responseFromServer'));
  1226. }
  1227. this.set('logsPopup', popup);
  1228. return popup;
  1229. }
  1230. });
  1231. /**
  1232. * View with input field used to repo-version URLs
  1233. * @type {*}
  1234. */
  1235. App.BaseUrlTextField = Ember.TextField.extend({
  1236. layout: Ember.Handlebars.compile('<div class="pull-left">{{yield}}</div> {{#if view.valueWasChanged}}<div class="pull-right"><a class="btn-small" {{action "restoreValue" target="view"}}><i class="icon-undo"></i></a></div>{{/if}}'),
  1237. /**
  1238. * Binding in the template
  1239. * @type {App.RepositoryVersion}
  1240. */
  1241. repository: null,
  1242. /**
  1243. * @type {string}
  1244. */
  1245. valueBinding: 'repository.baseUrl',
  1246. /**
  1247. * @type {string}
  1248. */
  1249. defaultValue: '',
  1250. /**
  1251. * validate base URL
  1252. */
  1253. validate: function () {
  1254. if (this.get('repository.skipValidation')) {
  1255. this.set('repository.hasError', false);
  1256. } else {
  1257. this.set('repository.hasError', !(validator.isValidBaseUrl(this.get('value'))));
  1258. }
  1259. this.get('parentView').uiValidation();
  1260. }.observes('value', 'repository.skipValidation'),
  1261. /**
  1262. * Determines if user have put some new value
  1263. * @type {boolean}
  1264. */
  1265. valueWasChanged: function () {
  1266. return this.get('value') !== this.get('defaultValue');
  1267. }.property('value', 'defaultValue'),
  1268. didInsertElement: function () {
  1269. this.set('defaultValue', this.get('value'));
  1270. },
  1271. /**
  1272. * Restore value and unset error-flag
  1273. * @method restoreValue
  1274. */
  1275. restoreValue: function () {
  1276. this.set('value', this.get('defaultValue'));
  1277. }
  1278. });