batch_scheduled_requests.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  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 {Boolean} staleConfigsOnly restart only hostComponents with <code>staleConfig</code> true
  64. */
  65. restartAllServiceHostComponents: function(serviceName, staleConfigsOnly) {
  66. var service = App.Service.find(serviceName);
  67. if (service) {
  68. var hostComponents = service.get('hostComponents');
  69. if (staleConfigsOnly) {
  70. hostComponents = hostComponents.filterProperty('staleConfigs', true);
  71. }
  72. this.restartHostComponents(hostComponents);
  73. }
  74. },
  75. /**
  76. * Restart list of host components
  77. * @param {Array} hostComponentsList list of host components should be restarted
  78. */
  79. restartHostComponents: function(hostComponentsList) {
  80. /**
  81. * Format: {
  82. * 'DATANODE': ['host1', 'host2'],
  83. * 'NAMENODE': ['host1', 'host3']
  84. * ...
  85. * }
  86. * @type {Object}
  87. */
  88. var componentToHostsMap = {};
  89. var componentServiceMap = App.QuickDataMapper.componentServiceMap();
  90. hostComponentsList.forEach(function(hc) {
  91. var componentName = hc.get('componentName');
  92. if (!componentToHostsMap[componentName]) {
  93. componentToHostsMap[componentName] = [];
  94. }
  95. componentToHostsMap[componentName].push(hc.get('host.hostName'));
  96. });
  97. for (var componentName in componentToHostsMap) {
  98. App.ajax.send({
  99. name: 'restart.service.hostComponents',
  100. sender: {
  101. successCallback: defaultSuccessCallback,
  102. errorCallback: defaultErrorCallback
  103. },
  104. data: {
  105. serviceName: componentServiceMap[componentName],
  106. componentName: componentName,
  107. hosts: componentToHostsMap[componentName].join(",")
  108. },
  109. success: 'successCallback',
  110. error: 'errorCallback'
  111. });
  112. }
  113. },
  114. /**
  115. * Makes a REST call to the server requesting the rolling restart of the
  116. * provided host components.
  117. * @param {Array} restartHostComponents list of host components should be restarted
  118. * @param {Number} batchSize size of each batch
  119. * @param {Number} intervalTimeSeconds delay between two batches
  120. * @param {Number} tolerateSize task failure tolerance
  121. * @param {Function} successCallback
  122. * @param {Function} errorCallback
  123. */
  124. _doPostBatchRollingRestartRequest: function(restartHostComponents, batchSize, intervalTimeSeconds, tolerateSize, successCallback, errorCallback) {
  125. successCallback = successCallback ? successCallback : defaultSuccessCallback;
  126. errorCallback = errorCallback ? errorCallback : defaultErrorCallback;
  127. if (!restartHostComponents.length) {
  128. console.log('No batch rolling restart if no restartHostComponents provided!');
  129. return;
  130. }
  131. App.ajax.send({
  132. name: 'rolling_restart.post',
  133. sender: {
  134. successCallback: successCallback,
  135. errorCallback: errorCallback
  136. },
  137. data: {
  138. intervalTimeSeconds: intervalTimeSeconds,
  139. tolerateSize: tolerateSize,
  140. batches: this.getBatchesForRollingRestartRequest(restartHostComponents, batchSize)
  141. },
  142. success: 'successCallback',
  143. error: 'errorCallback'
  144. });
  145. },
  146. /**
  147. * Create list of batches for rolling restart request
  148. * @param {Array} restartHostComponents list host components should be restarted
  149. * @param {Number} batchSize size of each batch
  150. * @returns {Array} list of batches
  151. */
  152. getBatchesForRollingRestartRequest: function(restartHostComponents, batchSize) {
  153. var hostIndex = 0,
  154. batches = [],
  155. batchCount = Math.ceil(restartHostComponents.length / batchSize),
  156. sampleHostComponent = restartHostComponents.objectAt(0),
  157. componentName = sampleHostComponent.get('componentName'),
  158. serviceName = sampleHostComponent.get('service.serviceName');
  159. for ( var count = 0; count < batchCount; count++) {
  160. var hostNames = [];
  161. for ( var hc = 0; hc < batchSize && hostIndex < restartHostComponents.length; hc++) {
  162. hostNames.push(restartHostComponents.objectAt(hostIndex++).get('host.hostName'));
  163. }
  164. if (hostNames.length > 0) {
  165. batches.push({
  166. "order_id" : count + 1,
  167. "type" : "POST",
  168. "uri" : App.apiPrefix + "/clusters/" + App.get('clusterName') + "/requests",
  169. "RequestBodyInfo" : {
  170. "RequestInfo" : {
  171. "context" : "_PARSE_.ROLLING-RESTART." + componentName + "." + (count + 1) + "." + batchCount,
  172. "command" : "RESTART",
  173. "service_name" : serviceName,
  174. "component_name" : componentName,
  175. "hosts" : hostNames.join(",")
  176. }
  177. }
  178. });
  179. }
  180. }
  181. return batches;
  182. },
  183. /**
  184. * Launches dialog to handle rolling restarts of host components.
  185. *
  186. * Rolling restart is supported only for components listed in <code>getRollingRestartComponentName</code>
  187. * @see getRollingRestartComponentName
  188. * @param {String} hostComponentName
  189. * Type of host-component to restart across cluster
  190. * (ex: DATANODE)
  191. * @param {Boolean} staleConfigsOnly
  192. * Pre-select host-components which have stale
  193. * configurations
  194. */
  195. launchHostComponentRollingRestart: function(hostComponentName, staleConfigsOnly) {
  196. var componentDisplayName = App.format.role(hostComponentName);
  197. if (!componentDisplayName) {
  198. componentDisplayName = hostComponentName;
  199. }
  200. var self = this;
  201. var title = Em.I18n.t('rollingrestart.dialog.title').format(componentDisplayName);
  202. var allowedHostComponents = ["DATANODE", "TASKTRACKER", "NODEMANAGER", "HBASE_REGIONSERVER", "SUPERVISOR"];
  203. if (allowedHostComponents.contains(hostComponentName)) {
  204. App.ModalPopup.show({
  205. header : title,
  206. hostComponentName : hostComponentName,
  207. staleConfigsOnly : staleConfigsOnly,
  208. innerView : null,
  209. bodyClass : App.RollingRestartView.extend({
  210. hostComponentName : hostComponentName,
  211. staleConfigsOnly : staleConfigsOnly,
  212. didInsertElement : function() {
  213. this.set('parentView.innerView', this);
  214. this.initialize();
  215. }
  216. }),
  217. classNames : [ 'rolling-restart-popup' ],
  218. primary : Em.I18n.t('rollingrestart.dialog.primary'),
  219. onPrimary : function() {
  220. var dialog = this;
  221. if (!dialog.get('enablePrimary')) {
  222. return;
  223. }
  224. var restartComponents = this.get('innerView.restartHostComponents');
  225. var batchSize = this.get('innerView.batchSize');
  226. var waitTime = this.get('innerView.interBatchWaitTimeSeconds');
  227. var tolerateSize = this.get('innerView.tolerateSize');
  228. self._doPostBatchRollingRestartRequest(restartComponents, batchSize, waitTime, tolerateSize, function() {
  229. dialog.hide();
  230. defaultSuccessCallback();
  231. });
  232. },
  233. updateButtons : function() {
  234. var errors = this.get('innerView.errors');
  235. this.set('enablePrimary', !(errors != null && errors.length > 0))
  236. }.observes('innerView.errors')
  237. });
  238. } else {
  239. var msg = Em.I18n.t('rollingrestart.notsupported.hostComponent').format(componentDisplayName);
  240. console.log(msg);
  241. App.ModalPopup.show({
  242. header : title,
  243. secondary : false,
  244. msg : msg,
  245. bodyClass : Ember.View.extend({
  246. template : Ember.Handlebars.compile('<div class="alert alert-warning">{{msg}}</div>')
  247. })
  248. });
  249. }
  250. },
  251. /**
  252. * Retrieves the latest information about a specific request schedule
  253. * identified by 'requestScheduleId'
  254. *
  255. * @param {Number}
  256. * requestScheduleId ID of the request schedule to get
  257. * @param {Function}
  258. * successCallback Called with request_schedule data from server. An
  259. * empty object returned for invalid ID.
  260. * @param {Function}
  261. * errorCallback Optional error callback. Default behavior is to
  262. * popup default error dialog.
  263. */
  264. getRequestSchedule: function(requestScheduleId, successCallback, errorCallback) {
  265. if (requestScheduleId != null && !isNaN(requestScheduleId) && requestScheduleId > -1) {
  266. errorCallback = errorCallback ? errorCallback : defaultErrorCallback;
  267. App.ajax.send({
  268. name : 'request_schedule.get',
  269. sender : {
  270. successCallbackFunction : function(data) {
  271. successCallback(data);
  272. },
  273. errorCallbackFunction : function(xhr, textStatus, error, opt) {
  274. errorCallback(xhr, textStatus, error, opt);
  275. }
  276. },
  277. data : {
  278. request_schedule_id : requestScheduleId,
  279. },
  280. success : 'successCallbackFunction',
  281. error : 'errorCallbackFunction'
  282. });
  283. } else {
  284. successCallback({});
  285. }
  286. },
  287. /**
  288. * Attempts to abort a specific request schedule identified by 'requestScheduleId'
  289. *
  290. * @param {Number}
  291. * requestScheduleId ID of the request schedule to get
  292. * @param {Function}
  293. * successCallback Called when request schedule successfully aborted
  294. * @param {Function}
  295. * errorCallback Optional error callback. Default behavior is to
  296. * popup default error dialog.
  297. */
  298. doAbortRequestSchedule: function(requestScheduleId, successCallback, errorCallback) {
  299. if (requestScheduleId != null && !isNaN(requestScheduleId) && requestScheduleId > -1) {
  300. errorCallback = errorCallback ? errorCallback : defaultErrorCallback;
  301. App.ajax.send({
  302. name : 'request_schedule.delete',
  303. sender : {
  304. successCallbackFunction : function(data) {
  305. successCallback(data);
  306. },
  307. errorCallbackFunction : function(xhr, textStatus, error, opt) {
  308. errorCallback(xhr, textStatus, error, opt);
  309. }
  310. },
  311. data : {
  312. request_schedule_id : requestScheduleId,
  313. },
  314. success : 'successCallbackFunction',
  315. error : 'errorCallbackFunction'
  316. });
  317. } else {
  318. successCallback({});
  319. }
  320. }
  321. };