controls_view.js 62 KB

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