controls_view.js 51 KB

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