item.js 50 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400
  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 batchUtils = require('utils/batch_scheduled_requests');
  20. var blueprintUtils = require('utils/blueprint');
  21. App.MainServiceItemController = Em.Controller.extend(App.SupportClientConfigsDownload, App.InstallComponent, App.ConfigsSaverMixin, App.EnhancedConfigsMixin, {
  22. name: 'mainServiceItemController',
  23. /**
  24. * Callback functions for start and stop service have few differences
  25. *
  26. * Used with currentCallBack property
  27. */
  28. callBackConfig: {
  29. 'STARTED': {
  30. 'c': 'STARTING',
  31. 'f': 'starting',
  32. 'c2': 'live',
  33. 'hs': 'started',
  34. 's': 'start'
  35. },
  36. 'INSTALLED': {
  37. 'c': 'STOPPING',
  38. 'f': 'stopping',
  39. 'c2': 'dead',
  40. 'hs': 'stopped',
  41. 's': 'stop'
  42. }
  43. },
  44. /**
  45. * Map of service names and lists of sites they need to load
  46. */
  47. serviceConfigsMap: {
  48. 'OOZIE': ['oozie-env']
  49. },
  50. /**
  51. * Configs loaded to use for service actions menu
  52. *
  53. * format: {config-type: {property-name1: property-value1, property-name2: property-value2, ...}}
  54. */
  55. configs: {},
  56. /**
  57. * @type {boolean}
  58. * @default true
  59. */
  60. isPending: true,
  61. /**
  62. * @type {boolean}
  63. * @default false
  64. */
  65. isServicesInfoLoaded: false,
  66. /**
  67. * Define whether configs for service actions menu were loaded
  68. * @type {Boolean}
  69. */
  70. isServiceConfigsLoaded: false,
  71. /**
  72. * flag to control router switch between service summary and configs
  73. * @type {boolean}
  74. */
  75. routeToConfigs: false,
  76. isClientsOnlyService: function() {
  77. return App.get('services.clientOnly').contains(this.get('content.serviceName'));
  78. }.property('content.serviceName'),
  79. isConfigurable: function () {
  80. return !App.get('services.noConfigTypes').contains(this.get('content.serviceName'));
  81. }.property('App.services.noConfigTypes','content.serviceName'),
  82. clientComponents: function () {
  83. var clientNames = [];
  84. var clients = App.StackServiceComponent.find().filterProperty('serviceName', this.get('content.serviceName')).filterProperty('isClient');
  85. clients.forEach(function (item) {
  86. clientNames.push({
  87. action: 'downloadClientConfigs',
  88. context: {
  89. name: item.get('componentName'),
  90. label: item.get('displayName')
  91. }
  92. });
  93. });
  94. return clientNames;
  95. }.property('content.serviceName'),
  96. /**
  97. * Returns interdependent services
  98. *
  99. * @returns {string[]}
  100. */
  101. interDependentServices: function() {
  102. var serviceName = this.get('content.serviceName'), interDependentServices = [];
  103. App.StackService.find(serviceName).get('requiredServices').forEach(function(requiredService) {
  104. if (App.StackService.find(requiredService).get('requiredServices').contains(serviceName)) {
  105. interDependentServices.push(requiredService);
  106. }
  107. });
  108. return interDependentServices;
  109. }.property('content.serviceName'),
  110. /**
  111. * collection of serviceConfigs
  112. *
  113. * @type {Object[]}
  114. */
  115. stepConfigs: [],
  116. /**
  117. * List of service names that have configs dependent on current service configs
  118. *
  119. * @type {String[]}
  120. */
  121. dependentServiceNames: function() {
  122. return App.StackService.find(this.get('content.serviceName')).get('dependentServiceNames');
  123. }.property('content.serviceName'),
  124. /**
  125. * List of service names that could be deleted
  126. * Common case when there is only current service should be removed
  127. * But for some services there is <code>interDependentServices<code> services
  128. * Like 'YARN' depends on 'MAPREDUCE2' and 'MAPREDUCE2' depends on 'YARN'
  129. * So these services can be removed only together
  130. *
  131. * @type {String[]}
  132. */
  133. serviceNamesToDelete: function() {
  134. return [this.get('content.serviceName')].concat(this.get('interDependentServices'));
  135. }.property('content.serviceName'),
  136. /**
  137. * List of config types that should be loaded
  138. * Includes
  139. * 1. Dependent services config-types
  140. * 2. Some special cases from <code>serviceConfigsMap<code>
  141. * 3. 'cluster-env'
  142. *
  143. * @type {String[]}
  144. */
  145. sitesToLoad: function() {
  146. var services = this.get('dependentServiceNames'), configTypeList = [];
  147. if (services.length) {
  148. var configTypeList = App.StackService.find().filter(function(s) {
  149. return services.contains(s.get('serviceName'));
  150. }).mapProperty('configTypeList').reduce(function(p, v) {
  151. return p.concat(v);
  152. });
  153. }
  154. if (this.get('serviceConfigsMap')[this.get('content.serviceName')]) {
  155. configTypeList = configTypeList.concat(this.get('serviceConfigsMap')[this.get('content.serviceName')]);
  156. }
  157. configTypeList.push('cluster-env');
  158. return configTypeList.uniq();
  159. }.property('content.serviceName'),
  160. /**
  161. * Load all config tags for loading configs
  162. */
  163. loadConfigs: function(){
  164. this.set('isServiceConfigsLoaded', false);
  165. App.ajax.send({
  166. name: 'config.tags',
  167. sender: this,
  168. success: 'onLoadConfigsTags',
  169. error: 'onTaskError'
  170. });
  171. },
  172. /**
  173. * Load all configs for sites from <code>serviceConfigsMap</code> for current service
  174. * @param data
  175. */
  176. onLoadConfigsTags: function (data) {
  177. var self = this;
  178. var sitesToLoad = this.get('sitesToLoad'), allConfigs = [];
  179. var loadedSites = data.Clusters.desired_configs;
  180. var siteTagsToLoad = [];
  181. for (var site in loadedSites) {
  182. if (sitesToLoad.contains(site)) {
  183. siteTagsToLoad.push({
  184. siteName: site,
  185. tagName: loadedSites[site].tag
  186. });
  187. }
  188. }
  189. App.router.get('configurationController').getConfigsByTags(siteTagsToLoad).done(function (configs) {
  190. configs.forEach(function (site) {
  191. self.get('configs')[site.type] = site.properties;
  192. allConfigs = allConfigs.concat(App.config.getConfigsFromJSON(site, true));
  193. });
  194. self.get('dependentServiceNames').forEach(function(serviceName) {
  195. var configTypes = App.StackService.find(serviceName).get('configTypeList');
  196. var configsByService = allConfigs.filter(function (c) {
  197. return configTypes.contains(App.config.getConfigTagFromFileName(c.get('filename')));
  198. });
  199. self.get('stepConfigs').pushObject(App.config.createServiceConfig(serviceName, [], configsByService));
  200. });
  201. self.set('isServiceConfigsLoaded', true);
  202. });
  203. },
  204. /**
  205. * Common method for ajax (start/stop service) responses
  206. * @param data
  207. * @param ajaxOptions
  208. * @param params
  209. */
  210. startStopPopupSuccessCallback: function (data, ajaxOptions, params) {
  211. if (data && data.Requests) {
  212. params.query.set('status', 'SUCCESS');
  213. var config = this.get('callBackConfig')[(JSON.parse(ajaxOptions.data)).Body.ServiceInfo.state];
  214. var self = this;
  215. if (App.get('testMode')) {
  216. self.set('content.workStatus', App.Service.Health[config.f]);
  217. self.get('content.hostComponents').setEach('workStatus', App.HostComponentStatus[config.f]);
  218. setTimeout(function () {
  219. self.set('content.workStatus', App.Service.Health[config.c2]);
  220. self.get('content.hostComponents').setEach('workStatus', App.HostComponentStatus[config.hs]);
  221. }, App.get('testModeDelayForActions'));
  222. }
  223. // load data (if we need to show this background operations popup) from persist
  224. App.router.get('userSettingsController').dataLoading('show_bg').done(function (initValue) {
  225. if (initValue) {
  226. App.router.get('backgroundOperationsController').showPopup();
  227. }
  228. });
  229. } else {
  230. params.query.set('status', 'FAIL');
  231. }
  232. },
  233. startStopPopupErrorCallback: function(request, ajaxOptions, error, opt, params){
  234. params.query.set('status', 'FAIL');
  235. },
  236. /**
  237. * Confirmation popup for start/stop services
  238. * @param event
  239. * @param serviceHealth - 'STARTED' or 'INSTALLED'
  240. */
  241. startStopPopup: function(event, serviceHealth) {
  242. if ($(event.target).hasClass('disabled') || $(event.target.parentElement).hasClass('disabled')) {
  243. return;
  244. }
  245. var self = this;
  246. var serviceDisplayName = this.get('content.displayName');
  247. var isMaintenanceOFF = this.get('content.passiveState') === 'OFF';
  248. var msg = isMaintenanceOFF && serviceHealth == 'INSTALLED'? Em.I18n.t('services.service.stop.warningMsg.turnOnMM').format(serviceDisplayName) : null;
  249. msg = self.addAdditionalWarningMessage(serviceHealth, msg, serviceDisplayName);
  250. var bodyMessage = Em.Object.create({
  251. putInMaintenance: (serviceHealth == 'INSTALLED' && isMaintenanceOFF) || (serviceHealth == 'STARTED' && !isMaintenanceOFF),
  252. turnOnMmMsg: serviceHealth == 'INSTALLED' ? Em.I18n.t('passiveState.turnOnFor').format(serviceDisplayName) : Em.I18n.t('passiveState.turnOffFor').format(serviceDisplayName),
  253. confirmMsg: serviceHealth == 'INSTALLED'? Em.I18n.t('services.service.stop.confirmMsg').format(serviceDisplayName) : Em.I18n.t('services.service.start.confirmMsg').format(serviceDisplayName),
  254. confirmButton: serviceHealth == 'INSTALLED'? Em.I18n.t('services.service.stop.confirmButton') : Em.I18n.t('services.service.start.confirmButton'),
  255. additionalWarningMsg: msg
  256. });
  257. // check HDFS NameNode checkpoint before stop service
  258. if (this.get('content.serviceName') == 'HDFS' && serviceHealth == 'INSTALLED' &&
  259. this.get('content.hostComponents').filterProperty('componentName', 'NAMENODE').someProperty('workStatus', App.HostComponentStatus.started)) {
  260. this.checkNnLastCheckpointTime(function () {
  261. return App.showConfirmationFeedBackPopup(function(query, runMmOperation) {
  262. self.set('isPending', true);
  263. self.startStopWithMmode(serviceHealth, query, runMmOperation);
  264. }, bodyMessage);
  265. });
  266. } else {
  267. return App.showConfirmationFeedBackPopup(function(query, runMmOperation) {
  268. self.set('isPending', true);
  269. self.startStopWithMmode(serviceHealth, query, runMmOperation);
  270. }, bodyMessage);
  271. }
  272. },
  273. /**
  274. * this function will be called from :1) stop HDFS 2) restart all for HDFS 3) restart all affected for HDFS
  275. * @param callback - callback function to continue next operation
  276. */
  277. checkNnLastCheckpointTime: function(callback) {
  278. var self = this;
  279. this.pullNnCheckPointTime().complete(function () {
  280. var isNNCheckpointTooOld = self.get('isNNCheckpointTooOld');
  281. self.set('isNNCheckpointTooOld', null);
  282. if (isNNCheckpointTooOld) {
  283. // too old
  284. self.getHdfsUser().done(function() {
  285. var msg = Em.Object.create({
  286. confirmMsg: Em.I18n.t('services.service.stop.HDFS.warningMsg.checkPointTooOld').format(App.nnCheckpointAgeAlertThreshold) +
  287. Em.I18n.t('services.service.stop.HDFS.warningMsg.checkPointTooOld.instructions').format(isNNCheckpointTooOld, self.get('hdfsUser')),
  288. confirmButton: Em.I18n.t('common.next')
  289. });
  290. return App.showConfirmationFeedBackPopup(callback, msg);
  291. });
  292. } else if (isNNCheckpointTooOld == null) {
  293. // not available
  294. return App.showConfirmationPopup(
  295. callback, Em.I18n.t('services.service.stop.HDFS.warningMsg.checkPointNA'), null,
  296. Em.I18n.t('common.warning'), Em.I18n.t('common.proceedAnyway'), true
  297. );
  298. } else {
  299. // still young
  300. callback();
  301. }
  302. });
  303. },
  304. pullNnCheckPointTime: function () {
  305. return App.ajax.send({
  306. name: 'common.service.hdfs.getNnCheckPointTime',
  307. sender: this,
  308. success: 'parseNnCheckPointTime'
  309. });
  310. },
  311. parseNnCheckPointTime: function (data) {
  312. var nameNodesStatus = [];
  313. var lastCheckpointTime, hostName;
  314. if (data.host_components.length <= 1) {
  315. lastCheckpointTime = Em.get(data.host_components[0], 'metrics.dfs.FSNamesystem.LastCheckpointTime');
  316. hostName = Em.get(data.host_components[0], 'HostRoles.host_name');
  317. } else {
  318. // HA enabled
  319. data.host_components.forEach(function(namenode) {
  320. nameNodesStatus.pushObject( Em.Object.create({
  321. LastCheckpointTime: Em.get(namenode, 'metrics.dfs.FSNamesystem.LastCheckpointTime'),
  322. HAState: Em.get(namenode, 'metrics.dfs.FSNamesystem.HAState'),
  323. hostName: Em.get(namenode, 'HostRoles.host_name')
  324. }));
  325. });
  326. if (nameNodesStatus.someProperty('HAState', 'active')) {
  327. if (nameNodesStatus.findProperty('HAState', 'active').get('LastCheckpointTime')) {
  328. lastCheckpointTime = nameNodesStatus.findProperty('HAState', 'active').get('LastCheckpointTime');
  329. hostName = nameNodesStatus.findProperty('HAState', 'active').get('hostName');
  330. } else if (nameNodesStatus.someProperty('LastCheckpointTime')) {
  331. lastCheckpointTime = nameNodesStatus.findProperty('LastCheckpointTime').get('LastCheckpointTime');
  332. hostName = nameNodesStatus.findProperty('LastCheckpointTime').get('hostName');
  333. }
  334. } else if (nameNodesStatus.someProperty('HAState', 'standby')) {
  335. lastCheckpointTime = nameNodesStatus.findProperty('HAState', 'standby').get('LastCheckpointTime');
  336. hostName = nameNodesStatus.findProperty('HAState', 'standby').get('hostName')
  337. }
  338. }
  339. if (!lastCheckpointTime) {
  340. this.set("isNNCheckpointTooOld", null);
  341. } else {
  342. var time_criteria = App.nnCheckpointAgeAlertThreshold; // time in hours to define how many hours ago is too old
  343. var time_ago = (Math.round(App.dateTime() / 1000) - (time_criteria * 3600)) *1000;
  344. if (lastCheckpointTime <= time_ago) {
  345. // too old, set the effected hostName
  346. this.set("isNNCheckpointTooOld", hostName);
  347. } else {
  348. // still young
  349. this.set("isNNCheckpointTooOld", false);
  350. }
  351. }
  352. },
  353. /**
  354. * Return true if hdfs user data is loaded via App.MainServiceInfoConfigsController
  355. */
  356. getHdfsUser: function () {
  357. var self = this;
  358. var dfd = $.Deferred();
  359. var miscController = App.MainAdminServiceAccountsController.create();
  360. miscController.loadUsers();
  361. var interval = setInterval(function () {
  362. if (miscController.get('dataIsLoaded') && miscController.get('users')) {
  363. self.set('hdfsUser', miscController.get('users').findProperty('name', 'hdfs_user').get('value'));
  364. dfd.resolve();
  365. clearInterval(interval);
  366. }
  367. }, 10);
  368. return dfd.promise();
  369. },
  370. addAdditionalWarningMessage: function(serviceHealth, msg, serviceDisplayName){
  371. var servicesAffectedDisplayNames = [];
  372. var servicesAffected = [];
  373. if(serviceHealth == 'INSTALLED'){
  374. //To stop a service, display dependencies message...
  375. var currentService = this.get('content.serviceName');
  376. var stackServices = App.StackService.find();
  377. stackServices.forEach(function(service){
  378. if(service.get('isInstalled') || service.get('isSelected')){ //only care about services installed...
  379. var stackServiceDisplayName = service.get("displayName");
  380. var requiredServices = service.get('requiredServices'); //services required in order to have the current service be functional...
  381. if (!!requiredServices && requiredServices.length) { //only care about services with a non-empty requiredServices list.
  382. requiredServices.forEach(function(_requiredService){
  383. if (currentService === _requiredService) { //the service to be stopped is a required service by some other services...
  384. if(servicesAffected.indexOf(service) == -1 ) {
  385. servicesAffected.push(service);
  386. servicesAffectedDisplayNames.push(stackServiceDisplayName);
  387. }
  388. }
  389. },this);
  390. }
  391. }
  392. },this);
  393. var names = servicesAffectedDisplayNames.join();
  394. if(names){
  395. //only display this line with a non-empty dependency list
  396. var dependenciesMsg = Em.I18n.t('services.service.stop.warningMsg.dependent.services').format(serviceDisplayName,names);
  397. if(msg)
  398. msg = msg + " " + dependenciesMsg;
  399. else
  400. msg = dependenciesMsg;
  401. }
  402. }
  403. return msg;
  404. },
  405. startStopWithMmode: function(serviceHealth, query, runMmOperation) {
  406. var self = this;
  407. if (runMmOperation) {
  408. if (serviceHealth == "STARTED") {
  409. this.startStopPopupPrimary(serviceHealth, query).complete(function() {
  410. batchUtils.turnOnOffPassiveRequest("OFF", Em.I18n.t('passiveState.turnOff'), self.get('content.serviceName').toUpperCase());
  411. });
  412. } else {
  413. batchUtils.turnOnOffPassiveRequest("ON", Em.I18n.t('passiveState.turnOn'), this.get('content.serviceName').toUpperCase()).complete(function() {
  414. self.startStopPopupPrimary(serviceHealth, query);
  415. })
  416. }
  417. } else {
  418. this.startStopPopupPrimary(serviceHealth, query);
  419. }
  420. },
  421. startStopPopupPrimary: function (serviceHealth, query) {
  422. var requestInfo = (serviceHealth == "STARTED")
  423. ? App.BackgroundOperationsController.CommandContexts.START_SERVICE.format(this.get('content.serviceName'))
  424. : App.BackgroundOperationsController.CommandContexts.STOP_SERVICE.format(this.get('content.serviceName'));
  425. var data = {
  426. 'context': requestInfo,
  427. 'serviceName': this.get('content.serviceName').toUpperCase(),
  428. 'ServiceInfo': {
  429. 'state': serviceHealth
  430. },
  431. 'query': query
  432. };
  433. return App.ajax.send({
  434. 'name': 'common.service.update',
  435. 'sender': this,
  436. 'success': 'startStopPopupSuccessCallback',
  437. 'error': 'startStopPopupErrorCallback',
  438. 'data': data
  439. });
  440. },
  441. /**
  442. * On click callback for <code>start service</code> button
  443. * @param event
  444. */
  445. startService: function (event) {
  446. this.startStopPopup(event, App.HostComponentStatus.started);
  447. },
  448. /**
  449. * On click callback for <code>stop service</code> button
  450. * @param event
  451. */
  452. stopService: function (event) {
  453. this.startStopPopup(event, App.HostComponentStatus.stopped);
  454. },
  455. /**
  456. * On click callback for <code>run rebalancer</code> button
  457. * @param event
  458. */
  459. runRebalancer: function (event) {
  460. var self = this;
  461. return App.showConfirmationPopup(function() {
  462. self.set("content.runRebalancer", true);
  463. // load data (if we need to show this background operations popup) from persist
  464. App.router.get('userSettingsController').dataLoading('show_bg').done(function (initValue) {
  465. if (initValue) {
  466. App.router.get('backgroundOperationsController').showPopup();
  467. }
  468. });
  469. });
  470. },
  471. /**
  472. * On click handler for Yarn Refresh Queues command from items menu
  473. * @param event
  474. */
  475. refreshYarnQueues : function (event) {
  476. var controller = this;
  477. var hosts = App.Service.find('YARN').get('hostComponents').filterProperty('componentName', 'RESOURCEMANAGER').mapProperty('hostName');
  478. return App.showConfirmationPopup(function() {
  479. App.ajax.send({
  480. name : 'service.item.refreshQueueYarnRequest',
  481. sender: controller,
  482. data : {
  483. command : "REFRESHQUEUES",
  484. context : Em.I18n.t('services.service.actions.run.yarnRefreshQueues.context') ,
  485. hosts : hosts.join(','),
  486. serviceName : "YARN",
  487. componentName : "RESOURCEMANAGER",
  488. forceRefreshConfigTags : "capacity-scheduler"
  489. },
  490. success : 'refreshYarnQueuesSuccessCallback',
  491. error : 'refreshYarnQueuesErrorCallback'
  492. });
  493. });
  494. },
  495. refreshYarnQueuesSuccessCallback : function(data, ajaxOptions, params) {
  496. if (data.Requests.id) {
  497. App.router.get('backgroundOperationsController').showPopup();
  498. }
  499. },
  500. refreshYarnQueuesErrorCallback : function(data) {
  501. var error = Em.I18n.t('services.service.actions.run.yarnRefreshQueues.error');
  502. if(data && data.responseText){
  503. try {
  504. var json = $.parseJSON(data.responseText);
  505. error += json.message;
  506. } catch (err) {}
  507. }
  508. App.showAlertPopup(Em.I18n.t('services.service.actions.run.yarnRefreshQueues.error'), error);
  509. },
  510. startLdapKnox: function(event) {
  511. var context = Em.I18n.t('services.service.actions.run.startLdapKnox.context');
  512. this.startStopLdapKnox('STARTDEMOLDAP',context);
  513. },
  514. stopLdapKnox: function(event) {
  515. var context = Em.I18n.t('services.service.actions.run.stopLdapKnox.context');
  516. this.startStopLdapKnox('STOPDEMOLDAP',context);
  517. },
  518. startStopLdapKnox: function(command,context) {
  519. var controller = this;
  520. var host = App.HostComponent.find().findProperty('componentName', 'KNOX_GATEWAY').get('hostName');
  521. return App.showConfirmationPopup(function() {
  522. App.ajax.send({
  523. name: 'service.item.startStopLdapKnox',
  524. sender: controller,
  525. data: {
  526. command: command,
  527. context: context,
  528. host: host,
  529. serviceName: "KNOX",
  530. componentName: "KNOX_GATEWAY"
  531. },
  532. success: 'startStopLdapKnoxSuccessCallback',
  533. error: 'startStopLdapKnoxErrorCallback'
  534. });
  535. });
  536. },
  537. startStopLdapKnoxSuccessCallback : function(data, ajaxOptions, params) {
  538. if (data.Requests.id) {
  539. App.router.get('backgroundOperationsController').showPopup();
  540. }
  541. },
  542. startStopLdapKnoxErrorCallback : function(data) {
  543. var error = Em.I18n.t('services.service.actions.run.startStopLdapKnox.error');
  544. if(data && data.responseText){
  545. try {
  546. var json = $.parseJSON(data.responseText);
  547. error += json.message;
  548. } catch (err) {}
  549. }
  550. App.showAlertPopup(Em.I18n.t('services.service.actions.run.yarnRefreshQueues.error'), error);
  551. },
  552. restartLLAP: function(event) {
  553. var context = Em.I18n.t('services.service.actions.run.restartLLAP');
  554. this.manageLLAP('RESTART_LLAP', context);
  555. },
  556. manageLLAP: function(command, context) {
  557. var controller = this;
  558. var host = App.HostComponent.find().findProperty('componentName', 'HIVE_SERVER_INTERACTIVE').get('hostName');
  559. return App.showConfirmationPopup(function() {
  560. App.ajax.send({
  561. name: 'service.item.executeCustomCommand',
  562. sender: controller,
  563. data: {
  564. command: command,
  565. context: context,
  566. hosts: host,
  567. serviceName: "HIVE",
  568. componentName: "HIVE_SERVER_INTERACTIVE"
  569. },
  570. success: 'manageLLAPSuccessCallback',
  571. error: 'manageLLAPErrorCallback'
  572. });
  573. });
  574. },
  575. manageLLAPSuccessCallback : function(data, ajaxOptions, params) {
  576. if (data.Requests.id) {
  577. App.router.get('backgroundOperationsController').showPopup();
  578. }
  579. },
  580. manageLLAPErrorCallback : function(data) {
  581. var error = Em.I18n.t('services.service.actions.run.executeCustomCommand.error');
  582. if (data && data.responseText) {
  583. try {
  584. var json = $.parseJSON(data.responseText);
  585. error += json.message;
  586. } catch (err) {}
  587. }
  588. App.showAlertPopup(Em.I18n.t('common.error'), error);
  589. },
  590. /**
  591. * On click handler for rebalance Hdfs command from items menu
  592. */
  593. rebalanceHdfsNodes: function () {
  594. var controller = this;
  595. App.ModalPopup.show({
  596. classNames: ['fourty-percent-width-modal'],
  597. header: Em.I18n.t('services.service.actions.run.rebalanceHdfsNodes.context'),
  598. primary: Em.I18n.t('common.start'),
  599. secondary: Em.I18n.t('common.cancel'),
  600. inputValue: 10,
  601. errorMessage: Em.I18n.t('services.service.actions.run.rebalanceHdfsNodes.promptError'),
  602. isInvalid: function () {
  603. var intValue = Number(this.get('inputValue'));
  604. return this.get('inputValue')!=='DEBUG' && (isNaN(intValue) || intValue < 1 || intValue > 100);
  605. }.property('inputValue'),
  606. disablePrimary: Em.computed.alias('isInvalid'),
  607. onPrimary: function () {
  608. if (this.get('isInvalid')) {
  609. return;
  610. }
  611. App.ajax.send({
  612. name : 'service.item.rebalanceHdfsNodes',
  613. sender: controller,
  614. data : {
  615. hosts : App.Service.find('HDFS').get('hostComponents').findProperty('componentName', 'NAMENODE').get('hostName'),
  616. threshold: this.get('inputValue')
  617. },
  618. success : 'rebalanceHdfsNodesSuccessCallback',
  619. error : 'rebalanceHdfsNodesErrorCallback'
  620. });
  621. this.hide();
  622. },
  623. bodyClass: Ember.View.extend({
  624. templateName: require('templates/common/modal_popups/prompt_popup'),
  625. text: Em.I18n.t('services.service.actions.run.rebalanceHdfsNodes.prompt'),
  626. didInsertElement: function () {
  627. App.tooltip(this.$(".prompt-input"), {
  628. placement: "bottom",
  629. title: Em.I18n.t('services.service.actions.run.rebalanceHdfsNodes.promptTooltip')
  630. });
  631. }
  632. })
  633. });
  634. },
  635. rebalanceHdfsNodesSuccessCallback: function (data) {
  636. if (data.Requests.id) {
  637. App.router.get('backgroundOperationsController').showPopup();
  638. }
  639. },
  640. rebalanceHdfsNodesErrorCallback : function(data) {
  641. var error = Em.I18n.t('services.service.actions.run.rebalanceHdfsNodes.error');
  642. if(data && data.responseText){
  643. try {
  644. var json = $.parseJSON(data.responseText);
  645. error += json.message;
  646. } catch (err) {
  647. }
  648. }
  649. App.showAlertPopup(Em.I18n.t('services.service.actions.run.rebalanceHdfsNodes.error'), error);
  650. },
  651. /**
  652. * On click callback for <code>run compaction</code> button
  653. * @param event
  654. */
  655. runCompaction: function (event) {
  656. var self = this;
  657. return App.showConfirmationPopup(function() {
  658. self.set("content.runCompaction", true);
  659. // load data (if we need to show this background operations popup) from persist
  660. App.router.get('userSettingsController').dataLoading('show_bg').done(function (initValue) {
  661. if (initValue) {
  662. App.router.get('backgroundOperationsController').showPopup();
  663. }
  664. });
  665. });
  666. },
  667. /**
  668. * On click callback for <code>run smoke test</code> button
  669. * @param event
  670. */
  671. runSmokeTest: function (event) {
  672. var self = this;
  673. if (this.get('content.serviceName') === 'MAPREDUCE2' && !App.Service.find('YARN').get('isStarted')) {
  674. return App.showAlertPopup(Em.I18n.t('common.error'), Em.I18n.t('services.mapreduce2.smokeTest.requirement'));
  675. }
  676. return App.showConfirmationFeedBackPopup(function(query) {
  677. self.runSmokeTestPrimary(query);
  678. });
  679. },
  680. restartAllHostComponents : function(serviceName) {
  681. var serviceDisplayName = this.get('content.displayName');
  682. var bodyMessage = Em.Object.create({
  683. putInMaintenance: this.get('content.passiveState') === 'OFF',
  684. turnOnMmMsg: Em.I18n.t('passiveState.turnOnFor').format(serviceDisplayName),
  685. confirmMsg: Em.I18n.t('services.service.restartAll.confirmMsg').format(serviceDisplayName),
  686. confirmButton: Em.I18n.t('services.service.restartAll.confirmButton'),
  687. additionalWarningMsg: this.get('content.passiveState') === 'OFF' ? Em.I18n.t('services.service.restartAll.warningMsg.turnOnMM').format(serviceDisplayName): null
  688. });
  689. // check HDFS NameNode checkpoint before stop service
  690. if (this.get('content.serviceName') == 'HDFS' &&
  691. this.get('content.hostComponents').filterProperty('componentName', 'NAMENODE').someProperty('workStatus', App.HostComponentStatus.started)) {
  692. this.checkNnLastCheckpointTime(function () {
  693. return App.showConfirmationFeedBackPopup(function(query, runMmOperation) {
  694. batchUtils.restartAllServiceHostComponents(serviceDisplayName, serviceName, false, query, runMmOperation);
  695. }, bodyMessage);
  696. });
  697. } else {
  698. return App.showConfirmationFeedBackPopup(function(query, runMmOperation) {
  699. batchUtils.restartAllServiceHostComponents(serviceDisplayName, serviceName, false, query, runMmOperation);
  700. }, bodyMessage);
  701. }
  702. },
  703. turnOnOffPassive: function(label) {
  704. var self = this;
  705. var state = this.get('content.passiveState') == 'OFF' ? 'ON' : 'OFF';
  706. var onOff = state === 'ON' ? "On" : "Off";
  707. return App.showConfirmationPopup(function() {
  708. batchUtils.turnOnOffPassiveRequest(state, label, self.get('content.serviceName').toUpperCase(), function(data, opt, params) {
  709. self.set('content.passiveState', params.passive_state);
  710. batchUtils.infoPassiveState(params.passive_state);})
  711. },
  712. Em.I18n.t('hosts.passiveMode.popup').format(onOff,self.get('content.displayName'))
  713. );
  714. },
  715. rollingRestart: function(hostComponentName) {
  716. batchUtils.launchHostComponentRollingRestart(hostComponentName, this.get('content.displayName'), this.get('content.passiveState') === "ON", false, this.get('content.passiveState') === "ON");
  717. },
  718. runSmokeTestPrimary: function(query) {
  719. var clusterLevelRequired = ['KERBEROS'];
  720. var requestData = {
  721. 'serviceName': this.get('content.serviceName'),
  722. 'displayName': this.get('content.displayName'),
  723. 'actionName': this.get('content.serviceName') === 'ZOOKEEPER' ? 'ZOOKEEPER_QUORUM_SERVICE_CHECK' : this.get('content.serviceName') + '_SERVICE_CHECK',
  724. 'query': query
  725. };
  726. if (clusterLevelRequired.contains(this.get('content.serviceName'))) {
  727. requestData.operationLevel = {
  728. "level": "CLUSTER",
  729. "cluster_name": App.get('clusterName')
  730. };
  731. }
  732. App.ajax.send({
  733. 'name': 'service.item.smoke',
  734. 'sender': this,
  735. 'success':'runSmokeTestSuccessCallBack',
  736. 'error':'runSmokeTestErrorCallBack',
  737. 'data': requestData
  738. });
  739. },
  740. runSmokeTestSuccessCallBack: function (data, ajaxOptions, params) {
  741. if (data.Requests.id) {
  742. // load data (if we need to show this background operations popup) from persist
  743. App.router.get('userSettingsController').dataLoading('show_bg').done(function (initValue) {
  744. params.query.set('status', 'SUCCESS');
  745. if (initValue) {
  746. App.router.get('backgroundOperationsController').showPopup();
  747. }
  748. });
  749. }
  750. else {
  751. params.query.set('status', 'FAIL');
  752. }
  753. },
  754. runSmokeTestErrorCallBack: function (request, ajaxOptions, error, opt, params) {
  755. params.query.set('status', 'FAIL');
  756. },
  757. /**
  758. * On click callback for <code>Reassign <master component></code> button
  759. * @param hostComponent
  760. */
  761. reassignMaster: function (hostComponent) {
  762. var component = App.HostComponent.find().findProperty('componentName', hostComponent);
  763. if (component) {
  764. var reassignMasterController = App.router.get('reassignMasterController');
  765. reassignMasterController.saveComponentToReassign(component);
  766. reassignMasterController.setCurrentStep('1');
  767. App.router.transitionTo('reassign');
  768. }
  769. },
  770. /**
  771. * On click callback for <code>action</code> dropdown menu
  772. * Calls runSmokeTest, runRebalancer, runCompaction or reassignMaster depending on context
  773. * @param event
  774. */
  775. doAction: function (event) {
  776. if ($(event.target).hasClass('disabled') || $(event.target.parentElement).hasClass('disabled')) {
  777. return;
  778. }
  779. var methodName = event.context.action;
  780. var context = event.context.context;
  781. if (methodName) {
  782. this[methodName](context);
  783. }
  784. },
  785. /**
  786. * Restart clients host components to apply config changes
  787. */
  788. refreshConfigs: function () {
  789. var self = this;
  790. if (this.get('isClientsOnlyService') || this.get('content.serviceName') == "FLUME") {
  791. return App.showConfirmationFeedBackPopup(function (query) {
  792. batchUtils.getComponentsFromServer({
  793. services: [self.get('content.serviceName')]
  794. }, function (data) {
  795. var hostComponents = [];
  796. data.items.forEach(function (host) {
  797. host.host_components.forEach(function (hostComponent) {
  798. hostComponents.push(Em.Object.create({
  799. componentName: hostComponent.HostRoles.component_name,
  800. hostName: host.Hosts.host_name
  801. }))
  802. });
  803. });
  804. batchUtils.restartHostComponents(hostComponents, Em.I18n.t('rollingrestart.context.allForSelectedService').format(self.get('content.serviceName')), "SERVICE", query);
  805. })
  806. });
  807. }
  808. },
  809. /**
  810. * Send command to server to install client on selected host
  811. * @param componentName
  812. */
  813. addComponent: function (componentName) {
  814. var self = this;
  815. var component = App.StackServiceComponent.find().findProperty('componentName', componentName);
  816. var componentDisplayName = component.get('displayName');
  817. App.get('router.mainAdminKerberosController').getKDCSessionState(function () {
  818. return App.ModalPopup.show({
  819. primary: Em.computed.ifThenElse('anyHostsWithoutComponent', Em.I18n.t('hosts.host.addComponent.popup.confirm'), undefined),
  820. header: Em.I18n.t('popup.confirmation.commonHeader'),
  821. addComponentMsg: Em.I18n.t('hosts.host.addComponent.msg').format(componentDisplayName),
  822. selectHostMsg: Em.computed.i18nFormat('services.summary.selectHostForComponent', 'componentDisplayName'),
  823. thereIsNoHostsMsg: Em.computed.i18nFormat('services.summary.allHostsAlreadyRunComponent', 'componentDisplayName'),
  824. hostsWithoutComponent: function () {
  825. var hostsWithComponent = App.HostComponent.find().filterProperty('componentName', componentName).mapProperty('hostName');
  826. var result = App.get('allHostNames');
  827. hostsWithComponent.forEach(function (host) {
  828. result = result.without(host);
  829. });
  830. return result;
  831. }.property(),
  832. anyHostsWithoutComponent: Em.computed.gt('hostsWithoutComponent.length', 0),
  833. selectedHost: null,
  834. componentName: componentName,
  835. componentDisplayName: componentDisplayName,
  836. bodyClass: Em.View.extend({
  837. templateName: require('templates/main/service/add_host_popup')
  838. }),
  839. onPrimary: function () {
  840. var selectedHost = this.get('selectedHost');
  841. // Install
  842. if (['HIVE_METASTORE', 'RANGER_KMS_SERVER', 'NIMBUS'].contains(component.get('componentName')) && !!selectedHost) {
  843. App.router.get('mainHostDetailsController').addComponentWithCheck(
  844. {
  845. context: component,
  846. selectedHost: selectedHost
  847. }
  848. );
  849. } else {
  850. self.installHostComponentCall(selectedHost, component);
  851. }
  852. this.hide();
  853. }
  854. });
  855. });
  856. },
  857. /**
  858. * set property isPending (if this property is true - means that service has task in BGO)
  859. * and this makes start/stop button disabled
  860. */
  861. setStartStopState: function () {
  862. var serviceName = this.get('content.serviceName');
  863. var backgroundOperations = App.router.get('backgroundOperationsController.services');
  864. if (backgroundOperations && backgroundOperations.length > 0) {
  865. for (var i = 0; i < backgroundOperations.length; i++) {
  866. if (backgroundOperations[i].isRunning &&
  867. (backgroundOperations[i].dependentService === "ALL_SERVICES" ||
  868. backgroundOperations[i].dependentService === serviceName)) {
  869. this.set('isPending', true);
  870. return;
  871. }
  872. }
  873. this.set('isPending', false);
  874. } else {
  875. this.set('isPending', true);
  876. }
  877. }.observes('App.router.backgroundOperationsController.serviceTimestamp'),
  878. isStartDisabled: function () {
  879. if(this.get('isPending')) return true;
  880. return !(this.get('content.healthStatus') == 'red');
  881. }.property('content.healthStatus','isPending'),
  882. isStopDisabled: function () {
  883. if(this.get('isPending')) return true;
  884. if (App.get('isHaEnabled') && this.get('content.serviceName') == 'HDFS' && this.get('content.hostComponents').filterProperty('componentName', 'NAMENODE').someProperty('workStatus', App.HostComponentStatus.started)) {
  885. return false;
  886. }
  887. return (this.get('content.healthStatus') != 'green');
  888. }.property('content.healthStatus','isPending', 'App.isHaEnabled'),
  889. /**
  890. * Determine if service has than one service client components
  891. */
  892. isSeveralClients: function () {
  893. return App.StackServiceComponent.find().filterProperty('serviceName', this.get('content.serviceName')).filterProperty('isClient').length > 1;
  894. }.property('content.serviceName'),
  895. enableHighAvailability: function() {
  896. var highAvailabilityController = App.router.get('mainAdminHighAvailabilityController');
  897. highAvailabilityController.enableHighAvailability();
  898. },
  899. disableHighAvailability: function() {
  900. var highAvailabilityController = App.router.get('mainAdminHighAvailabilityController');
  901. highAvailabilityController.disableHighAvailability();
  902. },
  903. enableRMHighAvailability: function() {
  904. var highAvailabilityController = App.router.get('mainAdminHighAvailabilityController');
  905. highAvailabilityController.enableRMHighAvailability();
  906. },
  907. addHawqStandby: function() {
  908. var highAvailabilityController = App.router.get('mainAdminHighAvailabilityController');
  909. highAvailabilityController.addHawqStandby();
  910. },
  911. removeHawqStandby: function() {
  912. var highAvailabilityController = App.router.get('mainAdminHighAvailabilityController');
  913. highAvailabilityController.removeHawqStandby();
  914. },
  915. activateHawqStandby: function() {
  916. var highAvailabilityController = App.router.get('mainAdminHighAvailabilityController');
  917. highAvailabilityController.activateHawqStandby();
  918. },
  919. enableRAHighAvailability: function() {
  920. var highAvailabilityController = App.router.get('mainAdminHighAvailabilityController');
  921. highAvailabilityController.enableRAHighAvailability();
  922. },
  923. downloadClientConfigs: function (event) {
  924. var component = this.get('content.clientComponents').rejectProperty('totalCount', 0)[0];
  925. this.downloadClientConfigsCall({
  926. serviceName: this.get('content.serviceName'),
  927. componentName: (event && event.name) || component.get('componentName'),
  928. displayName: (event && event.label) || component.get('displayName')
  929. });
  930. },
  931. /**
  932. * On click handler for custom hawq command from items menu
  933. * @param context
  934. */
  935. executeHawqCustomCommand: function(context) {
  936. var controller = this;
  937. return App.showConfirmationPopup(function() {
  938. App.ajax.send({
  939. name : 'service.item.executeCustomCommand',
  940. sender: controller,
  941. data : {
  942. command : context.command,
  943. context : context.label,
  944. hosts : App.Service.find(context.service).get('hostComponents').findProperty('componentName', context.component).get('hostName'),
  945. serviceName : context.service,
  946. componentName : context.component
  947. },
  948. success : 'executeCustomCommandSuccessCallback',
  949. error : 'executeCustomCommandErrorCallback'
  950. });
  951. });
  952. },
  953. /**
  954. * On click handler for custom command from items menu
  955. * @param context
  956. */
  957. executeCustomCommand: function(context) {
  958. var controller = this;
  959. return App.showConfirmationPopup(function() {
  960. App.ajax.send({
  961. name : 'service.item.executeCustomCommand',
  962. sender: controller,
  963. data : {
  964. command : context.command,
  965. context : 'Execute ' + context.command,
  966. hosts : App.Service.find(context.service).get('hostComponents').findProperty('componentName', context.component).get('hostName'),
  967. serviceName : context.service,
  968. componentName : context.component,
  969. forceRefreshConfigTags : "capacity-scheduler"
  970. },
  971. success : 'executeCustomCommandSuccessCallback',
  972. error : 'executeCustomCommandErrorCallback'
  973. });
  974. });
  975. },
  976. executeCustomCommandSuccessCallback : function(data, ajaxOptions, params) {
  977. if (data.Requests.id) {
  978. App.router.get('backgroundOperationsController').showPopup();
  979. }
  980. },
  981. executeCustomCommandErrorCallback : function(data) {
  982. var error = Em.I18n.t('services.service.actions.run.executeCustomCommand.error');
  983. if(data && data.responseText){
  984. try {
  985. var json = $.parseJSON(data.responseText);
  986. error += json.message;
  987. } catch (err) {}
  988. }
  989. App.showAlertPopup(Em.I18n.t('services.service.actions.run.executeCustomCommand.error'), error);
  990. },
  991. /**
  992. * find dependent services
  993. * @param {string[]} serviceNamesToDelete
  994. * @returns {Array}
  995. */
  996. findDependentServices: function (serviceNamesToDelete) {
  997. var dependentServices = [];
  998. App.Service.find().forEach(function (service) {
  999. if (!serviceNamesToDelete.contains(service.get('serviceName'))) {
  1000. var requiredServices = App.StackService.find(service.get('serviceName')).get('requiredServices');
  1001. serviceNamesToDelete.forEach(function (dependsOnService) {
  1002. if (requiredServices.contains(dependsOnService)) {
  1003. dependentServices.push(service.get('serviceName'));
  1004. }
  1005. });
  1006. }
  1007. }, this);
  1008. return dependentServices;
  1009. },
  1010. /**
  1011. * @param serviceNames
  1012. * @returns {string}
  1013. */
  1014. servicesDisplayNames: function(serviceNames) {
  1015. return serviceNames.map(function(serviceName) {
  1016. return App.format.role(serviceName, true);
  1017. }).join(',');
  1018. },
  1019. /**
  1020. * Is services can be removed based on work status
  1021. * @param serviceNames
  1022. */
  1023. allowUninstallServices: function(serviceNames) {
  1024. return App.Service.find().filter(function (service) {
  1025. return serviceNames.contains(service.get('serviceName'));
  1026. }).everyProperty('allowToDelete');
  1027. },
  1028. /**
  1029. * delete service action
  1030. * @param {string} serviceName
  1031. */
  1032. deleteService: function(serviceName) {
  1033. var self = this,
  1034. interDependentServices = this.get('interDependentServices'),
  1035. serviceNamesToDelete = this.get('serviceNamesToDelete'),
  1036. dependentServices = this.findDependentServices(serviceNamesToDelete),
  1037. displayName = App.format.role(serviceName, true),
  1038. popupHeader = Em.I18n.t('services.service.delete.popup.header'),
  1039. dependentServicesToDeleteFmt = this.servicesDisplayNames(interDependentServices);
  1040. if (App.Service.find().get('length') === 1) {
  1041. //at least one service should be installed
  1042. App.ModalPopup.show({
  1043. secondary: null,
  1044. header: popupHeader,
  1045. encodeBody: false,
  1046. body: Em.I18n.t('services.service.delete.lastService.popup.body').format(displayName)
  1047. });
  1048. } else if (dependentServices.length > 0) {
  1049. this.dependentServicesWarning(serviceName, dependentServices);
  1050. } else if (this.allowUninstallServices(serviceNamesToDelete)) {
  1051. App.showConfirmationPopup(
  1052. function() {self.confirmDeleteService(serviceName, interDependentServices, dependentServicesToDeleteFmt)},
  1053. Em.I18n.t('services.service.delete.popup.warning').format(displayName) +
  1054. (interDependentServices.length ? Em.I18n.t('services.service.delete.popup.warning.dependent').format(dependentServicesToDeleteFmt) : ''),
  1055. null,
  1056. popupHeader,
  1057. Em.I18n.t('common.delete'),
  1058. true
  1059. );
  1060. } else {
  1061. var body = Em.I18n.t('services.service.delete.popup.mustBeStopped').format(displayName);
  1062. if (interDependentServices.length) {
  1063. body += Em.I18n.t('services.service.delete.popup.mustBeStopped.dependent').format(dependentServicesToDeleteFmt)
  1064. }
  1065. App.ModalPopup.show({
  1066. secondary: null,
  1067. header: popupHeader,
  1068. encodeBody: false,
  1069. body: body
  1070. });
  1071. }
  1072. },
  1073. /**
  1074. * warning that show dependent services which must be deleted prior to chosen service deletion
  1075. * @param {string} origin
  1076. * @param {Array.<string>} dependent
  1077. * @returns {App.ModalPopup}
  1078. */
  1079. dependentServicesWarning: function(origin, dependent) {
  1080. var body = Em.I18n.t('services.service.delete.popup.dependentServices').format(App.format.role(origin, true));
  1081. body += '<ul>';
  1082. dependent.forEach(function(serviceName) {
  1083. body += '<li>' + App.format.role(serviceName, true) + '</li>';
  1084. });
  1085. body += '</ul>';
  1086. return App.ModalPopup.show({
  1087. secondary: null,
  1088. header: Em.I18n.t('services.service.delete.popup.header'),
  1089. bodyClass: Em.View.extend({
  1090. template: Em.Handlebars.compile(body)
  1091. })
  1092. });
  1093. },
  1094. /**
  1095. * Confirmation popup of service deletion
  1096. * @param {string} serviceName
  1097. * @param {string[]} [dependentServiceNames]
  1098. * @param {string} [servicesToDeleteFmt]
  1099. */
  1100. confirmDeleteService: function (serviceName, dependentServiceNames, servicesToDeleteFmt) {
  1101. var confirmKey = 'delete',
  1102. self = this,
  1103. message = Em.I18n.t('services.service.confirmDelete.popup.body').format(App.format.role(serviceName, true), confirmKey);
  1104. if (dependentServiceNames.length > 0) {
  1105. message = Em.I18n.t('services.service.confirmDelete.popup.body.dependent')
  1106. .format(App.format.role(serviceName, true), servicesToDeleteFmt, confirmKey);
  1107. }
  1108. App.ModalPopup.show({
  1109. /**
  1110. * @function onPrimary
  1111. */
  1112. onPrimary: function() {
  1113. self.deleteServiceCall([serviceName].concat(dependentServiceNames));
  1114. this._super();
  1115. },
  1116. /**
  1117. * @type {string}
  1118. */
  1119. primary: Em.I18n.t('common.delete'),
  1120. /**
  1121. * @type {string}
  1122. */
  1123. primaryClass: 'btn-danger',
  1124. /**
  1125. * @type {string}
  1126. */
  1127. header: Em.I18n.t('services.service.confirmDelete.popup.header'),
  1128. /**
  1129. * @type {string}
  1130. */
  1131. confirmInput: '',
  1132. /**
  1133. * @type {boolean}
  1134. */
  1135. disablePrimary: Em.computed.notEqual('confirmInput', confirmKey),
  1136. /**
  1137. * @type {Em.View}
  1138. */
  1139. bodyClass: Em.View.extend({
  1140. confirmKey: confirmKey,
  1141. typeMessage: Em.I18n.t('services.service.confirmDelete.popup.body.type').format(confirmKey),
  1142. template: Em.Handlebars.compile(message +
  1143. '<div class="form-inline align-center"></br>' +
  1144. '<label><b>{{view.typeMessage}}</b></label>&nbsp;' +
  1145. '{{view Ember.TextField valueBinding="view.parentView.confirmInput" class="input-small"}}</br>' +
  1146. '</div>')
  1147. }),
  1148. enterKeyPressed: function(e) {
  1149. if (this.get('disablePrimary')) return;
  1150. this.onPrimary();
  1151. }
  1152. });
  1153. },
  1154. /**
  1155. * All host names
  1156. * This property required for request for recommendations
  1157. *
  1158. * @type {String[]}
  1159. * @override
  1160. */
  1161. hostNames: Em.computed.alias('App.allHostNames'),
  1162. /**
  1163. * Recommendation object
  1164. * This property required for request for recommendations
  1165. *
  1166. * @type {Object}
  1167. * @override
  1168. */
  1169. hostGroups: function() {
  1170. var hostGroup = blueprintUtils.generateHostGroups(App.get('allHostNames'));
  1171. return blueprintUtils.removeDeletedComponents(hostGroup, [this.get('serviceNamesToDelete')]);
  1172. }.property('serviceNamesToDelete', 'App.allHostNames'),
  1173. /**
  1174. * List of services without removed
  1175. * This property required for request for recommendations
  1176. *
  1177. * @type {String[]}
  1178. * @override
  1179. */
  1180. serviceNames: function() {
  1181. return App.Service.find().filter(function(s) {
  1182. return !this.get('serviceNamesToDelete').contains(s.get('serviceName'));
  1183. }, this).mapProperty('serviceName');
  1184. }.property('serviceNamesToDelete'),
  1185. /**
  1186. * This property required for request for recommendations
  1187. *
  1188. * @return {Boolean}
  1189. * @override
  1190. */
  1191. isConfigHasInitialState: function() { return false; },
  1192. /**
  1193. * Describes condition when recommendation should be applied
  1194. * For removing services it's always true
  1195. * This property required for request for recommendations
  1196. *
  1197. * @return {Boolean}
  1198. * @override
  1199. */
  1200. allowUpdateProperty: function() { return true; },
  1201. /**
  1202. * Just config version note
  1203. *
  1204. * @type {String}
  1205. */
  1206. serviceConfigVersionNote: function() {
  1207. var services = this.get('serviceNamesToDelete').join(',');
  1208. if (this.get('serviceNamesToDelete.length') === 1) {
  1209. return Em.I18n.t('services.service.delete.configVersionNote').format(services);
  1210. }
  1211. return Em.I18n.t('services.service.delete.configVersionNote.plural').format(services);
  1212. }.property('serviceNamesToDelete'),
  1213. /**
  1214. * Method ot save configs after service have been removed
  1215. * @override
  1216. */
  1217. saveConfigs: function() {
  1218. var data = [];
  1219. this.get('stepConfigs').forEach(function(stepConfig) {
  1220. var serviceConfig = this.getServiceConfigToSave(stepConfig.get('serviceName'), stepConfig.get('configs'));
  1221. if (serviceConfig) {
  1222. data.push(serviceConfig);
  1223. }
  1224. }, this);
  1225. if (Em.isArray(data) && data.length) {
  1226. this.putChangedConfigurations(data, 'onSaveConfigs');
  1227. } else {
  1228. this.confirmServiceDeletion();
  1229. }
  1230. },
  1231. confirmServiceDeletion: function() {
  1232. var msg = this.get('interDependentServices.length')
  1233. ? Em.I18n.t('services.service.delete.service.success.confirmation.plural').format(this.get('serviceNamesToDelete').join(','))
  1234. : Em.I18n.t('services.service.delete.service.success.confirmation').format(this.get('content.serviceName'));
  1235. return App.showAlertPopup(Em.I18n.t('popup.confirmation.commonHeader'), msg, function() {
  1236. window.location.reload();
  1237. })
  1238. },
  1239. /**
  1240. * Ajax call to delete service
  1241. * @param {string[]} serviceNames
  1242. * @returns {$.ajax}
  1243. */
  1244. deleteServiceCall: function(serviceNames) {
  1245. var serviceToDeleteNow = serviceNames[0];
  1246. if (serviceNames.length > 1) {
  1247. var servicesToDeleteNext = serviceNames.slice(1);
  1248. }
  1249. return App.ajax.send({
  1250. name : 'common.delete.service',
  1251. sender: this,
  1252. data : {
  1253. serviceName : serviceToDeleteNow,
  1254. servicesToDeleteNext: servicesToDeleteNext
  1255. },
  1256. success : 'deleteServiceCallSuccessCallback',
  1257. error: 'deleteServiceCallErrorCallback'
  1258. });
  1259. },
  1260. deleteServiceCallSuccessCallback: function(data, ajaxOptions, params) {
  1261. if (params.servicesToDeleteNext) {
  1262. this.deleteServiceCall(params.servicesToDeleteNext);
  1263. } else {
  1264. this.loadConfigRecommendations(null, this.saveConfigs.bind(this));
  1265. }
  1266. },
  1267. deleteServiceCallErrorCallback: function (jqXHR, ajaxOptions, error, opt) {
  1268. App.ajax.defaultErrorHandler(jqXHR, opt.url, opt.method, jqXHR.status);
  1269. }
  1270. });