Преглед на файлове

AMBARI-6795. Expose ability to cancel requests in the UI (alexantonenko)

Alex Antonenko преди 10 години
родител
ревизия
4da141ec69

+ 2 - 1
ambari-web/app/assets/test/tests.js

@@ -117,6 +117,7 @@ var files = ['test/init_model_test',
   'test/utils/date_test',
   'test/utils/config_test',
   'test/utils/form_field_test',
+  'test/utils/host_progress_popup_test',
   'test/utils/misc_test',
   'test/utils/number_utils_test',
   'test/utils/validator_test',
@@ -213,4 +214,4 @@ describe('Ambari Web Unit tests', function() {
 
   }
 
-});
+});

+ 2 - 1
ambari-web/app/config.js

@@ -82,7 +82,8 @@ App.supports = {
   databaseConnection: true,
   configHistory: false,
   serverRecommendValidate: false,
-  downloadClientConfigs: false
+  downloadClientConfigs: false,
+  abortRequests: false
 };
 
 if (App.enableExperimental) {

+ 5 - 0
ambari-web/app/messages.js

@@ -254,6 +254,11 @@ Em.I18n.translations = {
   'hostPopup.status.category.timedout':'Timedout ({0})',
   'hostPopup.header.postFix':' Background Operations Running',
   'hostPopup.serviceInfo.showMore':'Show more...',
+  'hostPopup.bgop.abortRequest.title': 'Abort request',
+  'hostPopup.bgop.abortRequest.confirmation.body': 'Are you sure you want to abort \'{0}\' operation?',
+  'hostPopup.bgop.abortRequest.reason': 'Aborted by user',
+  'hostPopup.bgop.abortRequest.modal.header': 'Abort request sent',
+  'hostPopup.bgop.abortRequest.modal.body': 'The request to abort \'{0}\' operation is sent to server. Note that some tasks that are already running may have enough time to finish as completed or failed ones before abort request is applied.',
   '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',

+ 5 - 0
ambari-web/app/styles/application.less

@@ -1928,6 +1928,11 @@ width:100%;
   .modal-body, .modal-footer, .modal-header {
     min-width: 600px;
   }
+  #service-info {
+    .log-list-wrap:hover {
+      background-color: #e6e6e6;
+    }
+  }
 }
 .host-component-popup-wrap {
   .task-top-wrap {

+ 4 - 1
ambari-web/app/templates/common/host_progress_popup.hbs

@@ -44,7 +44,10 @@
             <div {{bindAttr class="servicesInfo.isVisible::hidden :log-list-wrap"}}>
               <div {{action gotoHosts servicesInfo}} class="task-list-line-cursor">
                 <div class="operation-name-icon-wrap">
-                  <i {{bindAttr class="servicesInfo.status servicesInfo.icon"}}></i>
+                  <i {{bindAttr class=":service-status servicesInfo.status servicesInfo.icon"}}></i>
+                  {{#if App.supports.abortRequests}}
+                    <i {{action abortRequest servicesInfo}} {{translateAttr title="hostPopup.bgop.abortRequest.title"}} class="abort-icon icon-remove-circle hidden"></i>
+                  {{/if}}
                   <a href="#">
                     {{servicesInfo.name}}
                   </a>

+ 15 - 0
ambari-web/app/utils/ajax/ajax.js

@@ -286,6 +286,21 @@ var urls = {
     'mock': '/data/background_operations/list_on_start.json',
     'testInProduction': true
   },
+  'background_operations.abort_request': {
+    'real': '/clusters/{clusterName}/requests/{requestId}',
+    'mock': '',
+    'format': function () {
+      return {
+        type: 'PUT',
+        data: JSON.stringify({
+          "Requests": {
+            "request_status": "ABORTED",
+            "abort_reason": Em.I18n.t('hostPopup.bgop.abortRequest.reason')
+          }
+        })
+      };
+    }
+  },
   'service.item.smoke': {
     'real': '/clusters/{clusterName}/requests',
     'mock': '/data/wizard/deploy/poll_1.json',

+ 50 - 0
ambari-web/app/utils/host_progress_popup.js

@@ -479,6 +479,21 @@ App.HostPopup = Em.Object.create({
       isOpen: false,
 
       didInsertElement: function(){
+        if (App.get('supports.abortRequests')) {
+          $(document).on({
+            mouseenter: function () {
+              if ($(this).find('.service-status').hasClass(App.format.taskStatus('IN_PROGRESS')) || $(this).find('.service-status').hasClass(App.format.taskStatus('PENDING'))) {
+                App.tooltip($('.abort-icon'));
+                $(this).find('.service-status').addClass('hidden');
+                $(this).find('.abort-icon').removeClass('hidden');
+              }
+            },
+            mouseleave: function () {
+              $(this).find('.abort-icon').addClass('hidden');
+              $(this).find('.service-status').removeClass('hidden');
+            }
+          }, '#service-info .log-list-wrap');
+        }
         this._super();
         this.set('isOpen', true);
       },
@@ -996,6 +1011,41 @@ App.HostPopup = Em.Object.create({
           $(".modal-body").scrollTop(0);
         },
 
+        /**
+         * Send request to abort operation
+         */
+        abortRequest: function (event) {
+          var requestName = event.context.get('name');
+          var self = this;
+          App.showConfirmationPopup(function () {
+            App.ajax.send({
+              name: 'background_operations.abort_request',
+              sender: self,
+              data: {
+                requestId: event.context.get('id'),
+                requestName: requestName
+              },
+              success: 'abortRequestSuccessCallback',
+              error: 'abortRequestErrorCallback'
+            });
+          }, Em.I18n.t('hostPopup.bgop.abortRequest.confirmation.body').format(requestName));
+          return false;
+        },
+
+        /**
+         * Method called on successful sending request to abort operation
+         */
+        abortRequestSuccessCallback: function (response, request, data) {
+          App.showAlertPopup(Em.I18n.t('hostPopup.bgop.abortRequest.modal.header'), Em.I18n.t('hostPopup.bgop.abortRequest.modal.body').format(data.requestName));
+        },
+
+        /**
+         * Method called on unsuccessful sending request to abort operation
+         */
+        abortRequestErrorCallback: function (xhr, textStatus, error, opt) {
+          App.ajax.defaultErrorHandler(xhr, opt.url, 'PUT', xhr.status);
+        },
+
         /**
          * Onclick event for copy to clipboard button
          */

+ 59 - 1
ambari-web/test/utils/host_progress_popup_test.js

@@ -165,7 +165,7 @@ describe('App.HostPopup', function () {
         }
       ],
       m: 'One ABORTED',
-      r: 'CANCELLED',
+      r: 'ABORTED',
       p: 100,
       ids: [1,2]
     },
@@ -334,4 +334,62 @@ describe('App.HostPopup', function () {
     });
   });
 
+  describe('#abortRequest', function () {
+    beforeEach(function () {
+      sinon.stub(App.ajax, 'send', Em.K);
+      sinon.spy(App, 'showConfirmationPopup');
+      App.HostPopup.createPopup();
+    });
+    afterEach(function () {
+      App.HostPopup.get('isPopup').hide();
+      App.ajax.send.restore();
+      App.showConfirmationPopup.restore();
+    });
+    it('should show confirmation popup', function () {
+      App.HostPopup.get('isPopup.bodyClass').create().abortRequest({
+        context: Em.Object.create({
+          name: 'name'
+        })
+      });
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#abortRequestSuccessCallback', function () {
+    beforeEach(function () {
+      App.HostPopup.createPopup();
+      sinon.spy(App.ModalPopup, 'show');
+    });
+    afterEach(function () {
+      App.HostPopup.get('isPopup').hide();
+      App.ModalPopup.show.restore();
+    });
+    it('should open popup', function () {
+      App.HostPopup.get('isPopup.bodyClass').create().abortRequestSuccessCallback(null, null, {
+        requestName: 'name'
+      });
+      expect(App.ModalPopup.show.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#abortRequestErrorCallback', function () {
+    beforeEach(function () {
+      App.HostPopup.createPopup();
+      sinon.spy(App.ModalPopup, 'show');
+    });
+    afterEach(function () {
+      App.HostPopup.get('isPopup').hide();
+      App.ModalPopup.show.restore();
+    });
+    it('should open popup', function () {
+      App.HostPopup.get('isPopup.bodyClass').create().abortRequestErrorCallback({
+        responseText: {
+          message: 'message'
+        },
+        status: 404
+      }, 'url', 'PUT', 404);
+      expect(App.ModalPopup.show.calledOnce).to.be.true;
+    });
+  });
+
 });