فهرست منبع

AMBARI-8365 Unit tests for decommission mixins. (atkach)

Andrii Tkach 10 سال پیش
والد
کامیت
efcf07c7b0

+ 4 - 0
ambari-web/app/assets/test/tests.js

@@ -174,6 +174,10 @@ var files = ['test/init_model_test',
   'test/views/main/host/summary_test',
   'test/views/main/host/details/host_component_view_test',
   'test/views/main/host/details/host_component_views/decommissionable_test',
+  'test/views/main/host/details/host_component_views/datanode_view_test',
+  'test/views/main/host/details/host_component_views/nodemanager_view_test',
+  'test/views/main/host/details/host_component_views/regionserver_view_test',
+  'test/views/main/host/details/host_component_views/tasktracker_view_test',
   'test/views/main/charts/heatmap/heatmap_host_test',
   'test/views/main/service/item_test',
   'test/views/main/service/info/config_test',

+ 8 - 0
ambari-web/app/controllers/main/host/add_controller.js

@@ -78,6 +78,14 @@ App.AddHostController = App.WizardController.extend({
     return jQuery.extend({}, this.get('clusterStatusTemplate'), {name: App.router.getClusterName()});
   },
 
+  /**
+   * return new object extended from installOptionsTemplate
+   * @return Object
+   */
+  getInstallOptions: function () {
+    return jQuery.extend({}, this.get('installOptionsTemplate'));
+  },
+
   /**
    * Remove host from model. Used at <code>Confirm hosts</code> step
    * @param hosts Array of hosts, which we want to delete

+ 55 - 31
ambari-web/app/mixins/main/host/details/host_components/decommissionable.js

@@ -32,18 +32,6 @@ App.Decommissionable = Em.Mixin.create({
    */
   componentForCheckDecommission: '',
 
-  /**
-   * Received from server object with data about decommission
-   * @type {Object}
-   */
-  decommissionedStatusObject: null,
-
-  /**
-   * Received from server desired_admin_state value
-   * @type {String}
-   */
-  desiredAdminState: null,
-
   /**
    * Is component in decommission process right know
    * @type {bool}
@@ -144,6 +132,13 @@ App.Decommissionable = Em.Mixin.create({
       this.get('isDecommissioning');
   }.property('workStatus', 'isDecommissioning'),
 
+  /**
+   * load Recommission/Decommission status of component
+   */
+  loadComponentDecommissionStatus: function () {
+    return this.getDesiredAdminState();
+  },
+
   /**
    * Get desired_admin_state status from server
    */
@@ -161,27 +156,29 @@ App.Decommissionable = Em.Mixin.create({
   },
 
   /**
-   * Set received value or null to <code>desiredAdminState</code>
+   * pass received value or null to <code>setDesiredAdminState</code>
    * @param {Object} response
    * @returns {String|null}
    */
   getDesiredAdminStateSuccessCallback: function (response) {
     var status = response.HostRoles.desired_admin_state;
-    if ( status != null) {
-      this.set('desiredAdminState', status);
+    if (status != null) {
+      this.setDesiredAdminState(status);
       return status;
     }
     return null;
   },
 
   /**
-   * Set null to <code>desiredAdminState</code> if server returns error
-   * @returns {null}
+   * error callback of <code>getDesiredAdminState</code>
    */
-  getDesiredAdminStateErrorCallback: function () {
-    this.set('desiredAdminState', null);
-    return null;
-  },
+  getDesiredAdminStateErrorCallback: Em.K,
+
+  /**
+   * compute decommission state by desiredAdminState
+   * @param {Object} status
+   */
+  setDesiredAdminState: Em.K,
 
   /**
    * Get component decommission status from server
@@ -202,7 +199,7 @@ App.Decommissionable = Em.Mixin.create({
   },
 
   /**
-   * Set received value or null to <code>decommissionedStatusObject</code>
+   * pass received value or null to <code>setDecommissionStatus</code>
    * @param {Object} response
    * @returns {Object|null}
    */
@@ -210,7 +207,7 @@ App.Decommissionable = Em.Mixin.create({
     var statusObject = response.ServiceComponentInfo;
     if ( statusObject != null) {
       statusObject.component_state = response.host_components[0].HostRoles.state;
-      this.set('decommissionedStatusObject', statusObject);
+      this.setDecommissionStatus(statusObject);
       return statusObject;
     }
     return null;
@@ -220,9 +217,41 @@ App.Decommissionable = Em.Mixin.create({
    * Set null to <code>decommissionedStatusObject</code> if server returns error
    * @returns {null}
    */
-  getDecommissionStatusErrorCallback: function () {
-    this.set('decommissionedStatusObject', null);
-    return null;
+  getDecommissionStatusErrorCallback: Em.K,
+
+  /**
+   * compute decommission state by component info
+   * @param {Object} status
+   */
+  setDecommissionStatus: Em.K,
+
+  /**
+   * set decommission and recommission flags according to status
+   * @param status
+   */
+  setStatusAs: function (status) {
+    switch (status) {
+      case "INSERVICE":
+        this.set('isComponentRecommissionAvailable', false);
+        this.set('isComponentDecommissioning', false);
+        this.set('isComponentDecommissionAvailable', this.get('isStart'));
+        break;
+      case "DECOMMISSIONING":
+        this.set('isComponentRecommissionAvailable', true);
+        this.set('isComponentDecommissioning', true);
+        this.set('isComponentDecommissionAvailable', false);
+        break;
+      case "DECOMMISSIONED":
+        this.set('isComponentRecommissionAvailable', true);
+        this.set('isComponentDecommissioning', false);
+        this.set('isComponentDecommissionAvailable', false);
+        break;
+      case "RS_DECOMMISSIONED":
+        this.set('isComponentRecommissionAvailable', true);
+        this.set('isComponentDecommissioning', this.get('isStart'));
+        this.set('isComponentDecommissionAvailable', false);
+        break;
+    }
   },
 
   /**
@@ -256,11 +285,6 @@ App.Decommissionable = Em.Mixin.create({
     this.doBlinking();
   }.observes('workStatus','isComponentRecommissionAvailable', 'isDecommissioning'),
 
-  /**
-   * Should be redeclared in views that use this mixin
-   */
-  loadComponentDecommissionStatus: function() {},
-
   didInsertElement: function() {
     this._super();
     this.loadComponentDecommissionStatus();

+ 46 - 93
ambari-web/app/views/main/host/details/host_component_views/datanode_view.js

@@ -26,10 +26,10 @@ App.DataNodeComponentView = App.HostComponentView.extend(App.Decommissionable, {
    * Get component decommission status from server
    * @returns {$.ajax}
    */
-  getDNDecommissionStatus: function() {
+  getDNDecommissionStatus: function () {
     // always get datanode decommission statue from active namenode (if NN HA enabled)
     var hdfs = App.HDFSService.find().objectAt(0);
-    var activeNNHostName = (!hdfs.get('snameNode') && hdfs.get('activeNameNode')) ? hdfs.get('activeNameNode.hostName'): hdfs.get('nameNode.hostName');
+    var activeNNHostName = (!hdfs.get('snameNode') && hdfs.get('activeNameNode')) ? hdfs.get('activeNameNode.hostName') : hdfs.get('nameNode.hostName');
     return App.ajax.send({
       name: 'host.host_component.decommission_status_datanode',
       sender: this,
@@ -49,8 +49,8 @@ App.DataNodeComponentView = App.HostComponentView.extend(App.Decommissionable, {
    */
   getDNDecommissionStatusSuccessCallback: function (response) {
     var statusObject = Em.get(response, 'metrics.dfs.namenode');
-    if ( statusObject != null) {
-      this.set('decommissionedStatusObject', statusObject);
+    if (!Em.isNone(statusObject)) {
+      this.computeStatus(statusObject);
       return statusObject;
     }
     return null;
@@ -69,101 +69,54 @@ App.DataNodeComponentView = App.HostComponentView.extend(App.Decommissionable, {
    * load Recommission/Decommission status from adminState of each live node
    */
   loadComponentDecommissionStatus: function () {
+    return this.getDNDecommissionStatus();
+  },
+
+  setDesiredAdminState: function (desired_admin_state) {
+    this.setStatusAs(desired_admin_state);
+  },
+
+  /**
+   * compute and set decommission state by namenode metrics
+   * @param curObj
+   */
+  computeStatus: function (curObj) {
     var hostName = this.get('content.hostName');
-    var dfd = $.Deferred();
-    var self = this;
-    this.getDNDecommissionStatus().done(function () {
-      var curObj = self.get('decommissionedStatusObject');
-      self.set('decommissionedStatusObject', null);
+
+    if (curObj) {
+      var liveNodesJson = App.parseJSON(curObj.LiveNodes);
       // HDP-2 stack
       if (App.get('isHadoop2Stack')) {
-        if (curObj) {
-          var liveNodesJson = App.parseJSON(curObj.LiveNodes);
-          if (liveNodesJson && liveNodesJson[hostName] ) {
-            switch(liveNodesJson[hostName].adminState) {
-              case "In Service":
-                self.set('isComponentRecommissionAvailable', false);
-                self.set('isComponentDecommissioning', false);
-                self.set('isComponentDecommissionAvailable', self.get('isStart'));
-                break;
-              case "Decommission In Progress":
-                self.set('isComponentRecommissionAvailable', true);
-                self.set('isComponentDecommissioning', true);
-                self.set('isComponentDecommissionAvailable', false);
-                break;
-              case "Decommissioned":
-                self.set('isComponentRecommissionAvailable', true);
-                self.set('isComponentDecommissioning', false);
-                self.set('isComponentDecommissionAvailable', false);
-                break;
-            }
-          } else {
-            // if namenode is down, get desired_admin_state to decide if the user had issued a decommission
-            var deferred = $.Deferred();
-            self.getDesiredAdminState().done(function () {
-              var desired_admin_state = self.get('desiredAdminState');
-              self.set('desiredAdminState', null);
-              switch(desired_admin_state) {
-                case "INSERVICE":
-                  self.set('isComponentRecommissionAvailable', false);
-                  self.set('isComponentDecommissioning', false);
-                  self.set('isComponentDecommissionAvailable', self.get('isStart'));
-                  break;
-                case "DECOMMISSIONED":
-                  self.set('isComponentRecommissionAvailable', true);
-                  self.set('isComponentDecommissioning', false);
-                  self.set('isComponentDecommissionAvailable', false);
-                  break;
-              }
-              deferred.resolve(desired_admin_state);
-            });
+        if (liveNodesJson && liveNodesJson[hostName]) {
+          switch (liveNodesJson[hostName].adminState) {
+            case "In Service":
+              this.setStatusAs('INSERVICE');
+              break;
+            case "Decommission In Progress":
+              this.setStatusAs('DECOMMISSIONING');
+              break;
+            case "Decommissioned":
+              this.setStatusAs('DECOMMISSIONED');
+              break;
           }
+        } else {
+          // if namenode is down, get desired_admin_state to decide if the user had issued a decommission
+          this.getDesiredAdminState();
         }
-      }
-      else {
-        if (curObj) {
-          var liveNodesJson = App.parseJSON(curObj.LiveNodes);
-          var decomNodesJson = App.parseJSON(curObj.DecomNodes);
-          var deadNodesJson = App.parseJSON(curObj.DeadNodes);
-          if (decomNodesJson && decomNodesJson[hostName] ) {
-            self.set('isComponentRecommissionAvailable', true);
-            self.set('isComponentDecommissioning', true);
-            self.set('isComponentDecommissionAvailable', false);
-          } else if (deadNodesJson && deadNodesJson[hostName] ) {
-            self.set('isComponentRecommissionAvailable', true);
-            self.set('isComponentDecommissioning', false);
-            self.set('isComponentDecommissionAvailable', false);
-          } else if (liveNodesJson && liveNodesJson[hostName] ) {
-            self.set('isComponentRecommissionAvailable', false);
-            self.set('isComponentDecommissioning', false);
-            self.set('isComponentDecommissionAvailable', self.get('isStart'));
-          } else {
-            // if namenode is down, get desired_admin_state to decide if the user had issued a decommission
-            var deferred = $.Deferred();
-            self.getDesiredAdminState().done( function () {
-              var desired_admin_state = self.get('desiredAdminState');
-              self.set('desiredAdminState', null);
-              switch(desired_admin_state) {
-                case "INSERVICE":
-                  self.set('isComponentRecommissionAvailable', false);
-                  self.set('isComponentDecommissioning', false);
-                  self.set('isComponentDecommissionAvailable', self.get('isStart'));
-                  break;
-                case "DECOMMISSIONED":
-                  self.set('isComponentRecommissionAvailable', true);
-                  self.set('isComponentDecommissioning', false);
-                  self.set('isComponentDecommissionAvailable', false);
-                  break;
-              }
-              deferred.resolve(desired_admin_state);
-            });
-          }
+      } else {
+        var decomNodesJson = App.parseJSON(curObj.DecomNodes);
+        var deadNodesJson = App.parseJSON(curObj.DeadNodes);
+        if (decomNodesJson && decomNodesJson[hostName]) {
+          this.setStatusAs('DECOMMISSIONING');
+        } else if (deadNodesJson && deadNodesJson[hostName]) {
+          this.setStatusAs('DECOMMISSIONED');
+        } else if (liveNodesJson && liveNodesJson[hostName]) {
+          this.setStatusAs('INSERVICE');
+        } else {
+          // if namenode is down, get desired_admin_state to decide if the user had issued a decommission
+          this.getDesiredAdminState();
         }
       }
-      dfd.resolve(curObj);
-    });
-    return dfd.promise();
+    }
   }
-
-
 });

+ 31 - 49
ambari-web/app/views/main/host/details/host_component_views/nodemanager_view.js

@@ -22,58 +22,40 @@ App.NodeManagerComponentView = App.HostComponentView.extend(App.Decommissionable
 
   componentForCheckDecommission: 'RESOURCEMANAGER',
 
+  setDesiredAdminState: function (desiredAdminState) {
+    var self = this;
+    switch (desiredAdminState) {
+      case "INSERVICE":
+        // can be decommissioned if already started
+        this.setStatusAs(desiredAdminState);
+        break;
+      case "DECOMMISSIONED":
+        this.getDecommissionStatus();
+        break;
+    }
+  },
+
   /**
-   * load Recommission/Decommission status for nodeManager from nodeManagers list
+   * set decommission status according to component status and resource manager metrics
    */
-  loadComponentDecommissionStatus: function () {
-    var hostName = this.get('content.hostName');
-    var dfd = $.Deferred();
-    var self = this;
-    this.getDesiredAdminState().done( function () {
-      var desired_admin_state = self.get('desiredAdminState');
-      self.set('desiredAdminState', null);
-      switch(desired_admin_state) {
-        case "INSERVICE":
-          // can be decommissioned if already started
-          self.set('isComponentRecommissionAvailable', false);
-          self.set('isComponentDecommissioning', false);
-          self.set('isComponentDecommissionAvailable', self.get('isStart'));
-          break;
-        case "DECOMMISSIONED":
-          var deferred = $.Deferred();
-          self.getDecommissionStatus().done( function() {
-            var curObj = self.get('decommissionedStatusObject'),
-                rmComponent = self.get('content.service.hostComponents').findProperty('componentName', self.get('componentForCheckDecommission'));
+  setDecommissionStatus: function (curObj) {
+    var rmComponent = this.get('content.service.hostComponents').findProperty('componentName', this.get('componentForCheckDecommission')),
+        hostName = this.get('content.hostName');
 
-            rmComponent.set('workStatus', curObj.component_state);
-            self.set('decommissionedStatusObject', null);
-            if (curObj && curObj.rm_metrics) {
-              // Update RESOURCEMANAGER status
-              var nodeManagersArray = App.parseJSON(curObj.rm_metrics.cluster.nodeManagers);
-              if (nodeManagersArray.findProperty('HostName', hostName)){
-                // decommisioning ..
-                self.set('isComponentRecommissionAvailable', true);
-                self.set('isComponentDecommissioning', true);
-                self.set('isComponentDecommissionAvailable', false);
-              } else {
-                // decommissioned ..
-                self.set('isComponentRecommissionAvailable', true);
-                self.set('isComponentDecommissioning', false);
-                self.set('isComponentDecommissionAvailable', false);
-              }
-            } else {
-              // in this case ResourceManager not started. Set status to Decommissioned
-              self.set('isComponentRecommissionAvailable', true);
-              self.set('isComponentDecommissioning', false);
-              self.set('isComponentDecommissionAvailable', false);
-            }
-            deferred.resolve(curObj);
-          });
-          break;
+    rmComponent.set('workStatus', curObj.component_state);
+    if (curObj.rm_metrics) {
+      // Update RESOURCEMANAGER status
+      var nodeManagersArray = App.parseJSON(curObj.rm_metrics.cluster.nodeManagers);
+      if (nodeManagersArray.findProperty('HostName', hostName)) {
+        // decommisioning ..
+        this.setStatusAs('DECOMMISSIONING');
+      } else {
+        // decommissioned ..
+        this.setStatusAs('DECOMMISSIONED');
       }
-      dfd.resolve(desired_admin_state);
-    });
-    return dfd.promise();
+    } else {
+      // in this case ResourceManager not started. Set status to Decommissioned
+      this.setStatusAs('DECOMMISSIONED');
+    }
   }
-
 });

+ 10 - 27
ambari-web/app/views/main/host/details/host_component_views/regionserver_view.js

@@ -21,32 +21,15 @@ var App = require('app');
 App.RegionServerComponentView = App.HostComponentView.extend(App.Decommissionable, {
 
   componentForCheckDecommission: 'HBASE_MASTER',
-  /**
-   * load Recommission/Decommission status of RegionServer
-   */
-  loadComponentDecommissionStatus: function () {
-    var hostName = this.get('content.hostName');
-    var slaveType = 'HBASE_REGIONSERVER';
-    var self = this;
-    var deferred = $.Deferred();
-    self.getDesiredAdminState().done( function () {
-      var desired_admin_state = self.get('desiredAdminState');
-      self.set('desiredAdminState', null);
-      switch(desired_admin_state) {
-        case "INSERVICE":
-          self.set('isComponentRecommissionAvailable', false);
-          self.set('isComponentDecommissioning', false);
-          self.set('isComponentDecommissionAvailable', self.get('isStart'));
-          break;
-        case "DECOMMISSIONED":
-          self.set('isComponentRecommissionAvailable', true);
-          self.set('isComponentDecommissioning', self.get('isStart'));
-          self.set('isComponentDecommissionAvailable', false);
-          break;
-      }
-      deferred.resolve(desired_admin_state);
-    });
-    return deferred.promise();
-  }
 
+  setDesiredAdminState: function (desiredAdminState) {
+    switch (desiredAdminState) {
+      case "INSERVICE":
+        this.setStatusAs(desiredAdminState);
+        break;
+      case "DECOMMISSIONED":
+        this.setStatusAs("RS_DECOMMISSIONED");
+        break;
+    }
+  }
 });

+ 24 - 44
ambari-web/app/views/main/host/details/host_component_views/tasktracker_view.js

@@ -22,52 +22,32 @@ App.TaskTrackerComponentView = App.HostComponentView.extend(App.Decommissionable
 
   componentForCheckDecommission: 'JOBTRACKER',
 
-  /**
-   * load Recommission/Decommission status for TaskTracker from JobTracker/AliveNodes list
-   */
-  loadComponentDecommissionStatus: function () {
+  setDesiredAdminState: function (desiredAdminState) {
+    switch (desiredAdminState) {
+      case "INSERVICE":
+        // can be decommissioned if already started
+        this.setStatusAs('INSERVICE');
+        break;
+      case "DECOMMISSIONED":
+        this.getDecommissionStatus();
+        break;
+    }
+  },
+
+  setDecommissionStatus: function (curObj) {
     var hostName = this.get('content.hostName');
-    var dfd = $.Deferred();
-    var self = this;
-    this.getDesiredAdminState().done( function () {
-      var desired_admin_state = self.get('desiredAdminState');
-      self.set('desiredAdminState', null);
-      switch(desired_admin_state) {
-        case "INSERVICE":
-          // can be decommissioned if already started
-          self.set('isComponentRecommissionAvailable', false);
-          self.set('isComponentDecommissioning', false);
-          self.set('isComponentDecommissionAvailable', self.get('isStart'));
-          break;
-        case "DECOMMISSIONED":
-          var deferred = $.Deferred();
-          self.getDecommissionStatus().done( function() {
-            var curObj = self.get('decommissionedStatusObject');
-            self.set('decommissionedStatusObject', null);
-            if (curObj) {
-              var aliveNodesArray = App.parseJSON(curObj.AliveNodes);
-              if (aliveNodesArray != null) {
-                if (aliveNodesArray.findProperty('hostname', hostName)){
-                  //decommissioning ..
-                  self.set('isComponentRecommissionAvailable', true);
-                  self.set('isComponentDecommissioning', true);
-                  self.set('isComponentDecommissionAvailable', false);
-                } else {
-                  //decommissioned
-                  self.set('isComponentRecommissionAvailable', true);
-                  self.set('isComponentDecommissioning', false);
-                  self.set('isComponentDecommissionAvailable', false);
-                }
-              }
 
-            }
-            deferred.resolve(curObj);
-          });
-          break;
+    if (curObj) {
+      var aliveNodesArray = App.parseJSON(curObj.AliveNodes);
+      if (!Em.isNone(aliveNodesArray)) {
+        if (aliveNodesArray.findProperty('hostname', hostName)) {
+          //decommissioning ..
+          this.setStatusAs("DECOMMISSIONING");
+        } else {
+          //decommissioned
+          this.setStatusAs("DECOMMISSIONED");
+        }
       }
-      dfd.resolve(desired_admin_state);
-    });
-    return dfd.promise();
+    }
   }
-
 });

+ 3 - 1
ambari-web/test/controllers/main/host/add_controller_test.js

@@ -415,7 +415,7 @@ describe('App.AddHostController', function () {
   describe("#getInstallOptions()", function () {
     it("", function () {
       controller.set('installOptionsTemplate', {'prop': 'installOptionsTemplate'});
-      expect(controller.get('getInstallOptions')).to.be.eql({
+      expect(controller.getInstallOptions()).to.be.eql({
         prop: 'installOptionsTemplate'
       });
     });
@@ -1217,6 +1217,7 @@ describe('App.AddHostController', function () {
       sinon.stub(controller, 'loadMasterComponentHosts', Em.K);
       sinon.stub(controller, 'loadSlaveComponentHosts', Em.K);
       sinon.stub(controller, 'load', Em.K);
+      sinon.stub(controller, 'saveClusterStatus', Em.K);
     });
     afterEach(function () {
       this.mock.restore();
@@ -1227,6 +1228,7 @@ describe('App.AddHostController', function () {
       controller.loadMasterComponentHosts.restore();
       controller.loadSlaveComponentHosts.restore();
       controller.load.restore();
+      controller.saveClusterStatus.restore();
     });
     testCases.forEach(function (test) {
       it("current step - " + test.currentStep, function () {

+ 301 - 0
ambari-web/test/views/main/host/details/host_component_views/datanode_view_test.js

@@ -0,0 +1,301 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var App = require('app');
+require('views/main/host/details/host_component_views/datanode_view');
+
+describe('App.DataNodeComponentView', function () {
+  var view = App.DataNodeComponentView.create({
+    content: {
+      hostName: 'host1'
+    }
+  });
+
+  describe("#getDNDecommissionStatus()", function () {
+    beforeEach(function () {
+      this.stub = sinon.stub(App.HDFSService, 'find');
+      sinon.stub(App.ajax, 'send');
+    });
+    afterEach(function () {
+      App.ajax.send.restore();
+      this.stub.restore();
+    });
+    it("snameNode absent and no activeNameNode", function () {
+      this.stub.returns([
+        Em.Object.create({
+          snameNode: null,
+          activeNameNode: null,
+          nameNode: {hostName: 'host1'}
+        })
+      ]);
+      view.getDNDecommissionStatus();
+      expect(App.ajax.send.getCall(0).args[0].data).to.eql({
+        "hostName": "host1",
+        "componentName": "NAMENODE"
+      });
+    });
+    it("snameNode present and no activeNameNode", function () {
+      this.stub.returns([
+        Em.Object.create({
+          snameNode: {},
+          activeNameNode: null,
+          nameNode: {hostName: 'host1'}
+        })
+      ]);
+      view.getDNDecommissionStatus();
+      expect(App.ajax.send.getCall(0).args[0].data).to.eql({
+        "hostName": "host1",
+        "componentName": "NAMENODE"
+      });
+    });
+    it("snameNode absent and activeNameNode valid", function () {
+      this.stub.returns([
+        Em.Object.create({
+          snameNode: null,
+          activeNameNode: {hostName: 'host2'},
+          nameNode: {hostName: 'host1'}
+        })
+      ]);
+      view.getDNDecommissionStatus();
+      expect(App.ajax.send.getCall(0).args[0].data).to.eql({
+        "hostName": "host2",
+        "componentName": "NAMENODE"
+      });
+    });
+  });
+
+  describe("#getDNDecommissionStatusSuccessCallback()", function () {
+    beforeEach(function () {
+      sinon.stub(view, 'computeStatus', Em.K);
+      view.set('decommissionedStatusObject', null);
+    });
+    afterEach(function () {
+      view.computeStatus.restore();
+    });
+    it("metric null", function () {
+      var data = {
+        metrics: {
+          dfs: {
+            namenode: null
+          }
+        }
+      };
+      expect(view.getDNDecommissionStatusSuccessCallback(data)).to.equal(null);
+      expect(view.computeStatus.calledOnce).to.be.false;
+    });
+    it("metric valid", function () {
+      var data = {
+        metrics: {
+          dfs: {
+            namenode: "status"
+          }
+        }
+      };
+      expect(view.getDNDecommissionStatusSuccessCallback(data)).to.equal("status");
+      expect(view.computeStatus.calledOnce).to.be.true;
+    });
+  });
+
+  describe("#getDNDecommissionStatusErrorCallback()", function () {
+    it("reset to null", function () {
+      expect(view.getDNDecommissionStatusErrorCallback()).to.be.null;
+      expect(view.get('decommissionedStatusObject')).to.be.null;
+    });
+  });
+
+  describe("#loadComponentDecommissionStatus()", function () {
+    before(function () {
+      sinon.stub(view, 'getDNDecommissionStatus', Em.K);
+    });
+    after(function () {
+      view.getDNDecommissionStatus.restore();
+    });
+    it("call getDNDecommissionStatus()", function () {
+      view.loadComponentDecommissionStatus();
+      expect(view.getDNDecommissionStatus.calledOnce).to.be.true;
+    });
+  });
+
+  describe("#setDesiredAdminState()", function () {
+    before(function () {
+      sinon.stub(view, 'setStatusAs', Em.K);
+    });
+    after(function () {
+      view.setStatusAs.restore();
+    });
+    it("call getDNDecommissionStatus()", function () {
+      view.setDesiredAdminState('status');
+      expect(view.setStatusAs.calledWith('status')).to.be.true;
+    });
+  });
+
+  describe("#computeStatus()", function () {
+    beforeEach(function () {
+      sinon.stub(view, 'getDesiredAdminState', Em.K);
+      sinon.stub(view, 'setStatusAs', Em.K);
+      this.stub = sinon.stub(App, 'get');
+    });
+    afterEach(function () {
+      view.getDesiredAdminState.restore();
+      view.setStatusAs.restore();
+      this.stub.restore();
+    });
+    var testCases = [
+      {
+        title: 'No live nodes',
+        data: {
+          curObj: {},
+          isHadoop2Stack: true
+        },
+        result: {
+          getDesiredAdminStateCalled: true,
+          status: "",
+          setStatusAsCalled: false
+        }
+      },
+      {
+        title: 'Live nodes In Service',
+        data: {
+          "curObj": {
+            "LiveNodes": {
+              "host1": {
+                "adminState": "In Service"
+              }
+            }
+          },
+          isHadoop2Stack: true
+        },
+        result: {
+          getDesiredAdminStateCalled: false,
+          status: "INSERVICE",
+          setStatusAsCalled: true
+        }
+      },
+      {
+        title: 'Live nodes In Progress',
+        data: {
+          "curObj": {
+            "LiveNodes": {
+              "host1": {
+                "adminState": "Decommission In Progress"
+              }
+            }
+          },
+          isHadoop2Stack: true
+        },
+        result: {
+          getDesiredAdminStateCalled: false,
+          status: "DECOMMISSIONING",
+          setStatusAsCalled: true
+        }
+      },
+      {
+        title: 'Live nodes Decommissioned',
+        data: {
+          "curObj": {
+            "LiveNodes": {
+              "host1": {
+                "adminState": "Decommissioned"
+              }
+            }
+          },
+          isHadoop2Stack: true
+        },
+        result: {
+          getDesiredAdminStateCalled: false,
+          status: "DECOMMISSIONED",
+          setStatusAsCalled: true
+        }
+      },
+      {
+        title: 'nodes DECOMMISSIONING',
+        data: {
+          "curObj": {
+            "DecomNodes": {
+              "host1": {}
+            }
+          },
+          isHadoop2Stack: false
+        },
+        result: {
+          getDesiredAdminStateCalled: false,
+          status: "DECOMMISSIONING",
+          setStatusAsCalled: true
+        }
+      },
+      {
+        title: 'nodes DECOMMISSIONED',
+        data: {
+          "curObj": {
+            "DeadNodes": {
+              "host1": {}
+            }
+          },
+          isHadoop2Stack: false
+        },
+        result: {
+          getDesiredAdminStateCalled: false,
+          status: "DECOMMISSIONED",
+          setStatusAsCalled: true
+        }
+      },
+      {
+        title: 'nodes INSERVICE',
+        data: {
+          "curObj": {
+            "LiveNodes": {
+              "host1": {}
+            }
+          },
+          isHadoop2Stack: false
+        },
+        result: {
+          getDesiredAdminStateCalled: false,
+          status: "INSERVICE",
+          setStatusAsCalled: true
+        }
+      },
+      {
+        title: 'namenode down',
+        data: {
+          "curObj": {},
+          isHadoop2Stack: false
+        },
+        result: {
+          getDesiredAdminStateCalled: true,
+          status: "",
+          setStatusAsCalled: false
+        }
+      }
+    ];
+    testCases.forEach(function (test) {
+      it(test.title + ", isHadoop2Stack:" + test.data.isHadoop2Stack, function () {
+        this.stub.withArgs('isHadoop2Stack').returns(test.data.isHadoop2Stack);
+        view.computeStatus(test.data.curObj);
+        expect(view.getDesiredAdminState.called).to.equal(test.result.getDesiredAdminStateCalled);
+        expect(view.setStatusAs.calledWith(test.result.status)).to.equal(test.result.setStatusAsCalled);
+      });
+    }, this);
+    it("data is null", function () {
+      view.computeStatus(null);
+      expect(view.getDesiredAdminState.called).to.be.false;
+      expect(view.setStatusAs.called).to.be.false;
+    });
+  });
+
+});

+ 87 - 0
ambari-web/test/views/main/host/details/host_component_views/nodemanager_view_test.js

@@ -0,0 +1,87 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var App = require('app');
+require('views/main/host/details/host_component_views/nodemanager_view');
+
+describe('App.NodeManagerComponentView', function () {
+  var view = App.NodeManagerComponentView.create({
+    content: {
+      service: {
+        hostComponents: [
+          Em.Object.create({
+            componentName: 'RESOURCEMANAGER'
+          })
+        ]
+      },
+      hostName: 'host1'
+    }
+  });
+
+  describe("#setDesiredAdminState()", function () {
+    beforeEach(function () {
+      sinon.stub(view, 'setStatusAs', Em.K);
+      sinon.stub(view, 'getDecommissionStatus', Em.K);
+    });
+    afterEach(function () {
+      view.setStatusAs.restore();
+      view.getDecommissionStatus.restore();
+    });
+    it("state INSERVICE", function () {
+      view.setDesiredAdminState('INSERVICE');
+      expect(view.setStatusAs.calledWith('INSERVICE')).to.be.true;
+    });
+    it("state DECOMMISSIONED", function () {
+      view.setDesiredAdminState('DECOMMISSIONED');
+      expect(view.getDecommissionStatus.calledOnce).to.be.true;
+    });
+  });
+
+  describe("#setDecommissionStatus()", function () {
+    beforeEach(function () {
+      sinon.stub(view, 'setStatusAs', Em.K);
+    });
+    afterEach(function () {
+      view.setStatusAs.restore();
+    });
+    it("rm_metrics null", function () {
+      view.setDecommissionStatus({rm_metrics: null});
+      expect(view.setStatusAs.calledWith('DECOMMISSIONED')).to.be.true;
+    });
+    it("RM contains host", function () {
+      var curObj =  {rm_metrics: {
+        cluster: {
+          nodeManagers: [{
+            HostName: 'host1'
+          }]
+        }
+      }};
+      view.setDecommissionStatus(curObj);
+      expect(view.setStatusAs.calledWith('DECOMMISSIONING')).to.be.true;
+    });
+    it("RM does not contain host", function () {
+      var curObj =  {rm_metrics: {
+        cluster: {
+          nodeManagers: []
+        }
+      }};
+      view.setDecommissionStatus(curObj);
+      expect(view.setStatusAs.calledWith('DECOMMISSIONED')).to.be.true;
+    });
+  });
+});

+ 41 - 0
ambari-web/test/views/main/host/details/host_component_views/regionserver_view_test.js

@@ -0,0 +1,41 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var App = require('app');
+require('views/main/host/details/host_component_views/regionserver_view');
+
+describe('App.RegionServerComponentView', function () {
+  var view = App.RegionServerComponentView.create();
+
+  describe("#setDesiredAdminState()", function () {
+    beforeEach(function () {
+      sinon.stub(view, 'setStatusAs', Em.K);
+    });
+    afterEach(function () {
+      view.setStatusAs.restore();
+    });
+    it("INSERVICE state)", function () {
+      view.setDesiredAdminState('INSERVICE');
+      expect(view.setStatusAs.calledWith('INSERVICE')).to.be.true;
+    });
+    it("DECOMMISSIONED state)", function () {
+      view.setDesiredAdminState('DECOMMISSIONED');
+      expect(view.setStatusAs.calledWith('RS_DECOMMISSIONED')).to.be.true;
+    });
+  });
+});

+ 76 - 0
ambari-web/test/views/main/host/details/host_component_views/tasktracker_view_test.js

@@ -0,0 +1,76 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var App = require('app');
+require('views/main/host/details/host_component_views/tasktracker_view');
+
+describe('App.TaskTrackerComponentView', function () {
+  var view = App.TaskTrackerComponentView.create({
+    content: {
+      hostName: 'host1'
+    }
+  });
+
+  describe("#setDesiredAdminState()", function () {
+    beforeEach(function () {
+      sinon.stub(view, 'setStatusAs', Em.K);
+      sinon.stub(view, 'getDecommissionStatus', Em.K);
+    });
+    afterEach(function () {
+      view.setStatusAs.restore();
+      view.getDecommissionStatus.restore();
+    });
+    it("INSERVICE state)", function () {
+      view.setDesiredAdminState('INSERVICE');
+      expect(view.setStatusAs.calledWith('INSERVICE')).to.be.true;
+    });
+    it("DECOMMISSIONED state)", function () {
+      view.setDesiredAdminState('DECOMMISSIONED');
+      expect(view.getDecommissionStatus.calledOnce).to.be.true;
+    });
+  });
+
+  describe("#setDecommissionStatus()", function() {
+    beforeEach(function () {
+      sinon.stub(view, 'setStatusAs', Em.K);
+    });
+    afterEach(function () {
+      view.setStatusAs.restore();
+    });
+    it("data is null", function() {
+      view.setDecommissionStatus(null);
+      expect(view.setStatusAs.called).to.be.false;
+    });
+    it("AliveNodes is null", function() {
+      view.setDecommissionStatus({"AliveNodes": null});
+      expect(view.setStatusAs.called).to.be.false;
+    });
+    it("AliveNodes contain current host", function() {
+      view.setDecommissionStatus({"AliveNodes": [
+        {
+          "hostname": "host1"
+        }
+      ]});
+      expect(view.setStatusAs.calledWith("DECOMMISSIONING")).to.be.true;
+    });
+    it("AliveNodes does not contain current host", function() {
+      view.setDecommissionStatus({"AliveNodes": []});
+      expect(view.setStatusAs.calledWith('DECOMMISSIONED')).to.be.true;
+    });
+  });
+});

+ 26 - 7
ambari-web/test/views/main/host/details_test.js

@@ -153,16 +153,35 @@ describe('App.MainHostDetailsView', function () {
   });
 
   describe('#didInsertElement()', function () {
-    it('isLoaded should be set as true', function () {
+    beforeEach(function () {
+      sinon.stub(App.router, 'get', function () {
+        return {
+          updateHost: function (callback) {
+            callback();
+          }
+        }
+      });
+      sinon.stub(App.router, 'transitionTo', Em.K);
+      sinon.stub(App, 'tooltip', Em.K);
+    });
+    afterEach(function () {
+      App.router.transitionTo.restore();
+      App.router.get.restore();
+      App.tooltip.restore();
+    });
+    it('host loaded', function () {
+      view.set('content.isLoaded', true);
       view.didInsertElement();
-      expect(view.get('isLoaded')).to.be.true;
+      expect(App.router.get.calledWith('updateController')).to.be.true;
+      expect(App.tooltip.calledOnce).to.be.true;
+      expect(App.router.transitionTo.calledWith('main.hosts.index')).to.be.false;
     });
-    it('router should reditect to main.hosts.index', function () {
-      App.router.set('mainHostDetailsController.content', {
-        isLoaded: false
-      });
+    it('host is not loaded', function () {
+      view.set('content.isLoaded', false);
       view.didInsertElement();
-      expect(App.router.get('currentState.name')).to.equal('index');
+      expect(App.router.get.calledWith('updateController')).to.be.true;
+      expect(App.tooltip.calledOnce).to.be.true;
+      expect(App.router.transitionTo.calledWith('main.hosts.index')).to.be.true;
     });
   });
 });