batch_scheduled_requests.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. /**
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with this
  4. * work for additional information regarding copyright ownership. The ASF
  5. * licenses this file to you under the Apache License, Version 2.0 (the
  6. * "License"); you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  13. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  14. * License for the specific language governing permissions and limitations under
  15. * the License.
  16. */
  17. var App = require('app');
  18. /**
  19. * Default success callback for ajax-requests in this module
  20. * @type {Function}
  21. */
  22. var defaultSuccessCallback = function() {
  23. App.router.get('applicationController').dataLoading().done(function(initValue) {
  24. if (initValue) {
  25. App.router.get('backgroundOperationsController').showPopup();
  26. }
  27. });
  28. };
  29. /**
  30. * Default error callback for ajax-requests in this module
  31. * @param {Object} xhr
  32. * @param {String} textStatus
  33. * @param {String} error
  34. * @param {Object} opt
  35. * @type {Function}
  36. */
  37. var defaultErrorCallback = function(xhr, textStatus, error, opt) {
  38. App.ajax.defaultErrorHandler(xhr, opt.url, 'POST', xhr.status);
  39. };
  40. /**
  41. * Contains helpful utilities for handling batch and scheduled requests.
  42. */
  43. module.exports = {
  44. /**
  45. * Some services have components which have a need for rolling restarts. This
  46. * method returns the name of the host-component which supports rolling
  47. * restarts for a service.
  48. * @param {String} serviceName
  49. */
  50. getRollingRestartComponentName: function(serviceName) {
  51. var rollingRestartComponents = {
  52. HDFS: 'DATANODE',
  53. YARN: 'NODEMANAGER',
  54. MAPREDUCE: 'TASKTRACKER',
  55. HBASE: 'HBASE_REGIONSERVER',
  56. STORM: 'SUPERVISOR'
  57. };
  58. return rollingRestartComponents[serviceName] ? rollingRestartComponents[serviceName] : null;
  59. },
  60. /**
  61. * Facade-function for restarting host components of specific service
  62. * @param {String} serviceName for which service hostComponents should be restarted
  63. * @param {bool} staleConfigsOnly restart only hostComponents with <code>staleConfig</code> true
  64. */
  65. restartAllServiceHostComponents: function(serviceName, staleConfigsOnly) {
  66. var service = App.Service.find(serviceName);
  67. var context = staleConfigsOnly ? Em.I18n.t('rollingrestart.context.allWithStaleConfigsForSelectedService').format(serviceName) : Em.I18n.t('rollingrestart.context.allForSelectedService').format(serviceName);
  68. if (service) {
  69. var hostComponents = service.get('hostComponents');
  70. if (staleConfigsOnly) {
  71. hostComponents = hostComponents.filterProperty('staleConfigs', true);
  72. }
  73. this.restartHostComponents(hostComponents, context);
  74. }
  75. },
  76. /**
  77. * Restart list of host components
  78. * @param {Ember.Enumerable} hostComponentsList list of host components should be restarted
  79. * @param {String} context message to show in BG popup
  80. */
  81. restartHostComponents: function(hostComponentsList, context) {
  82. context = context || Em.I18n.t('rollingrestart.context.default');
  83. /**
  84. * Format: {
  85. * 'DATANODE': ['host1', 'host2'],
  86. * 'NAMENODE': ['host1', 'host3']
  87. * ...
  88. * }
  89. */
  90. var componentToHostsMap = {};
  91. var componentServiceMap = App.QuickDataMapper.componentServiceMap();
  92. hostComponentsList.forEach(function(hc) {
  93. var componentName = hc.get('componentName');
  94. if (!componentToHostsMap[componentName]) {
  95. componentToHostsMap[componentName] = [];
  96. }
  97. componentToHostsMap[componentName].push(hc.get('host.hostName'));
  98. });
  99. var resource_filters = [];
  100. for (var componentName in componentToHostsMap) {
  101. if (componentToHostsMap.hasOwnProperty(componentName)) {
  102. resource_filters.push({
  103. service_name: componentServiceMap[componentName],
  104. component_name: componentName,
  105. hosts: componentToHostsMap[componentName].join(",")
  106. });
  107. }
  108. }
  109. if (resource_filters.length) {
  110. App.ajax.send({
  111. name: 'restart.hostComponents',
  112. sender: {
  113. successCallback: defaultSuccessCallback,
  114. errorCallback: defaultErrorCallback
  115. },
  116. data: {
  117. context: context,
  118. resource_filters: resource_filters
  119. },
  120. success: 'successCallback',
  121. error: 'errorCallback'
  122. });
  123. }
  124. },
  125. /**
  126. * Makes a REST call to the server requesting the rolling restart of the
  127. * provided host components.
  128. * @param {Array} restartHostComponents list of host components should be restarted
  129. * @param {Number} batchSize size of each batch
  130. * @param {Number} intervalTimeSeconds delay between two batches
  131. * @param {Number} tolerateSize task failure tolerance
  132. * @param {callback} successCallback
  133. * @param {callback} errorCallback
  134. */
  135. _doPostBatchRollingRestartRequest: function(restartHostComponents, batchSize, intervalTimeSeconds, tolerateSize, successCallback, errorCallback) {
  136. successCallback = successCallback || defaultSuccessCallback;
  137. errorCallback = errorCallback || defaultErrorCallback;
  138. if (!restartHostComponents.length) {
  139. console.log('No batch rolling restart if no restartHostComponents provided!');
  140. return;
  141. }
  142. App.ajax.send({
  143. name: 'rolling_restart.post',
  144. sender: {
  145. successCallback: successCallback,
  146. errorCallback: errorCallback
  147. },
  148. data: {
  149. intervalTimeSeconds: intervalTimeSeconds,
  150. tolerateSize: tolerateSize,
  151. batches: this.getBatchesForRollingRestartRequest(restartHostComponents, batchSize)
  152. },
  153. success: 'successCallback',
  154. error: 'errorCallback'
  155. });
  156. },
  157. /**
  158. * Create list of batches for rolling restart request
  159. * @param {Array} restartHostComponents list host components should be restarted
  160. * @param {Number} batchSize size of each batch
  161. * @returns {Array} list of batches
  162. */
  163. getBatchesForRollingRestartRequest: function(restartHostComponents, batchSize) {
  164. var hostIndex = 0,
  165. batches = [],
  166. batchCount = Math.ceil(restartHostComponents.length / batchSize),
  167. sampleHostComponent = restartHostComponents.objectAt(0),
  168. componentName = sampleHostComponent.get('componentName'),
  169. serviceName = sampleHostComponent.get('service.serviceName');
  170. for ( var count = 0; count < batchCount; count++) {
  171. var hostNames = [];
  172. for ( var hc = 0; hc < batchSize && hostIndex < restartHostComponents.length; hc++) {
  173. hostNames.push(restartHostComponents.objectAt(hostIndex++).get('host.hostName'));
  174. }
  175. if (hostNames.length > 0) {
  176. batches.push({
  177. "order_id" : count + 1,
  178. "type" : "POST",
  179. "uri" : App.apiPrefix + "/clusters/" + App.get('clusterName') + "/requests",
  180. "RequestBodyInfo" : {
  181. "RequestInfo" : {
  182. "context" : "_PARSE_.ROLLING-RESTART." + componentName + "." + (count + 1) + "." + batchCount,
  183. "command" : "RESTART"
  184. },
  185. "Requests/resource_filters": [{
  186. "service_name" : serviceName,
  187. "component_name" : componentName,
  188. "hosts" : hostNames.join(",")
  189. }]
  190. }
  191. });
  192. }
  193. }
  194. return batches;
  195. },
  196. /**
  197. * Launches dialog to handle rolling restarts of host components.
  198. *
  199. * Rolling restart is supported only for components listed in <code>getRollingRestartComponentName</code>
  200. * @see getRollingRestartComponentName
  201. * @param {String} hostComponentName
  202. * Type of host-component to restart across cluster
  203. * (ex: DATANODE)
  204. * @param {bool} staleConfigsOnly
  205. * Pre-select host-components which have stale
  206. * configurations
  207. */
  208. launchHostComponentRollingRestart: function(hostComponentName, staleConfigsOnly, skipMaintenance) {
  209. if (App.get('components.rollinRestartAllowed').contains(hostComponentName)) {
  210. this.showRollingRestartPopup(hostComponentName, staleConfigsOnly, null, skipMaintenance);
  211. }
  212. else {
  213. this.showWarningRollingRestartPopup(hostComponentName);
  214. }
  215. },
  216. /**
  217. * Show popup with rolling restart dialog
  218. * @param {String} hostComponentName name of the host components that should be restarted
  219. * @param {bool} staleConfigsOnly restart only components with <code>staleConfigs</code> = true
  220. * @param {App.hostComponent[]} hostComponents list of hostComponents that should be restarted (optional).
  221. * Using this parameter will reset hostComponentName
  222. */
  223. showRollingRestartPopup: function(hostComponentName, staleConfigsOnly, hostComponents, skipMaintenance) {
  224. hostComponents = hostComponents || [];
  225. var componentDisplayName = App.format.role(hostComponentName);
  226. if (!componentDisplayName) {
  227. componentDisplayName = hostComponentName;
  228. }
  229. var title = Em.I18n.t('rollingrestart.dialog.title').format(componentDisplayName);
  230. var viewExtend = {
  231. staleConfigsOnly : staleConfigsOnly,
  232. hostComponentName : hostComponentName,
  233. skipMaintenance: skipMaintenance,
  234. didInsertElement : function() {
  235. this.set('parentView.innerView', this);
  236. this.initialize();
  237. }
  238. };
  239. if (hostComponents.length) {
  240. viewExtend.allHostComponents = hostComponents;
  241. }
  242. var self = this;
  243. App.ModalPopup.show({
  244. header : title,
  245. hostComponentName : hostComponentName,
  246. staleConfigsOnly : staleConfigsOnly,
  247. skipMaintenance: skipMaintenance,
  248. innerView : null,
  249. bodyClass : App.RollingRestartView.extend(viewExtend),
  250. classNames : [ 'rolling-restart-popup' ],
  251. primary : Em.I18n.t('rollingrestart.dialog.primary'),
  252. onPrimary : function() {
  253. var dialog = this;
  254. if (!dialog.get('enablePrimary')) {
  255. return;
  256. }
  257. var restartComponents = this.get('innerView.restartHostComponents');
  258. var batchSize = this.get('innerView.batchSize');
  259. var waitTime = this.get('innerView.interBatchWaitTimeSeconds');
  260. var tolerateSize = this.get('innerView.tolerateSize');
  261. self._doPostBatchRollingRestartRequest(restartComponents, batchSize, waitTime, tolerateSize, function() {
  262. dialog.hide();
  263. defaultSuccessCallback();
  264. });
  265. },
  266. updateButtons : function() {
  267. var errors = this.get('innerView.errors');
  268. this.set('enablePrimary', !(errors != null && errors.length > 0))
  269. }.observes('innerView.errors')
  270. });
  271. },
  272. /**
  273. * Show warning popup about not supported host components
  274. * @param {String} hostComponentName
  275. */
  276. showWarningRollingRestartPopup: function(hostComponentName) {
  277. var componentDisplayName = App.format.role(hostComponentName);
  278. if (!componentDisplayName) {
  279. componentDisplayName = hostComponentName;
  280. }
  281. var title = Em.I18n.t('rollingrestart.dialog.title').format(componentDisplayName);
  282. var msg = Em.I18n.t('rollingrestart.notsupported.hostComponent').format(componentDisplayName);
  283. console.log(msg);
  284. App.ModalPopup.show({
  285. header : title,
  286. secondary : false,
  287. msg : msg,
  288. bodyClass : Em.View.extend({
  289. template : Em.Handlebars.compile('<div class="alert alert-warning">{{msg}}</div>')
  290. })
  291. });
  292. },
  293. /**
  294. * Retrieves the latest information about a specific request schedule
  295. * identified by 'requestScheduleId'
  296. *
  297. * @param {Number} requestScheduleId ID of the request schedule to get
  298. * @param {Function} successCallback Called with request_schedule data from server. An
  299. * empty object returned for invalid ID.
  300. * @param {Function} errorCallback Optional error callback. Default behavior is to
  301. * popup default error dialog.
  302. */
  303. getRequestSchedule: function(requestScheduleId, successCallback, errorCallback) {
  304. if (requestScheduleId != null && !isNaN(requestScheduleId) && requestScheduleId > -1) {
  305. errorCallback = errorCallback ? errorCallback : defaultErrorCallback;
  306. App.ajax.send({
  307. name : 'request_schedule.get',
  308. sender : {
  309. successCallbackFunction : function(data) {
  310. successCallback(data);
  311. },
  312. errorCallbackFunction : function(xhr, textStatus, error, opt) {
  313. errorCallback(xhr, textStatus, error, opt);
  314. }
  315. },
  316. data : {
  317. request_schedule_id : requestScheduleId
  318. },
  319. success : 'successCallbackFunction',
  320. error : 'errorCallbackFunction'
  321. });
  322. } else {
  323. successCallback({});
  324. }
  325. },
  326. /**
  327. * Attempts to abort a specific request schedule identified by 'requestScheduleId'
  328. *
  329. * @param {Number} requestScheduleId ID of the request schedule to get
  330. * @param {Function} successCallback Called when request schedule successfully aborted
  331. * @param {Function} errorCallback Optional error callback. Default behavior is to
  332. * popup default error dialog.
  333. */
  334. doAbortRequestSchedule: function(requestScheduleId, successCallback, errorCallback) {
  335. if (requestScheduleId != null && !isNaN(requestScheduleId) && requestScheduleId > -1) {
  336. errorCallback = errorCallback || defaultErrorCallback;
  337. App.ajax.send({
  338. name : 'request_schedule.delete',
  339. sender : {
  340. successCallbackFunction : function(data) {
  341. successCallback(data);
  342. },
  343. errorCallbackFunction : function(xhr, textStatus, error, opt) {
  344. errorCallback(xhr, textStatus, error, opt);
  345. }
  346. },
  347. data : {
  348. request_schedule_id : requestScheduleId
  349. },
  350. success : 'successCallbackFunction',
  351. error : 'errorCallbackFunction'
  352. });
  353. } else {
  354. successCallback({});
  355. }
  356. }
  357. };