You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by sr...@apache.org on 2014/01/23 21:18:01 UTC

git commit: AMBARI-4390. Add ability to abort Rolling Restart. (srimanth)

Updated Branches:
  refs/heads/trunk 67b7c76fd -> e5d440b4f


AMBARI-4390. Add ability to abort Rolling Restart. (srimanth)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/e5d440b4
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/e5d440b4
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/e5d440b4

Branch: refs/heads/trunk
Commit: e5d440b4f1eb342e0bcd92b792fdc4240a44395f
Parents: 67b7c76
Author: Srimanth Gunturi <sg...@hortonworks.com>
Authored: Wed Jan 22 15:26:51 2014 -0800
Committer: Srimanth Gunturi <sg...@hortonworks.com>
Committed: Thu Jan 23 11:51:03 2014 -0800

----------------------------------------------------------------------
 .../global/background_operations_controller.js  | 75 +++++++++++++++++---
 ambari-web/app/controllers/main/service.js      |  8 ++-
 ambari-web/app/controllers/main/service/item.js |  4 +-
 ambari-web/app/messages.js                      |  3 +
 ambari-web/app/styles/application.less          |  7 ++
 .../templates/common/host_progress_popup.hbs    | 18 +++++
 ambari-web/app/utils/ajax.js                    | 13 ++++
 .../app/utils/batch_scheduled_requests.js       | 75 +++++++++++++++++++-
 ambari-web/app/utils/host_progress_popup.js     | 69 +++++++++++++++++-
 9 files changed, 255 insertions(+), 17 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/e5d440b4/ambari-web/app/controllers/global/background_operations_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/global/background_operations_controller.js b/ambari-web/app/controllers/global/background_operations_controller.js
index 04a5339..9905cef 100644
--- a/ambari-web/app/controllers/global/background_operations_controller.js
+++ b/ambari-web/app/controllers/global/background_operations_controller.js
@@ -178,7 +178,9 @@ App.BackgroundOperationsController = Em.Controller.extend({
           hostsMap: {},
           tasks: [],
           dependentService: requestParams.dependentService,
-          previousTaskStatusMap: {}
+          sourceRequestScheduleId: request.Requests.source_schedule_id,
+          previousTaskStatusMap: {},
+          contextCommand: requestParams.contextCommand
         });
         self.get("services").unshift(rq);
       }
@@ -202,15 +204,24 @@ App.BackgroundOperationsController = Em.Controller.extend({
   parseRequestContext: function (requestContext) {
     var parsedRequestContext;
     var service;
-    var command;
+    var contextCommand;
     if (requestContext) {
-      if (requestContext.indexOf("_PARSE_") !== -1) {
-        command = requestContext.split('.')[1];
-        service = requestContext.split('.')[2];
-        if (service === 'ALL_SERVICES') {
-          parsedRequestContext = Em.I18n.t("requestInfo." + command.toLowerCase()).format(Em.I18n.t('common.allServices'));
-        } else {
-          parsedRequestContext = Em.I18n.t("requestInfo." + command.toLowerCase()).format(App.Service.DisplayNames[service]);
+      if (requestContext.indexOf(App.BackgroundOperationsController.CommandContexts.PREFIX) !== -1) {
+        var contextSplits = requestContext.split('.');
+        contextCommand = contextSplits[1];
+        service = contextSplits[2];
+        switch(contextCommand){
+        case "STOP":
+        case "START":
+          if (service === 'ALL_SERVICES') {
+            parsedRequestContext = Em.I18n.t("requestInfo." + contextCommand.toLowerCase()).format(Em.I18n.t('common.allServices'));
+          } else {
+            parsedRequestContext = Em.I18n.t("requestInfo." + contextCommand.toLowerCase()).format(App.Service.DisplayNames[service]);
+          }
+          break;
+        case "ROLLING-RESTART":
+          parsedRequestContext = Em.I18n.t("rollingrestart.rest.context").format(App.format.role(service), contextSplits[3], contextSplits[4]);
+          break;
         }
       } else {
         parsedRequestContext = requestContext;
@@ -220,7 +231,8 @@ App.BackgroundOperationsController = Em.Controller.extend({
     }
     return {
       requestContext: parsedRequestContext,
-      dependentService: service
+      dependentService: service,
+      contextCommand: contextCommand
     }
   },
 
@@ -246,3 +258,46 @@ App.BackgroundOperationsController = Em.Controller.extend({
   }
 
 });
+
+/**
+ * Each background operation has a context in which it operates.
+ * Generally these contexts are fixed messages. However, we might
+ * want to associate semantics to this context - like showing, disabling
+ * buttons when certain operations are in progress.
+ *
+ * To make this possible we have command contexts where the context
+ * is not a human readable string, but a pattern indicating the command
+ * it is running. When UI shows these, they are translated into human
+ * readable strings.
+ *
+ * General pattern of context names is "_PARSE_.{COMMAND}.{ID}[.{Additional-Data}...]"
+ */
+App.BackgroundOperationsController.CommandContexts = {
+  PREFIX : "_PARSE_",
+  /**
+   * Stops all services
+   */
+  STOP_ALL_SERVICES : "_PARSE_.STOP.ALL_SERVICES",
+  /**
+   * Starts all services
+   */
+  START_ALL_SERVICES : "_PARSE_.START.ALL_SERVICES",
+  /**
+   * Starts service indicated by serviceID.
+   * @param {String} serviceID Parameter {0}. Example: HDFS
+   */
+  START_SERVICE : "_PARSE_.START.{0}",
+  /**
+   * Stops service indicated by serviceID.
+   * @param {String} serviceID Parameter {0}. Example: HDFS
+   */
+  STOP_SERVICE : "_PARSE_.STOP.{0}",
+  /**
+   * Performs rolling restart of componentID in batches.
+   * This context is the batchNumber batch out of totalBatchCount batches.
+   * @param {String} componentID Parameter {0}. Example "DATANODE"
+   * @param {Number} batchNumber Parameter {1}. Batch number of this batch. Example 3.
+   * @param {Number} totalBatchCount Parameter {2}. Total number of batches. Example 10.
+   */
+  ROLLING_RESTART : "_PARSE_.ROLLING-RESTART.{0}.{1}.{2}"
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/e5d440b4/ambari-web/app/controllers/main/service.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/service.js b/ambari-web/app/controllers/main/service.js
index 766fee3..27c26e2 100644
--- a/ambari-web/app/controllers/main/service.js
+++ b/ambari-web/app/controllers/main/service.js
@@ -121,9 +121,13 @@ App.MainServiceController = Em.ArrayController.extend({
   allServicesCall: function(state) {
     var data;
     if (state == 'stopAllService') {
-      data = '{"RequestInfo": {"context" :"_PARSE_.STOP.ALL_SERVICES"}, "Body": {"ServiceInfo": {"state": "INSTALLED"}}}';
+      data = '{"RequestInfo": {"context" :"' +
+        App.BackgroundOperationsController.CommandContexts.STOP_ALL_SERVICES +
+        '"}, "Body": {"ServiceInfo": {"state": "INSTALLED"}}}';
     } else {
-      data = '{"RequestInfo": {"context" :"_PARSE_.START.ALL_SERVICES"}, "Body": {"ServiceInfo": {"state": "STARTED"}}}';
+      data = '{"RequestInfo": {"context" :"' +
+        App.BackgroundOperationsController.CommandContexts.START_ALL_SERVICES +
+        '"}, "Body": {"ServiceInfo": {"state": "STARTED"}}}';
     }
 
     App.ajax.send({

http://git-wip-us.apache.org/repos/asf/ambari/blob/e5d440b4/ambari-web/app/controllers/main/service/item.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/service/item.js b/ambari-web/app/controllers/main/service/item.js
index 91e353c..b1c7b27 100644
--- a/ambari-web/app/controllers/main/service/item.js
+++ b/ambari-web/app/controllers/main/service/item.js
@@ -103,9 +103,9 @@ App.MainServiceItemController = Em.Controller.extend({
   startStopPopupPrimary: function (serviceHealth) {
     var requestInfo = "";
     if (serviceHealth == "STARTED") {
-      requestInfo = '_PARSE_.START.' + this.get('content.serviceName');
+      requestInfo = App.BackgroundOperationsController.CommandContexts.START_SERVICE.format(this.get('content.serviceName'));
     } else {
-      requestInfo = '_PARSE_.STOP.' + this.get('content.serviceName');
+      requestInfo = App.BackgroundOperationsController.CommandContexts.STOP_SERVICE.format(this.get('content.serviceName'));
     }
 
     App.ajax.send({

http://git-wip-us.apache.org/repos/asf/ambari/blob/e5d440b4/ambari-web/app/messages.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index e41b8df..1bf7a52 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -202,6 +202,9 @@ Em.I18n.translations = {
   'hostPopup.status.category.aborted':'Aborted ({0})',
   'hostPopup.status.category.timedout':'Timedout ({0})',
   'hostPopup.header.postFix':' Background Operations Running',
+  'hostPopup.bgop.sourceRequestSchedule.running': 'Future operations of this batch request can be aborted',
+  'hostPopup.bgop.sourceRequestSchedule.aborted': 'Future operations of this batch request have been aborted',
+  'hostPopup.bgop.abort.rollingRestart': 'Abort Rolling Restart',
 
   'question.sure':'Are you sure?',
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/e5d440b4/ambari-web/app/styles/application.less
----------------------------------------------------------------------
diff --git a/ambari-web/app/styles/application.less b/ambari-web/app/styles/application.less
index 30aabb6..50015c7 100644
--- a/ambari-web/app/styles/application.less
+++ b/ambari-web/app/styles/application.less
@@ -1443,6 +1443,13 @@ width:100%;
     cursor: pointer;
   }
 
+  .request-schedule-abort {
+    margin-top: 7px;
+    .btn {
+      margin-top: -5px;
+    }
+  }
+
   .task-top-wrap {
 
     width: 100%;

http://git-wip-us.apache.org/repos/asf/ambari/blob/e5d440b4/ambari-web/app/templates/common/host_progress_popup.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/common/host_progress_popup.hbs b/ambari-web/app/templates/common/host_progress_popup.hbs
index 7e43c9c..3827820 100644
--- a/ambari-web/app/templates/common/host_progress_popup.hbs
+++ b/ambari-web/app/templates/common/host_progress_popup.hbs
@@ -82,6 +82,24 @@
         }}
       </div>
     </div>
+    {{#if view.isRequestSchedule}}
+      {{#if view.sourceRequestScheduleRunning}}
+        <div class="alert alert-info request-schedule-abort">
+          {{t hostPopup.bgop.sourceRequestSchedule.running}}
+          <button type="button" class="btn btn-warning pull-right"
+            {{action doAbortRequestSchedule view.sourceRequestScheduleId target="view"}}>
+            {{view.requestScheduleAbortLabel}}
+          </button>
+        </div>
+      {{/if}}
+      {{#if view.sourceRequestScheduleAborted}}
+        <div class="alert alert-info request-schedule-abort">
+          {{t hostPopup.bgop.sourceRequestSchedule.aborted}}
+        </div>
+      {{/if}}
+    {{/if}}
+    </div>
+    </div>
     <div id="host-info">
       {{#if view.isHostEmptyList}}
         <div class="log-list-wrap">{{t hostPopup.noHostsToShow}}</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/e5d440b4/ambari-web/app/utils/ajax.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/ajax.js b/ambari-web/app/utils/ajax.js
index fb1ae7d..c7d73a1 100644
--- a/ambari-web/app/utils/ajax.js
+++ b/ambari-web/app/utils/ajax.js
@@ -1294,6 +1294,19 @@ var urls = {
       }
     }
   },
+  'request_schedule.delete': {
+    'real': '/clusters/{clusterName}/request_schedules/{request_schedule_id}',
+    'mock': '',
+    'format' : function(data) {
+      return {
+        type : 'DELETE',
+      }
+    }
+  },
+  'request_schedule.get': {
+    'real': '/clusters/{clusterName}/request_schedules/{request_schedule_id}',
+    'mock': '',
+  },
   'restart.service.hostComponents' : {
     'real' : '/clusters/{clusterName}/requests',
     'mock' : '',

http://git-wip-us.apache.org/repos/asf/ambari/blob/e5d440b4/ambari-web/app/utils/batch_scheduled_requests.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/batch_scheduled_requests.js b/ambari-web/app/utils/batch_scheduled_requests.js
index bf9164f..33dad38 100644
--- a/ambari-web/app/utils/batch_scheduled_requests.js
+++ b/ambari-web/app/utils/batch_scheduled_requests.js
@@ -163,7 +163,6 @@ module.exports = {
         batchCount = Math.ceil(restartHostComponents.length / batchSize),
         sampleHostComponent = restartHostComponents.objectAt(0),
         componentName = sampleHostComponent.get('componentName'),
-        componentDisplayName = App.format.role(componentName),
         serviceName = sampleHostComponent.get('service.serviceName');
 
     for ( var count = 0; count < batchCount; count++) {
@@ -178,7 +177,7 @@ module.exports = {
           "uri" : App.apiPrefix + "/clusters/" + App.get('clusterName') + "/requests",
           "RequestBodyInfo" : {
             "RequestInfo" : {
-              "context" : Em.I18n.t('rollingrestart.rest.context').format(componentDisplayName, (count + 1), batchCount),
+              "context" : "_PARSE_.ROLLING-RESTART." + componentName + "." + (count + 1) + "." + batchCount,
               "command" : "RESTART",
               "service_name" : serviceName,
               "component_name" : componentName,
@@ -258,5 +257,77 @@ module.exports = {
         })
       });
     }
+  },
+
+  /**
+   * Retrieves the latest information about a specific request schedule
+   * identified by 'requestScheduleId'
+   *
+   * @param {Number}
+   *          requestScheduleId ID of the request schedule to get
+   * @param {Function}
+   *          successCallback Called with request_schedule data from server. An
+   *          empty object returned for invalid ID.
+   * @param {Function}
+   *          errorCallback Optional error callback. Default behavior is to
+   *          popup default error dialog.
+   */
+  getRequestSchedule: function(requestScheduleId, successCallback, errorCallback) {
+    if (requestScheduleId != null && !isNaN(requestScheduleId) && requestScheduleId > -1) {
+      errorCallback = errorCallback ? errorCallback : defaultErrorCallback;
+      App.ajax.send({
+        name : 'request_schedule.get',
+        sender : {
+          successCallbackFunction : function(data) {
+            successCallback(data);
+          },
+          errorCallbackFunction : function(xhr, textStatus, error, opt) {
+            errorCallback(xhr, textStatus, error, opt);
+          }
+        },
+        data : {
+          request_schedule_id : requestScheduleId,
+        },
+        success : 'successCallbackFunction',
+        error : 'errorCallbackFunction'
+      });
+    } else {
+      successCallback({});
+    }
+  },
+
+  /**
+   * Attempts to abort a specific request schedule identified by 'requestScheduleId'
+   *
+   * @param {Number}
+   *          requestScheduleId ID of the request schedule to get
+   * @param {Function}
+   *          successCallback Called when request schedule successfully aborted
+   * @param {Function}
+   *          errorCallback Optional error callback. Default behavior is to
+   *          popup default error dialog.
+   */
+  doAbortRequestSchedule: function(requestScheduleId, successCallback, errorCallback) {
+    if (requestScheduleId != null && !isNaN(requestScheduleId) && requestScheduleId > -1) {
+      errorCallback = errorCallback ? errorCallback : defaultErrorCallback;
+      App.ajax.send({
+        name : 'request_schedule.delete',
+        sender : {
+          successCallbackFunction : function(data) {
+            successCallback(data);
+          },
+          errorCallbackFunction : function(xhr, textStatus, error, opt) {
+            errorCallback(xhr, textStatus, error, opt);
+          }
+        },
+        data : {
+          request_schedule_id : requestScheduleId,
+        },
+        success : 'successCallbackFunction',
+        error : 'errorCallbackFunction'
+      });
+    } else {
+      successCallback({});
+    }
   }
 };
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/e5d440b4/ambari-web/app/utils/host_progress_popup.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/host_progress_popup.js b/ambari-web/app/utils/host_progress_popup.js
index e20b7da..fe921eb 100644
--- a/ambari-web/app/utils/host_progress_popup.js
+++ b/ambari-web/app/utils/host_progress_popup.js
@@ -17,6 +17,7 @@
  */
 
 var App = require('app');
+var batchUtils = require('utils/batch_scheduled_requests');
 
 /**
  * App.HostPopup is for the popup that shows up upon clicking already-performed or currently-in-progress operations
@@ -244,7 +245,9 @@ App.HostPopup = Em.Object.create({
           icon: status[1],
           barColor: status[2],
           isInProgress: status[3],
-          barWidth: "width:" + service.progress + "%;"
+          barWidth: "width:" + service.progress + "%;",
+          sourceRequestScheduleId: service.get('sourceRequestScheduleId'),
+          contextCommand: service.get('contextCommand')
         });
         allNewServices.push(newService);
       });
@@ -467,6 +470,10 @@ App.HostPopup = Em.Object.create({
         isHostEmptyList: true,
         isTasksEmptyList: true,
         controller: this,
+        sourceRequestScheduleId: -1,
+        sourceRequestScheduleRunning: false,
+        sourceRequestScheduleAborted: false,
+        sourceRequestScheduleCommand: null,
         hosts: self.get('hosts'),
         services: self.get('servicesInfo'),
 
@@ -722,8 +729,68 @@ App.HostPopup = Em.Object.create({
               this.set('hosts', this.get('hosts').concat(servicesInfo.slice(50, servicesInfo.length)));
             });
           }
+          // Determine if source request schedule is present
+          this.set('sourceRequestScheduleId', event.context.get("sourceRequestScheduleId"));
+          this.set('sourceRequestScheduleCommand', event.context.get('contextCommand'));
+          this.refreshRequestScheduleInfo();
         },
 
+        isRequestSchedule : function() {
+          var id = this.get('sourceRequestScheduleId');
+          return id != null && !isNaN(id) && id > -1;
+        }.property('sourceRequestScheduleId'),
+
+        refreshRequestScheduleInfo : function() {
+          var self = this;
+          var id = this.get('sourceRequestScheduleId');
+          batchUtils.getRequestSchedule(id, function(data) {
+            if (data != null && data.RequestSchedule.status != null) {
+              switch (data.RequestSchedule.status) {
+              case 'DISABLED':
+                self.set('sourceRequestScheduleRunning', false);
+                self.set('sourceRequestScheduleAborted', true);
+                break;
+              case 'COMPLETED':
+                self.set('sourceRequestScheduleRunning', false);
+                self.set('sourceRequestScheduleAborted', false);
+                break;
+              case 'SCHEDULED':
+                self.set('sourceRequestScheduleRunning', true);
+                self.set('sourceRequestScheduleAborted', false);
+                break;
+              }
+            } else {
+              self.set('sourceRequestScheduleRunning', false);
+              self.set('sourceRequestScheduleAborted', false);
+            }
+          }, function(xhr, textStatus, error, opt) {
+            console.log("Error getting request schedule information: ", textStatus, error, opt);
+            self.set('sourceRequestScheduleRunning', false);
+            self.set('sourceRequestScheduleAborted', false);
+          });
+        }.observes('sourceRequestScheduleId'),
+
+        /**
+         * Attempts to abort the current request schedule
+         */
+        doAbortRequestSchedule: function(event){
+          var self = this;
+          var id = event.context;
+          console.log("Aborting request schedule: ", id);
+          batchUtils.doAbortRequestSchedule(id, function(){
+            self.refreshRequestScheduleInfo();
+          });
+        },
+
+        requestScheduleAbortLabel : function() {
+          var label = Em.I18n.t("common.abort");
+          var command = this.get('sourceRequestScheduleCommand');
+          if (command != null && "ROLLING-RESTART" == command) {
+            label = Em.I18n.t("hostPopup.bgop.abort.rollingRestart");
+          }
+          return label;
+        }.property('sourceRequestScheduleCommand'),
+
         /**
          * Onclick handler for selected Host
          * @param event