Преглед изворни кода

AMBARI-9931 RU: upgrade dialog does not refresh with current tasks w/o browser reload. (atkach)

Andrii Tkach пре 10 година
родитељ
комит
f149fd15cf

+ 57 - 5
ambari-web/app/controllers/main/admin/stack_and_upgrade_controller.js

@@ -73,6 +73,12 @@ App.MainAdminStackAndUpgradeController = Em.Controller.extend(App.LocalStorage,
    */
   wizardStorageProperties: ['upgradeId', 'upgradeVersion', 'currentVersion', 'isDowngrade'],
 
+  /**
+   * mutable properties of Upgrade Task
+   * @type {Array}
+   */
+  taskDetailsProperties: ['status', 'stdout', 'stderr', 'error_log', 'host_name', 'output_log'],
+
   /**
    * path to the mock json
    * @type {String}
@@ -199,10 +205,9 @@ App.MainAdminStackAndUpgradeController = Em.Controller.extend(App.LocalStorage,
   updateUpgradeData: function (newData) {
     var oldData = this.get('upgradeData'),
       groupsMap = {},
-      itemsMap = {},
-      tasksMap = {};
+      itemsMap = {};
 
-    if (Em.isNone(oldData)) {
+    if (Em.isNone(oldData) || (newData.upgrade_groups.length !== oldData.upgradeGroups.length)) {
       this.initUpgradeData(newData);
     } else {
       //create entities maps
@@ -241,8 +246,7 @@ App.MainAdminStackAndUpgradeController = Em.Controller.extend(App.LocalStorage,
       var upgradeItems = [];
       newGroup.upgrade_items.forEach(function (item) {
         var oldItem = App.upgradeEntity.create({type: 'ITEM'}, item.UpgradeItem);
-        var tasks = [];
-        oldItem.set('tasks', tasks);
+        oldItem.set('tasks', []);
         upgradeItems.pushObject(oldItem);
       });
       upgradeItems.reverse();
@@ -256,6 +260,54 @@ App.MainAdminStackAndUpgradeController = Em.Controller.extend(App.LocalStorage,
     }));
   },
 
+  /**
+   * request Upgrade Item and its tasks from server
+   * @return {$.ajax}
+   */
+  getUpgradeItem: function (item) {
+    return App.ajax.send({
+      name: 'admin.upgrade.upgrade_item',
+      sender: this,
+      data: {
+        upgradeId: item.get('request_id'),
+        groupId: item.get('group_id'),
+        stageId: item.get('stage_id')
+      },
+      success: 'getUpgradeItemSuccessCallback'
+    });
+  },
+
+  /**
+   * success callback of <code>getTasks</code>
+   * @param {object} data
+   */
+  getUpgradeItemSuccessCallback: function (data) {
+    this.get('upgradeData.upgradeGroups').forEach(function (group) {
+      if (group.get('group_id') === data.UpgradeItem.group_id) {
+        group.get('upgradeItems').forEach(function (item) {
+          if (item.get('stage_id') === data.UpgradeItem.stage_id) {
+            if (item.get('tasks.length')) {
+              item.set('isTasksLoaded', true);
+              data.tasks.forEach(function (task) {
+                var currentTask = item.get('tasks').findProperty('id', task.Tasks.id);
+                this.get('taskDetailsProperties').forEach(function (property) {
+                  currentTask.set(property, task.Tasks[property]);
+                }, this);
+              }, this);
+            } else {
+              var tasks = [];
+              data.tasks.forEach(function (task) {
+                tasks.pushObject(App.upgradeEntity.create({type: 'TASK'}, task.Tasks));
+              });
+              item.set('tasks', tasks);
+            }
+            item.set('isTasksLoaded', true);
+          }
+        }, this);
+      }
+    }, this);
+  },
+
   /**
    * downgrade confirmation popup
    * @param {object} event

+ 17 - 9
ambari-web/app/templates/main/admin/stack_upgrade/stack_upgrade_wizard.hbs

@@ -39,10 +39,14 @@
             <div class="row-fluid">
               <div class="pull-left">{{t admin.stackUpgrade.dialog.inProgress}}&nbsp;{{view.runningItem.text}}</div>
               {{#if view.isDetailsOpened}}
-                <a href="#" class="pull-right" {{action toggleDetails target="view"}}>{{t admin.stackUpgrade.dialog.details.hide}}</a>
-                <div class="clear">
-                  {{view App.upgradeTaskView contentBinding="view.taskDetails" outsideViewBinding="view.outsideView"}}
-                </div>
+                  <a href="#" class="pull-right" {{action toggleDetails target="view"}}>{{t admin.stackUpgrade.dialog.details.hide}}</a>
+                {{#if view.runningItem.isTasksLoaded}}
+                  <div class="clear">
+                    {{view App.upgradeTaskView contentBinding="view.taskDetails" outsideViewBinding="view.outsideView"}}
+                  </div>
+                {{else}}
+                  <div class="clear spinner"></div>
+                {{/if}}
               {{else}}
                 <a href="#" class="pull-right" {{action toggleDetails target="view"}}>{{t admin.stackUpgrade.dialog.details.open}}</a>
               {{/if}}
@@ -54,11 +58,15 @@
             <div class="row-fluid">
               <div class="pull-left">{{t admin.stackUpgrade.dialog.failed}}&nbsp;{{view.failedItem.text}}</div>
               {{#if view.isDetailsOpened}}
-                <a href="#" class="pull-right" {{action toggleDetails target="view"}}>{{t admin.stackUpgrade.dialog.details.hide}}</a>
-                <div class="clear">
-                  {{view App.upgradeTaskView contentBinding="view.taskDetails" outsideViewBinding="view.outsideView"}}
-                </div>
-              {{else}}
+                  <a href="#" class="pull-right" {{action toggleDetails target="view"}}>{{t admin.stackUpgrade.dialog.details.hide}}</a>
+                {{#if view.failedItem.isTasksLoaded}}
+                  <div class="clear">
+                    {{view App.upgradeTaskView contentBinding="view.taskDetails" outsideViewBinding="view.outsideView"}}
+                  </div>
+                {{else}}
+                  <div class="clear spinner"></div>
+                {{/if}}
+               {{else}}
                 <a href="#" class="pull-right" {{action toggleDetails target="view"}}>{{t admin.stackUpgrade.dialog.details.open}}</a>
               {{/if}}
             </div>

+ 12 - 11
ambari-web/app/templates/main/admin/stack_upgrade/upgrade_group.hbs

@@ -57,19 +57,20 @@
           </div>
         {{/if}}
       </div>
-    {{/if}}
-    {{#if item.isExpanded}}
 
-      {{#if item.isTasksLoaded}}
-      {{! List of Tasks}}
-        <div class="task-list margin-bottom-5">
-          {{#each task in item.tasks}}
-            {{view App.upgradeTaskView contentBinding="task" tasksBinding="item.tasks"}}
-          {{/each}}
-        </div>
-      {{else}}
-        <div class="spinner"></div>
+      {{#if item.isExpanded}}
+        {{#if item.isTasksLoaded}}
+        {{! List of Tasks}}
+          <div class="task-list margin-bottom-5">
+            {{#each task in item.tasks}}
+              {{view App.upgradeTaskView contentBinding="task" tasksBinding="item.tasks"}}
+            {{/each}}
+          </div>
+        {{else}}
+          <div class="spinner"></div>
+        {{/if}}
       {{/if}}
+
     {{/if}}
   {{/each}}
 </div>

+ 38 - 34
ambari-web/app/templates/main/admin/stack_upgrade/upgrade_task.hbs

@@ -22,44 +22,48 @@
   </div>
 {{/unless}}
 {{#if view.showContent}}
-  <div class="task-details task-detail-info">
-    <ul class="nav nav-tabs">
-      <li class="active"><a data-toggle="tab" {{bindAttr href="view.logTabIdLink"}}>{{t common.stdout}}</a></li>
-      <li><a {{bindAttr href="view.errorTabIdLInk"}} data-toggle="tab">{{t common.stderr}}</a></li>
-    </ul>
-    <div class="tab-content">
-      <div class="tab-pane active" {{bindAttr id="view.logTabId"}}>
-        <p>{{view.content.host_name}}</p>
-        <div class="row-fluid">
-          <p class="pull-left">{{view.content.output_log}}</p>
-          <div class="manage-controls pull-right">
-            <a title="Click to Copy" {{action copyOutLog view.content target="view"}} class="task-detail-copy">
-              <i class="icon-copy"></i> {{t common.copy}}
-            </a>
-            <a title="Open in New Window" {{action openOutLog target="view"}} class="task-detail-open-dialog">
-              <i class="icon-external-link"></i> {{t common.open}}
-            </a>
+  {{#if view.content}}
+    <div class="task-details task-detail-info">
+      <ul class="nav nav-tabs">
+        <li class="active"><a data-toggle="tab" {{bindAttr href="view.logTabIdLink"}}>{{t common.stdout}}</a></li>
+        <li><a {{bindAttr href="view.errorTabIdLInk"}} data-toggle="tab">{{t common.stderr}}</a></li>
+      </ul>
+      <div class="tab-content">
+        <div class="tab-pane active" {{bindAttr id="view.logTabId"}}>
+          <p>{{view.content.host_name}}</p>
+          <div class="row-fluid">
+            <p class="pull-left">{{view.content.output_log}}</p>
+            <div class="manage-controls pull-right">
+              <a title="Click to Copy" {{action copyOutLog view.content target="view"}} class="task-detail-copy">
+                <i class="icon-copy"></i> {{t common.copy}}
+              </a>
+              <a title="Open in New Window" {{action openOutLog target="view"}} class="task-detail-open-dialog">
+                <i class="icon-external-link"></i> {{t common.open}}
+              </a>
+            </div>
           </div>
+          <pre {{bindAttr class="view.outputLogOpened:hidden :stdout"}}>{{view.content.stdout}}</pre>
+          {{view Ember.TextArea valueBinding="view.content.stdout" classBinding="view.outputLogOpened::hidden" readonly="readonly"}}
         </div>
-        <pre {{bindAttr class="view.outputLogOpened:hidden :stdout"}}>{{view.content.stdout}}</pre>
-        {{view Ember.TextArea valueBinding="view.content.stdout" classBinding="view.outputLogOpened::hidden" readonly="readonly"}}
-      </div>
-      <div class="tab-pane" {{bindAttr id="view.errorTabId"}}>
-        <p>{{view.content.host_name}}</p>
-        <div class="row-fluid">
-          <p class="pull-left">{{view.content.error_log}}</p>
-          <div class="manage-controls pull-right">
-            <a title="Click to Copy" {{action copyErrLog view.content target="view"}} class="task-detail-copy">
-              <i class="icon-copy"></i> {{t common.copy}}
-            </a>
-            <a title="Open in New Window" {{action openErrorLog target="view"}} class="task-detail-open-dialog">
-              <i class="icon-external-link"></i> {{t common.open}}
-            </a>
+        <div class="tab-pane" {{bindAttr id="view.errorTabId"}}>
+          <p>{{view.content.host_name}}</p>
+          <div class="row-fluid">
+            <p class="pull-left">{{view.content.error_log}}</p>
+            <div class="manage-controls pull-right">
+              <a title="Click to Copy" {{action copyErrLog view.content target="view"}} class="task-detail-copy">
+                <i class="icon-copy"></i> {{t common.copy}}
+              </a>
+              <a title="Open in New Window" {{action openErrorLog target="view"}} class="task-detail-open-dialog">
+                <i class="icon-external-link"></i> {{t common.open}}
+              </a>
+            </div>
           </div>
+          <pre {{bindAttr class="view.errorLogOpened:hidden :stderr"}}>{{view.content.stderr}}</pre>
+          {{view Ember.TextArea valueBinding="view.content.stderr" classBinding="view.errorLogOpened::hidden" readonly="readonly"}}
         </div>
-        <pre {{bindAttr class="view.errorLogOpened:hidden :stderr"}}>{{view.content.stderr}}</pre>
-        {{view Ember.TextArea valueBinding="view.content.stderr" classBinding="view.errorLogOpened::hidden" readonly="readonly"}}
       </div>
     </div>
-  </div>
+  {{else}}
+    <div class="spinner"></div>
+  {{/if}}
 {{/if}}

+ 1 - 54
ambari-web/app/views/main/admin/stack_upgrade/upgrade_group_view.js

@@ -22,11 +22,6 @@ var App = require('app');
 App.upgradeGroupView = Em.View.extend({
   templateName: require('templates/main/admin/stack_upgrade/upgrade_group'),
 
-  /**
-   * @type {Array}
-   */
-  taskDetailsProperties: ['status', 'stdout', 'stderr', 'error_log', 'host_name', 'output_log'],
-
   /**
    * Only one UpgradeGroup or UpgradeItem could be expanded at a time
    * @param {object} event
@@ -69,7 +64,7 @@ App.upgradeGroupView = Em.View.extend({
     var self = this;
 
     if (item && item.get('isExpanded')) {
-      this.getTasks(item).complete(function () {
+      this.get('controller').getUpgradeItem(item).complete(function () {
         self.set('timer', setTimeout(function () {
           self.doPolling(item);
         }, App.bgOperationsUpdateInterval));
@@ -79,54 +74,6 @@ App.upgradeGroupView = Em.View.extend({
     }
   },
 
-  /**
-   * request tasks from server
-   * @return {$.ajax}
-   */
-  getTasks: function (item) {
-    return App.ajax.send({
-      name: 'admin.upgrade.upgrade_item',
-      sender: this,
-      data: {
-        upgradeId: item.get('request_id'),
-        groupId: item.get('group_id'),
-        stageId: item.get('stage_id')
-      },
-      success: 'getTasksSuccessCallback'
-    });
-  },
-
-  /**
-   * success callback of <code>getTasks</code>
-   * @param {object} data
-   */
-  getTasksSuccessCallback: function (data) {
-    this.get('controller.upgradeData.upgradeGroups').forEach(function (group) {
-      if (group.get('group_id') === data.UpgradeItem.group_id) {
-        group.get('upgradeItems').forEach(function (item) {
-          if (item.get('stage_id') === data.UpgradeItem.stage_id) {
-            if (item.get('tasks.length')) {
-              item.set('isTasksLoaded', true);
-              data.tasks.forEach(function (task) {
-                var currentTask = item.get('tasks').findProperty('id', task.Tasks.id);
-                this.get('taskDetailsProperties').forEach(function (property) {
-                  currentTask.set(property, task.Tasks[property]);
-                }, this);
-              }, this);
-            } else {
-              var tasks = [];
-              data.tasks.forEach(function (task) {
-                tasks.pushObject(App.upgradeEntity.create({type: 'TASK'}, task.Tasks));
-              });
-              item.set('tasks', tasks);
-            }
-            item.set('isTasksLoaded', true);
-          }
-        }, this);
-      }
-    }, this);
-  },
-
   willDestroyElement: function () {
     clearTimeout(this.get('timer'));
   }

+ 37 - 0
ambari-web/app/views/main/admin/stack_upgrade/upgrade_wizard_view.js

@@ -41,6 +41,13 @@ App.upgradeWizardView = Em.View.extend({
    */
   updateTimer: null,
 
+  /**
+   * update timer of Upgrade Item
+   * @type {number|null}
+   * @default null
+   */
+  upgradeItemTimer: null,
+
   /**
    * @type {boolean}
    */
@@ -209,6 +216,15 @@ App.upgradeWizardView = Em.View.extend({
     this.toggleProperty('isDetailsOpened');
   },
 
+  /**
+   * close details block if no active task present
+   */
+  closeDetails: function () {
+    if (this.get('noActiveItem')) {
+      this.set('isDetailsOpened', false);
+    }
+  }.observes('noActiveItem'),
+
   /**
    * start polling upgrade data
    */
@@ -234,6 +250,7 @@ App.upgradeWizardView = Em.View.extend({
    */
   willDestroyElement: function () {
     clearTimeout(this.get('updateTimer'));
+    clearTimeout(this.get('upgradeItemTimer'));
     this.set('isLoaded', false);
   },
 
@@ -249,6 +266,24 @@ App.upgradeWizardView = Em.View.extend({
     }, App.bgOperationsUpdateInterval));
   },
 
+  /**
+   * poll for tasks when item is expanded
+   */
+  doUpgradeItemPolling: function () {
+    var self = this;
+    var item = this.get('runningItem') || this.get('failedItem');
+
+    if (item && this.get('isDetailsOpened')) {
+      this.get('controller').getUpgradeItem(item).complete(function () {
+        self.set('upgradeItemTimer', setTimeout(function () {
+          self.doUpgradeItemPolling();
+        }, App.bgOperationsUpdateInterval));
+      });
+    } else {
+      clearTimeout(this.get('upgradeItemTimer'));
+    }
+  }.observes('isDetailsOpened'),
+
   /**
    * set current upgrade item state to FAILED (for HOLDING_FAILED) or TIMED_OUT (for HOLDING_TIMED_OUT)
    * in order to ignore fail and continue Upgrade
@@ -256,6 +291,7 @@ App.upgradeWizardView = Em.View.extend({
    */
   continue: function (event) {
     this.get('controller').setUpgradeItemStatus(event.context, event.context.get('status').slice(8));
+    this.set('isDetailsOpened', false);
   },
 
   /**
@@ -264,6 +300,7 @@ App.upgradeWizardView = Em.View.extend({
    */
   retry: function (event) {
     this.get('controller').setUpgradeItemStatus(event.context, 'PENDING');
+    this.set('isDetailsOpened', false);
   },
 
   /**

+ 27 - 0
ambari-web/test/controllers/main/admin/stack_and_upgrade_controller_test.js

@@ -185,6 +185,33 @@ describe('App.MainAdminStackAndUpgradeController', function() {
     });
   });
 
+  describe("#getUpgradeItem()", function() {
+    beforeEach(function () {
+      sinon.stub(App.ajax, 'send', Em.K);
+    });
+    afterEach(function () {
+      App.ajax.send.restore();
+    });
+    it("", function() {
+      var item = Em.Object.create({
+        request_id: 1,
+        group_id: 2,
+        stage_id: 3
+      });
+      controller.getUpgradeItem(item);
+      expect(App.ajax.send.getCall(0).args[0]).to.eql({
+        name: 'admin.upgrade.upgrade_item',
+        sender: controller,
+        data: {
+          upgradeId: 1,
+          groupId: 2,
+          stageId: 3
+        },
+        success: 'getUpgradeItemSuccessCallback'
+      });
+    });
+  });
+
   describe("#openUpgradeDialog()", function () {
     before(function () {
       sinon.stub(App.router, 'transitionTo', Em.K);

+ 56 - 5
ambari-web/test/views/main/admin/stack_upgrade/upgrade_wizard_view_test.js

@@ -28,7 +28,8 @@ describe('App.upgradeWizardView', function () {
     controller: Em.Object.create({
       upgradeData: Em.Object.create(),
       loadUpgradeData: Em.K,
-      setUpgradeItemStatus: Em.K
+      setUpgradeItemStatus: Em.K,
+      getUpgradeItem: Em.K
     })
   });
   view.removeObserver('App.clusterName', view, 'startPolling');
@@ -40,9 +41,9 @@ describe('App.upgradeWizardView', function () {
       expect(view.get('upgradeGroups')).to.be.empty;
     });
     it("upgradeGroups is valid", function () {
-      view.set('controller.upgradeData.upgradeGroups', [1]);
+      view.set('controller.upgradeData.upgradeGroups', [Em.Object.create()]);
       view.propertyDidChange('upgradeGroups');
-      expect(view.get('upgradeGroups')).to.eql([1]);
+      expect(view.get('upgradeGroups')).to.not.be.empty;
     });
   });
 
@@ -172,6 +173,7 @@ describe('App.upgradeWizardView', function () {
     it("", function () {
       view.continue({context: Em.Object.create({'status': 'HOLDING_FAILED'})});
       expect(view.get('controller').setUpgradeItemStatus.calledWith(Em.Object.create({'status': 'HOLDING_FAILED'}), 'FAILED')).to.be.true;
+      expect(view.get('isDetailsOpened')).to.be.false;
     });
   });
 
@@ -199,6 +201,7 @@ describe('App.upgradeWizardView', function () {
     it("", function () {
       view.retry({context: Em.Object.create({'status': 'FAILED'})});
       expect(view.get('controller').setUpgradeItemStatus.calledWith(Em.Object.create({'status': 'FAILED'}), 'PENDING')).to.be.true;
+      expect(view.get('isDetailsOpened')).to.be.false;
     });
   });
 
@@ -251,10 +254,10 @@ describe('App.upgradeWizardView', function () {
     });
     it("running item present", function () {
       view.set('activeGroup.upgradeItems', [
-        {status: 'IN_PROGRESS'}
+        Em.Object.create({status: 'IN_PROGRESS'})
       ]);
       view.propertyDidChange('runningItem');
-      expect(view.get('runningItem')).to.be.eql({status: 'IN_PROGRESS'});
+      expect(view.get('runningItem')).to.be.eql(Em.Object.create({status: 'IN_PROGRESS'}));
     });
   });
 
@@ -593,4 +596,52 @@ describe('App.upgradeWizardView', function () {
       });
   });
 
+  describe("#doUpgradeItemPolling()", function () {
+    beforeEach(function () {
+      sinon.stub(view.get('controller'), 'getUpgradeItem', function () {
+        return {
+          complete: function (callback) {
+            callback();
+          }
+        }
+      });
+      sinon.spy(view, 'doUpgradeItemPolling');
+      this.clock = sinon.useFakeTimers();
+    });
+    afterEach(function () {
+      view.get('controller').getUpgradeItem.restore();
+      view.doUpgradeItemPolling.restore();
+      this.clock.restore();
+    });
+    it("running item details", function () {
+      view.reopen({
+        runningItem: {},
+        failedItem: null
+      });
+      view.set('isDetailsOpened', true);
+      //doUpgradeItemPolling triggered by observer
+      expect(view.get('controller').getUpgradeItem.calledOnce).to.be.true;
+      this.clock.tick(App.bgOperationsUpdateInterval);
+      expect(view.doUpgradeItemPolling.calledTwice).to.be.true;
+    });
+    it("failed item details", function () {
+      view.reopen({
+        failedItem: {},
+        runningItem: null
+      });
+      view.set('isDetailsOpened', true);
+      view.doUpgradeItemPolling();
+      expect(view.get('controller').getUpgradeItem.calledOnce).to.be.true;
+      this.clock.tick(App.bgOperationsUpdateInterval);
+      expect(view.doUpgradeItemPolling.calledTwice).to.be.true;
+    });
+    it("details not opened", function () {
+      view.set('isDetailsOpened', false);
+      //doUpgradeItemPolling triggered by observer
+      expect(view.get('controller').getUpgradeItem.calledOnce).to.be.false;
+      this.clock.tick(App.bgOperationsUpdateInterval);
+      expect(view.doUpgradeItemPolling.calledOnce).to.be.true;
+    });
+  });
+
 });