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

[2/2] git commit: AMBARI-5575. Unit tests for steps 9 (with refactor). (onechiporenko)

AMBARI-5575. Unit tests for steps 9 (with refactor). (onechiporenko)


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

Branch: refs/heads/trunk
Commit: b96edcf90d7d2e609aa5e2e4eb9024c8c467c000
Parents: 739d35e
Author: Oleg Nechiporenko <on...@apache.org>
Authored: Fri Apr 25 19:28:30 2014 +0300
Committer: Oleg Nechiporenko <on...@apache.org>
Committed: Fri Apr 25 19:28:30 2014 +0300

----------------------------------------------------------------------
 ambari-web/app/assets/test/tests.js             |   1 +
 .../app/controllers/wizard/step9_controller.js  | 123 +--
 ambari-web/app/templates/wizard/step9.hbs       |  71 +-
 .../wizard/step9/step9HostTasksLogPopup.hbs     |  90 ++
 .../wizard/step9/step9_install_host_popup.hbs   |  40 +
 .../templates/wizard/step9HostTasksLogPopup.hbs |  90 --
 .../wizard/step9_install_host_popup.hbs         |  40 -
 ambari-web/app/views.js                         |   1 +
 .../views/wizard/step9/hostLogPopupBody_view.js | 264 ++++++
 ambari-web/app/views/wizard/step9_view.js       | 389 ++++-----
 ambari-web/test/installer/step9_test.js         | 852 ++++++++++++++++++-
 .../wizard/step9/hostLogPopupBody_view_test.js  | 145 ++++
 ambari-web/test/views/wizard/step9_view_test.js | 387 ++++++++-
 13 files changed, 1997 insertions(+), 496 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/b96edcf9/ambari-web/app/assets/test/tests.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/assets/test/tests.js b/ambari-web/app/assets/test/tests.js
index 6175b3f..1ba5922 100644
--- a/ambari-web/app/assets/test/tests.js
+++ b/ambari-web/app/assets/test/tests.js
@@ -140,6 +140,7 @@ require('test/views/wizard/step3_view_test');
 require('test/views/wizard/step5_view_test');
 require('test/views/wizard/step6_view_test');
 require('test/views/wizard/step9_view_test');
+require('test/views/wizard/step9/hostLogPopupBody_view_test');
 require('test/views/wizard/step10_view_test');
 require('test/models/host_test');
 require('test/models/host_component_test');

http://git-wip-us.apache.org/repos/asf/ambari/blob/b96edcf9/ambari-web/app/controllers/wizard/step9_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/wizard/step9_controller.js b/ambari-web/app/controllers/wizard/step9_controller.js
index 56f97d1..8494a87 100644
--- a/ambari-web/app/controllers/wizard/step9_controller.js
+++ b/ambari-web/app/controllers/wizard/step9_controller.js
@@ -36,17 +36,16 @@ App.WizardStep9Controller = Em.Controller.extend({
    *  </code>
    *  @type {Array.<{name: string, status: string, logTasks: object[], message: string, progress: number, isNoTasksForInstall: bool}>}
    */
-
   hosts: [],
 
   /**
-   * overall progress of <Install,Start and Test> page shown as progress bar on the top of the page
+   * Overall progress of <Install,Start and Test> page shown as progress bar on the top of the page
    * @type {string}
    */
   progress: '0',
 
   /*
-   * json file for the mock data to be used in mock mode
+   * Json file for the mock data to be used in mock mode
    * @type {string}
    */
   mockDataPrefix: '/data/wizard/deploy/5_hosts',
@@ -119,10 +118,12 @@ App.WizardStep9Controller = Em.Controller.extend({
   togglePreviousSteps: function () {
     if (App.testMode) {
       return;
-    } else if ('INSTALL FAILED' === this.get('content.cluster.status') && this.get('content.controllerName') == 'installerController') {
-      App.router.get('installerController').setStepsEnable();
+    }
+    var installerController = App.router.get('installerController');
+    if ('INSTALL FAILED' === this.get('content.cluster.status') && this.get('content.controllerName') == 'installerController') {
+      installerController.setStepsEnable();
     } else {
-      App.router.get('installerController').setLowerStepsDisable(9);
+      installerController.setLowerStepsDisable(9);
     }
   }.observes('content.cluster.status', 'content.controllerName'),
 
@@ -142,7 +143,6 @@ App.WizardStep9Controller = Em.Controller.extend({
     Em.run.once(this, 'updateStatus');
   }.observes('hosts.@each.status'),
 
-
   /**
    * A flag that gets set with installation failure.
    * @type {bool}
@@ -150,6 +150,24 @@ App.WizardStep9Controller = Em.Controller.extend({
   installFailed: false,
 
   /**
+   * Incremental flag that triggers an event in step 9 view to change the tasks related data and icons of hosts.
+   * @type {number}
+   */
+  logTasksChangesCounter: 0,
+
+  /**
+   * <code>taskId</code> of current open task
+   * @type {number}
+   */
+  currentOpenTaskId: 0,
+
+  /**
+   * <code>requestId</code> of current open task
+   * @type {number}
+   */
+  currentOpenTaskRequestId: 0,
+
+  /**
    * Observer function: Updates {status} field of the controller.
    * @method updateStatus
    */
@@ -172,24 +190,6 @@ App.WizardStep9Controller = Em.Controller.extend({
   }.observes('progress'),
 
   /**
-   * Incremental flag that triggers an event in step 9 view to change the tasks related data and icons of hosts.
-   * @type {number}
-   */
-  logTasksChangesCounter: 0,
-
-  /**
-   * <code>taskId</code> of current open task
-   * @type {number}
-   */
-  currentOpenTaskId: 0,
-
-  /**
-   * <code>requestId</code> of current open task
-   * @type {number}
-   */
-  currentOpenTaskRequestId: 0,
-
-  /**
    * This function is called when a click event happens on Next button of "Install, Start and Test" page
    * @method submit
    */
@@ -207,7 +207,6 @@ App.WizardStep9Controller = Em.Controller.extend({
     }
   },
 
-
   /**
    * navigateStep is called by App.WizardStep9View's didInsertElement and "retry" from router.
    * content.cluster.status can be:
@@ -283,7 +282,6 @@ App.WizardStep9Controller = Em.Controller.extend({
     this.loadHosts();
   },
 
-
   /**
    * Reset status and message of all hosts when retry install
    * @method resetHostsForRetry
@@ -339,6 +337,7 @@ App.WizardStep9Controller = Em.Controller.extend({
    */
   displayMessage: function (task) {
     var role = App.format.role(task.role);
+    /* istanbul ignore next */
     switch (task.command) {
       case 'INSTALL':
         switch (task.status) {
@@ -431,6 +430,7 @@ App.WizardStep9Controller = Em.Controller.extend({
   /**
    * Run start/check services after installation phase.
    * Does Ajax call to start all services
+   * @return {$.ajax|null}
    * @method launchStartServices
    */
   launchStartServices: function () {
@@ -465,7 +465,7 @@ App.WizardStep9Controller = Em.Controller.extend({
       this.set('numPolls', 6);
     }
 
-    App.ajax.send({
+    return App.ajax.send({
       name: name,
       sender: this,
       data: {
@@ -497,7 +497,8 @@ App.WizardStep9Controller = Em.Controller.extend({
       };
       this.hostHasClientsOnly(false);
       this.saveClusterStatus(clusterStatus);
-    } else {
+    }
+    else {
       console.log('ERROR: Error occurred in parsing JSON data');
       this.hostHasClientsOnly(true);
       clusterStatus = {
@@ -759,7 +760,8 @@ App.WizardStep9Controller = Em.Controller.extend({
         clusterStatus.status = 'STARTED';
         var serviceStartTime = App.dateTime();
         clusterStatus.installTime = ((parseInt(serviceStartTime) - parseInt(this.get('content.cluster.installStartTime'))) / 60000).toFixed(2);
-      } else {
+      }
+      else {
         clusterStatus.status = 'START FAILED'; // 'START FAILED' implies to step10 that installation was successful but start failed
       }
       this.saveClusterStatus(clusterStatus);
@@ -835,9 +837,6 @@ App.WizardStep9Controller = Em.Controller.extend({
     var totalProgress = 0;
     var tasksData = polledData.tasks;
     console.log("The value of tasksData is: ", tasksData);
-    if (!tasksData) {
-      console.log("Step9: ERROR: NO tasks available to process");
-    }
     var requestId = this.get('content.cluster.requestId');
     tasksData.setEach('Tasks.request_id', requestId);
     if (polledData.Requests && polledData.Requests.id && polledData.Requests.id != requestId) {
@@ -947,10 +946,13 @@ App.WizardStep9Controller = Em.Controller.extend({
   loadCurrentTaskLogSuccessCallback: function (data) {
     var taskId = this.get('currentOpenTaskId');
     if (taskId) {
-      var currentTask = this.get('hosts').findProperty('name', data.Tasks.host_name).get('logTasks').findProperty('Tasks.id', data.Tasks.id);
-      if (currentTask) {
-        currentTask.Tasks.stderr = data.Tasks.stderr;
-        currentTask.Tasks.stdout = data.Tasks.stdout;
+      var host = this.get('hosts').findProperty('name', data.Tasks.host_name);
+      if (host) {
+        var currentTask = host.get('logTasks').findProperty('Tasks.id', data.Tasks.id);
+        if (currentTask) {
+          currentTask.Tasks.stderr = data.Tasks.stderr;
+          currentTask.Tasks.stdout = data.Tasks.stdout;
+        }
       }
     }
     this.set('logTasksChangesCounter', this.get('logTasksChangesCounter') + 1);
@@ -969,9 +971,10 @@ App.WizardStep9Controller = Em.Controller.extend({
    * @param {bool} polling whether to continue polling for status or not
    * @param {number} requestId
    * @method getLogsByRequest
+   * @return {$.ajax|null}
    */
   getLogsByRequest: function (polling, requestId) {
-    App.ajax.send({
+    return App.ajax.send({
       name: 'wizard.step9.load_log',
       sender: this,
       data: {
@@ -1015,22 +1018,15 @@ App.WizardStep9Controller = Em.Controller.extend({
           self.loadCurrentTaskLog();
         }
         self.doPolling();
-      }, this.POLL_INTERVAL);
+      }, this.get('POLL_INTERVAL'));
     }
   },
 
   /**
    * Error-callback for get log by request
-   * @param {object} request
-   * @param {object} ajaxOptions
-   * @param {string} error
    * @method getLogsByRequestErrorCallback
    */
-  getLogsByRequestErrorCallback: function (request, ajaxOptions, error) {
-    console.log("TRACE: STep9 -> In error function for the GET logs data");
-    console.log("TRACE: STep9 -> value of the url is: " + url);
-    console.log("TRACE: STep9 -> error code status is: " + request.status);
-  },
+  getLogsByRequestErrorCallback: Em.K,
 
   /**
    * Delegates the function call to {getLogsByRequest} with appropriate params
@@ -1047,13 +1043,14 @@ App.WizardStep9Controller = Em.Controller.extend({
   /**
    * Check that all components are in INSTALLED state before issuing start command
    * @method isAllComponentsInstalled
+   * @return {$.ajax|null}
    */
   isAllComponentsInstalled: function () {
     if (this.get('content.controllerName') !== 'installerController') {
-      return;
+      return null;
     }
     var name = 'wizard.step9.installer.get_host_status';
-    App.ajax.send({
+    return App.ajax.send({
       name: name,
       sender: this,
       data: {
@@ -1109,7 +1106,6 @@ App.WizardStep9Controller = Em.Controller.extend({
    * @method isAllComponentsInstalledErrorCallback
    */
   isAllComponentsInstalledErrorCallback: function () {
-    console.log("ERROR");
     var clusterStatus = {
       status: 'INSTALL FAILED',
       isStartError: true,
@@ -1134,18 +1130,22 @@ App.WizardStep9Controller = Em.Controller.extend({
   getComponentMessage: function (componentArr) {
     var label = '';
     componentArr.forEach(function (_component) {
-      if (_component === componentArr[0]) {
+      if (componentArr.length === 1) {
         label = _component;
-      } else if (_component !== componentArr[componentArr.length - 1]) {           // [clients.length - 1]
-        label = label + ' ' + _component;
-        if (_component !== componentArr[componentArr.length - 2]) {
-          label = label + ',';
+      }
+      else {
+        if (_component !== componentArr[componentArr.length - 1]) {           // [clients.length - 1]
+          label = label + ' ' + _component;
+          if (_component !== componentArr[componentArr.length - 2]) {
+            label = label + ',';
+          }
+        }
+        else {
+          label = label + ' ' + Em.I18n.t('and') + ' ' + _component;
         }
-      } else {
-        label = label + ' ' + Em.I18n.t('and') + ' ' + _component;
       }
     }, this);
-    return label;
+    return label.trim();
   },
 
   /**
@@ -1156,7 +1156,8 @@ App.WizardStep9Controller = Em.Controller.extend({
   saveClusterStatus: function (clusterStatus) {
     if (!App.testMode) {
       App.router.get(this.get('content.controllerName')).saveClusterStatus(clusterStatus);
-    } else {
+    }
+    else {
       this.set('content.cluster', clusterStatus);
     }
   },
@@ -1168,8 +1169,8 @@ App.WizardStep9Controller = Em.Controller.extend({
    */
   saveInstalledHosts: function (context) {
     if (!App.testMode) {
-      App.router.get(this.get('content.controllerName')).saveInstalledHosts(context)
+      App.router.get(this.get('content.controllerName')).saveInstalledHosts(context);
     }
   }
 
-});
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/b96edcf9/ambari-web/app/templates/wizard/step9.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/wizard/step9.hbs b/ambari-web/app/templates/wizard/step9.hbs
index 988aa32..307609e 100644
--- a/ambari-web/app/templates/wizard/step9.hbs
+++ b/ambari-web/app/templates/wizard/step9.hbs
@@ -21,6 +21,7 @@
   <h2>{{t installer.step9.header}}</h2>
 
   <p class="alert alert-info">{{t installer.step9.body}}</p>
+
   <div id="overallProgress">
     <div class="row-fluid">
       <div class="span10">
@@ -37,10 +38,10 @@
     <div class="box-header">
       <div class="pull-left">
         {{#if controller.showRetry}}
-        <a class="btn btn-primary"
-           href="#" {{action retry}}><i class="icon-repeat icon-white"></i>
-          {{t common.retry}}
-        </a>
+          <a class="btn btn-primary" href="#" {{action retry}}>
+            <i class="icon-repeat icon-white"></i>
+            {{t common.retry}}
+          </a>
         {{/if}}
       </div>
       <!-- filter by host level -->
@@ -75,9 +76,9 @@
         </thead>
 
         <tbody>
-        {{#if view.pageContent}}
-          {{#each host in view.pageContent}}
-            {{#view App.HostStatusView objBinding="host" controllerBinding="controller"}}
+          {{#if view.pageContent}}
+            {{#each host in view.pageContent}}
+              {{#view App.HostStatusView objBinding="host" controllerBinding="controller"}}
               <td>
                 {{host.name}}
               </td>
@@ -91,50 +92,52 @@
               </td>
               <td>
                 <a {{bindAttr class="view.isFailed:text-error view.isSuccess:text-success view.isWarning:text-warning"}}
-                  href="javascript:void(null)"
-                  data-toggle="modal" {{action hostLogPopup target="view"}}>{{host.message}}</a>
+                    href="javascript:void(null)"
+                    data-toggle="modal" {{action hostLogPopup target="view"}}>{{host.message}}</a>
               </td>
-            {{/view}}
-          {{/each}}
-        {{else}}
+              {{/view}}
+            {{/each}}
+          {{else}}
           <tr>
             <td colspan="3"><p>{{t installer.step3.hosts.noHosts}}</p></td>
           </tr>
-        {{/if}}
+          {{/if}}
         </tbody>
       </table>
     </div>
-      <div id="hosts">
-        <div class="page-bar">
-          <div class="selected-hosts-info pull-left">
-            {{view.filteredHostsInfo}}
-            -
-            <a {{action showAllHosts target="view"}} href="#">{{t tableView.filters.showAll}}</a>
-          </div>
-          <div class="items-on-page">
-              <label>{{t common.show}}: {{view view.rowsPerPageSelectView selectionBinding="view.displayLength"}}</label>
-          </div>
-          <div class="info">{{view.paginationInfo}}</div>
-          <div class="paging_two_button">
-            {{view view.paginationFirst}}
-            {{view view.paginationLeft}}
-            {{view view.paginationRight}}
-            {{view view.paginationLast}}
-          </div>
+    <div id="hosts">
+      <div class="page-bar">
+        <div class="selected-hosts-info pull-left">
+          {{view.filteredHostsInfo}}
+          -
+          <a {{action showAllHosts target="view"}} href="#">{{t tableView.filters.showAll}}</a>
+        </div>
+        <div class="items-on-page">
+          <label>{{t common.show}}: {{view view.rowsPerPageSelectView selectionBinding="view.displayLength"}}</label>
+        </div>
+        <div class="info">{{view.paginationInfo}}</div>
+        <div class="paging_two_button">
+          {{view view.paginationFirst}}
+          {{view view.paginationLeft}}
+          {{view view.paginationRight}}
+          {{view view.paginationLast}}
         </div>
       </div>
+    </div>
   </div>
 
   <div>
     {{#if view.resultMsg}}
       <p {{bindAttr class="view.resultMsgColor :alert"}}>{{view.resultMsg}}
-      {{#if view.isHostHeartbeatLost}}
-        <a href="javascript:void(null)" data-toggle="modal" {{action hostWithInstallFailed target="view"}}>{{t common.showDetails}}</a>
-      {{/if}}
+        {{#if view.isHostHeartbeatLost}}
+          <a href="javascript:void(null)"
+             data-toggle="modal" {{action hostWithInstallFailed target="view"}}>{{t common.showDetails}}</a>
+        {{/if}}
       </p>
     {{/if}}
     <div class="btn-area">
-      <button class="btn btn-success pull-right" {{bindAttr disabled="isSubmitDisabled"}} {{action submit target="controller"}}>{{t common.next}} &rarr;</button>
+      <button
+          class="btn btn-success pull-right" {{bindAttr disabled="isSubmitDisabled"}} {{action submit target="controller"}}>{{t common.next}} &rarr;</button>
     </div>
   </div>
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/b96edcf9/ambari-web/app/templates/wizard/step9/step9HostTasksLogPopup.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/wizard/step9/step9HostTasksLogPopup.hbs b/ambari-web/app/templates/wizard/step9/step9HostTasksLogPopup.hbs
new file mode 100644
index 0000000..2671bbb
--- /dev/null
+++ b/ambari-web/app/templates/wizard/step9/step9HostTasksLogPopup.hbs
@@ -0,0 +1,90 @@
+{{!
+* 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.
+}}
+
+{{#if view.isHeartbeatLost}}
+  <div>
+    {{t installer.step9.host.heartbeat_lost_popup}}
+  </div>
+{{else}}
+  {{#if view.isNoTasksScheduled}}
+    {{t installer.step9.host.status.noTasks}}
+  {{else}}
+    <div {{bindAttr class="view.isLogWrapHidden::hidden :task-list-main-warp"}}>
+      <div class="task-top-wrap">
+        <div class="task-name-top task-detail-log-rolename">{{t common.tasks}}</div>
+        <div class="select-wrap">
+          {{t common.show}}:
+          {{view Ember.Select
+          contentBinding="view.categories"
+          optionValuePath="content.value"
+          optionLabelPath="content.label"
+          selectionBinding="view.category"
+          }}
+        </div>
+      </div>
+      <div id="host-log">
+        {{#each taskInfo in view.tasks}}
+          <div {{bindAttr class="taskInfo.isVisible::hidden :log-list-wrap"}}>
+            <div {{action toggleTaskLog taskInfo}} class="task-list-line-cursor">
+              <div class="operation-name-icon-wrap">
+                <i {{bindAttr class="taskInfo.status taskInfo.icon"}}></i>
+                <a href="#">
+                  {{taskInfo.role}} {{taskInfo.command}}
+                </a>
+              </div>
+              <div class="show-details"><i class="icon-caret-right"></i></div>
+            </div>
+          </div>
+        {{/each}}
+        {{#if view.isEmptyList}}
+          <div class="log-list-wrap">{{t installer.step9.hostLog.popup.noTasksToShow}}</div>
+        {{/if}}
+      </div>
+    </div>
+
+    <div {{bindAttr class="view.isLogWrapHidden:hidden :task-detail-info"}}>
+      <div class="task-top-wrap">
+        <a class="task-detail-back" href="javascript:void(null)" {{action backToTaskList}} ><i
+                class="icon-arrow-left"></i>&nbsp;{{t common.tasks}}</a>
+
+        <div>
+          <i {{bindAttr class="view.openedTask.status :task-detail-status-ico view.openedTask.icon"}}
+                  class="task-detail-status-ico"></i>
+          <span class="task-detail-log-rolename">{{view.openedTask.role}} {{view.openedTask.command}}</span>
+        </div>
+        <div class="task-detail-ico-wrap">
+          <a title="Click to Copy" {{action "textTrigger" taskInfo target="view"}} class="task-detail-copy"><i
+                  class="icon-copy"></i> {{t common.copy}}</a>
+          <a title="Open in New Window" {{action openTaskLogInDialog}} class="task-detail-open-dialog"><i
+                  class="icon-external-link"></i> {{t common.open}}</a>
+        </div>
+      </div>
+      <div class="task-detail-log-info">
+        <div class="content-area">
+          <div class="task-detail-log-clipboard-wrap"></div>
+          <div class="task-detail-log-maintext">
+            <h5>stderr: &nbsp; <span class="muted">{{view.openedTask.errorLog}} </span></h5>
+            <pre class="stderr">{{view.openedTask.stderr}}</pre>
+            <h5>stdout: &nbsp; <span class="muted"> {{view.openedTask.outputLog}} </span></h5>
+            <pre class="stdout">{{view.openedTask.stdout}}</pre>
+          </div>
+        </div>
+      </div>
+    </div>
+  {{/if}}
+{{/if}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/b96edcf9/ambari-web/app/templates/wizard/step9/step9_install_host_popup.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/wizard/step9/step9_install_host_popup.hbs b/ambari-web/app/templates/wizard/step9/step9_install_host_popup.hbs
new file mode 100644
index 0000000..5da8642
--- /dev/null
+++ b/ambari-web/app/templates/wizard/step9/step9_install_host_popup.hbs
@@ -0,0 +1,40 @@
+{{!
+* 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.
+}}
+
+<div id="install-fail-popup" class="pre-scrollable">
+  <table class="table table-striped">
+    <thead>
+    <tr>
+      <th class="span3">{{t common.host}}</th>
+      <th class="span3">{{t common.components}}</th>
+    </tr>
+    </thead>
+    <tbody>
+  {{#each host in view.failedHosts}}
+    <tr>
+      <td>
+        {{host.hostName}}
+      </td>
+      <td>
+        {{host.componentNames}}
+      </td>
+    </tr>
+  {{/each}}
+    </tbody>
+  </table>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/b96edcf9/ambari-web/app/templates/wizard/step9HostTasksLogPopup.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/wizard/step9HostTasksLogPopup.hbs b/ambari-web/app/templates/wizard/step9HostTasksLogPopup.hbs
deleted file mode 100644
index 2671bbb..0000000
--- a/ambari-web/app/templates/wizard/step9HostTasksLogPopup.hbs
+++ /dev/null
@@ -1,90 +0,0 @@
-{{!
-* 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.
-}}
-
-{{#if view.isHeartbeatLost}}
-  <div>
-    {{t installer.step9.host.heartbeat_lost_popup}}
-  </div>
-{{else}}
-  {{#if view.isNoTasksScheduled}}
-    {{t installer.step9.host.status.noTasks}}
-  {{else}}
-    <div {{bindAttr class="view.isLogWrapHidden::hidden :task-list-main-warp"}}>
-      <div class="task-top-wrap">
-        <div class="task-name-top task-detail-log-rolename">{{t common.tasks}}</div>
-        <div class="select-wrap">
-          {{t common.show}}:
-          {{view Ember.Select
-          contentBinding="view.categories"
-          optionValuePath="content.value"
-          optionLabelPath="content.label"
-          selectionBinding="view.category"
-          }}
-        </div>
-      </div>
-      <div id="host-log">
-        {{#each taskInfo in view.tasks}}
-          <div {{bindAttr class="taskInfo.isVisible::hidden :log-list-wrap"}}>
-            <div {{action toggleTaskLog taskInfo}} class="task-list-line-cursor">
-              <div class="operation-name-icon-wrap">
-                <i {{bindAttr class="taskInfo.status taskInfo.icon"}}></i>
-                <a href="#">
-                  {{taskInfo.role}} {{taskInfo.command}}
-                </a>
-              </div>
-              <div class="show-details"><i class="icon-caret-right"></i></div>
-            </div>
-          </div>
-        {{/each}}
-        {{#if view.isEmptyList}}
-          <div class="log-list-wrap">{{t installer.step9.hostLog.popup.noTasksToShow}}</div>
-        {{/if}}
-      </div>
-    </div>
-
-    <div {{bindAttr class="view.isLogWrapHidden:hidden :task-detail-info"}}>
-      <div class="task-top-wrap">
-        <a class="task-detail-back" href="javascript:void(null)" {{action backToTaskList}} ><i
-                class="icon-arrow-left"></i>&nbsp;{{t common.tasks}}</a>
-
-        <div>
-          <i {{bindAttr class="view.openedTask.status :task-detail-status-ico view.openedTask.icon"}}
-                  class="task-detail-status-ico"></i>
-          <span class="task-detail-log-rolename">{{view.openedTask.role}} {{view.openedTask.command}}</span>
-        </div>
-        <div class="task-detail-ico-wrap">
-          <a title="Click to Copy" {{action "textTrigger" taskInfo target="view"}} class="task-detail-copy"><i
-                  class="icon-copy"></i> {{t common.copy}}</a>
-          <a title="Open in New Window" {{action openTaskLogInDialog}} class="task-detail-open-dialog"><i
-                  class="icon-external-link"></i> {{t common.open}}</a>
-        </div>
-      </div>
-      <div class="task-detail-log-info">
-        <div class="content-area">
-          <div class="task-detail-log-clipboard-wrap"></div>
-          <div class="task-detail-log-maintext">
-            <h5>stderr: &nbsp; <span class="muted">{{view.openedTask.errorLog}} </span></h5>
-            <pre class="stderr">{{view.openedTask.stderr}}</pre>
-            <h5>stdout: &nbsp; <span class="muted"> {{view.openedTask.outputLog}} </span></h5>
-            <pre class="stdout">{{view.openedTask.stdout}}</pre>
-          </div>
-        </div>
-      </div>
-    </div>
-  {{/if}}
-{{/if}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/b96edcf9/ambari-web/app/templates/wizard/step9_install_host_popup.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/wizard/step9_install_host_popup.hbs b/ambari-web/app/templates/wizard/step9_install_host_popup.hbs
deleted file mode 100644
index 5da8642..0000000
--- a/ambari-web/app/templates/wizard/step9_install_host_popup.hbs
+++ /dev/null
@@ -1,40 +0,0 @@
-{{!
-* 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.
-}}
-
-<div id="install-fail-popup" class="pre-scrollable">
-  <table class="table table-striped">
-    <thead>
-    <tr>
-      <th class="span3">{{t common.host}}</th>
-      <th class="span3">{{t common.components}}</th>
-    </tr>
-    </thead>
-    <tbody>
-  {{#each host in view.failedHosts}}
-    <tr>
-      <td>
-        {{host.hostName}}
-      </td>
-      <td>
-        {{host.componentNames}}
-      </td>
-    </tr>
-  {{/each}}
-    </tbody>
-  </table>
-</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/b96edcf9/ambari-web/app/views.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views.js b/ambari-web/app/views.js
index 2b48b42..6555313 100644
--- a/ambari-web/app/views.js
+++ b/ambari-web/app/views.js
@@ -257,6 +257,7 @@ require('views/wizard/step6_view');
 require('views/wizard/step7_view');
 require('views/wizard/step8_view');
 require('views/wizard/step9_view');
+require('views/wizard/step9/hostLogPopupBody_view');
 require('views/wizard/step10_view');
 require('views/wizard/stack_upgrade/step1_view');
 require('views/wizard/stack_upgrade/step2_view');

http://git-wip-us.apache.org/repos/asf/ambari/blob/b96edcf9/ambari-web/app/views/wizard/step9/hostLogPopupBody_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/wizard/step9/hostLogPopupBody_view.js b/ambari-web/app/views/wizard/step9/hostLogPopupBody_view.js
new file mode 100644
index 0000000..dd05b59
--- /dev/null
+++ b/ambari-web/app/views/wizard/step9/hostLogPopupBody_view.js
@@ -0,0 +1,264 @@
+/**
+ * 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');
+var date = require('utils/date');
+
+App.WizardStep9HostLogPopupBodyView = Em.View.extend({
+
+  templateName: require('templates/wizard/step9/step9HostTasksLogPopup'),
+
+  /**
+   * Does host lost heartbeat
+   * @type {bool}
+   */
+  isHeartbeatLost: function() {
+    return (this.get('parentView.host.status') === 'heartbeat_lost');
+  }.property('parentView.host.status'),
+
+  /**
+   * Does host doesn't have scheduled tasks for install
+   * @type {bool}
+   */
+  isNoTasksScheduled: function() {
+    return this.get('parentView.host.isNoTasksForInstall');
+  }.property('parentView.host.isNoTasksForInstall'),
+
+  /**
+   * Is log-box hidden
+   * @type {bool}
+   */
+  isLogWrapHidden: true,
+
+  /**
+   * Is log-textarea visible
+   * @type {bool}
+   */
+  showTextArea: false,
+
+  /**
+   * No tasks shown
+   * @type {bool}
+   */
+  isEmptyList: true,
+
+  /**
+   * Checks if no visible tasks are in popup
+   * @method visibleTasks
+   */
+  visibleTasks: function () {
+    this.set("isEmptyList", true);
+    if (this.get('category.value')) {
+      var filter = this.get('category.value');
+      var tasks = this.get('tasks');
+      tasks.setEach("isVisible", false);
+
+      if (filter == "all") {
+        tasks.setEach("isVisible", true);
+      }
+      else if (filter == "pending") {
+        tasks.filterProperty("status", "pending").setEach("isVisible", true);
+        tasks.filterProperty("status", "queued").setEach("isVisible", true);
+      }
+      else if (filter == "in_progress") {
+        tasks.filterProperty("status", "in_progress").setEach("isVisible", true);
+      }
+      else if (filter == "failed") {
+        tasks.filterProperty("status", "failed").setEach("isVisible", true);
+      }
+      else if (filter == "completed") {
+        tasks.filterProperty("status", "completed").setEach("isVisible", true);
+      }
+      else if (filter == "aborted") {
+        tasks.filterProperty("status", "aborted").setEach("isVisible", true);
+      }
+      else if (filter == "timedout") {
+        tasks.filterProperty("status", "timedout").setEach("isVisible", true);
+      }
+
+      if (tasks.filterProperty("isVisible", true).length > 0) {
+        this.set("isEmptyList", false);
+      }
+    }
+  }.observes('category', 'tasks'),
+
+  /**
+   * List categories (implements possible values for task status)
+   * @type {Em.Object[]}
+   */
+  categories: [
+    Em.Object.create({value: 'all', label: Em.I18n.t('installer.step9.hostLog.popup.categories.all') }),
+    Em.Object.create({value: 'pending', label: Em.I18n.t('installer.step9.hostLog.popup.categories.pending')}),
+    Em.Object.create({value: 'in_progress', label: Em.I18n.t('installer.step9.hostLog.popup.categories.in_progress')}),
+    Em.Object.create({value: 'failed', label: Em.I18n.t('installer.step9.hostLog.popup.categories.failed') }),
+    Em.Object.create({value: 'completed', label: Em.I18n.t('installer.step9.hostLog.popup.categories.completed') }),
+    Em.Object.create({value: 'aborted', label: Em.I18n.t('installer.step9.hostLog.popup.categories.aborted') }),
+    Em.Object.create({value: 'timedout', label: Em.I18n.t('installer.step9.hostLog.popup.categories.timedout') })
+  ],
+
+  /**
+   * Current category
+   * @type {Em.Object}
+   */
+  category: null,
+
+  /**
+   * List of tasks
+   * @type {Em.Object[]}
+   */
+  tasks: function () {
+    var tasksArr = [];
+    var host = this.get('parentView.host');
+    var tasks = this.getStartedTasks(host);
+    tasks = tasks.sortProperty('Tasks.id');
+    if (tasks.length) {
+      tasks.forEach(function (_task) {
+        var taskInfo = Em.Object.create({});
+        taskInfo.set('id', _task.Tasks.id);
+        taskInfo.set('requestId', _task.Tasks.request_id);
+        taskInfo.set('command', _task.Tasks.command.toLowerCase() === 'service_check' ? '' : _task.Tasks.command.toLowerCase());
+        taskInfo.set('status', App.format.taskStatus(_task.Tasks.status));
+        taskInfo.set('role', App.format.role(_task.Tasks.role));
+        taskInfo.set('stderr', _task.Tasks.stderr);
+        taskInfo.set('stdout', _task.Tasks.stdout);
+        taskInfo.set('startTime',  date.startTime(_task.Tasks.start_time));
+        taskInfo.set('duration', date.durationSummary(_task.Tasks.start_time, _task.Tasks.end_time));
+        taskInfo.set('isVisible', true);
+        taskInfo.set('icon', '');
+        taskInfo.set('hostName', _task.Tasks.host_name);
+        taskInfo.set('outputLog', Em.I18n.t('common.hostLog.popup.logDir.path') + Em.I18n.t('common.hostLog.popup.outputLog.value').format(_task.Tasks.id));
+        taskInfo.set('errorLog', Em.I18n.t('common.hostLog.popup.logDir.path') + Em.I18n.t('common.hostLog.popup.errorLog.value').format(_task.Tasks.id));
+        if (taskInfo.get('status') == 'pending' || taskInfo.get('status') == 'queued') {
+          taskInfo.set('icon', 'icon-cog');
+        } else if (taskInfo.get('status') == 'in_progress') {
+          taskInfo.set('icon', 'icon-cogs');
+        } else if (taskInfo.get('status') == 'completed') {
+          taskInfo.set('icon', ' icon-ok');
+        } else if (taskInfo.get('status') == 'failed') {
+          taskInfo.set('icon', 'icon-exclamation-sign');
+        } else if (taskInfo.get('status') == 'aborted') {
+          taskInfo.set('icon', 'icon-minus');
+        } else if (taskInfo.get('status') == 'timedout') {
+          taskInfo.set('icon', 'icon-time');
+        }
+        tasksArr.push(taskInfo);
+      }, this);
+    }
+    return tasksArr;
+  }.property('parentView.c.logTasksChangesCounter'),
+
+  /**
+   * Navigate to task list from task view
+   * @method backToTaskList
+   */
+  backToTaskList: function () {
+    this.destroyClipBoard();
+    this.set("isLogWrapHidden", true);
+  },
+
+  /**
+   * Get list of host's started tasks
+   * @param {object} host
+   * @returns {object[]}
+   * @method getStartedTasks
+   */
+  getStartedTasks: function (host) {
+    return host.logTasks.filter(function (task) {
+      return task.Tasks.status;
+    });
+  },
+
+  /**
+   * Open new window with task's log
+   * @method openTaskLogInDialog
+   */
+  openTaskLogInDialog: function () {
+    var newwindow = window.open();
+    var newdocument = newwindow.document;
+    newdocument.write($(".task-detail-log-info").html());
+    newdocument.close();
+  },
+
+  /**
+   * Currently open task
+   * @type {Em.Object}
+   */
+  openedTask: function () {
+    return this.get('tasks').findProperty('id', this.get('parentView.c.currentOpenTaskId'))
+  }.property('parentView.c.currentOpenTaskId', 'tasks.@each'),
+
+  /**
+   * Click-handler for toggle task's log view (textarea to box and back)
+   * @param {object} event
+   * @method toggleTaskLog
+   */
+  toggleTaskLog: function (event) {
+    if (this.get('isLogWrapHidden')) {
+      var taskInfo = event.context;
+      this.set("isLogWrapHidden", false);
+      this.set('parentView.c.currentOpenTaskId', taskInfo.id);
+      this.set('parentView.c.currentOpenTaskRequestId', taskInfo.requestId);
+      this.get('parentView.c').loadCurrentTaskLog();
+      $(".modal").scrollTop(0);
+      $(".modal-body").scrollTop(0);
+    }
+    else {
+      this.set("isLogWrapHidden", true);
+      this.set('parentView.c.currentOpenTaskId', 0);
+      this.set('parentView.c.currentOpenTaskRequestId', 0);
+    }
+  },
+
+  /**
+   * Create (if doesn't exist) or destroy (if exists) clipboard textarea
+   * @method textTrigger
+   */
+  textTrigger: function () {
+    if ($(".task-detail-log-clipboard").length > 0) {
+      this.destroyClipBoard();
+    }
+    else {
+      this.createClipBoard();
+    }
+  },
+
+  /**
+   * Create clipboard with task's log
+   * @method createClipBoard
+   */
+  createClipBoard: function () {
+    var log = $(".task-detail-log-maintext");
+    $(".task-detail-log-clipboard-wrap").html('<textarea class="task-detail-log-clipboard"></textarea>');
+    $(".task-detail-log-clipboard")
+      .html("stderr: \n" + $(".stderr").html() + "\n stdout:\n" + $(".stdout").html())
+      .css("display", "block")
+      .width(log.width())
+      .height(log.height())
+      .select();
+    log.css("display", "none")
+  },
+
+  /**
+   * Destroy clipboard with task's log
+   * @method destroyClipBoard
+   */
+  destroyClipBoard: function () {
+    $(".task-detail-log-clipboard").remove();
+    $(".task-detail-log-maintext").css("display", "block");
+  }
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/b96edcf9/ambari-web/app/views/wizard/step9_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/wizard/step9_view.js b/ambari-web/app/views/wizard/step9_view.js
index 1e08f26..ec64530 100644
--- a/ambari-web/app/views/wizard/step9_view.js
+++ b/ambari-web/app/views/wizard/step9_view.js
@@ -22,21 +22,51 @@ var date = require('utils/date');
 App.WizardStep9View = App.TableView.extend({
 
   templateName: require('templates/wizard/step9'),
+
+  /**
+   * Overall progress-bar color
+   * @type {string}
+   */
   barColor: '',
+
+  /**
+   * Overall result message
+   * @type {string}
+   */
   resultMsg: '',
+
+  /**
+   * Overall message color
+   * @type {string}
+   */
   resultMsgColor: '',
+
+  /**
+   * When progress is 100, step is completed
+   * @type {bool}
+   */
   isStepCompleted: function() {
    return (this.get('controller.progress') === '100');
   }.property('controller.progress'),
 
+  /**
+   * Number of visible hosts
+   * @type {string}
+   */
   displayLength: "25",
 
+  /**
+   * Same to <code>controller.hosts</code>
+   * @type {object[]}
+   */
   content: function () {
     return this.get('controller.hosts');
   }.property('controller.hosts'),
 
-  filteredContent: [],
-
+  /**
+   * Active category
+   * @type {Ember.Object}
+   */
   selectedCategory: function() {
     return this.get('categories').findProperty('isActive');
   }.property('categories.@each.isActive'),
@@ -62,9 +92,10 @@ App.WizardStep9View = App.TableView.extend({
       return this.get('isActive') ? 'active' : '';
     }.property('isActive')
   }),
+
   /**
-   * computed property creates the category objects on the load of the page and sets 'All' as the active category
-   * @Returns: All created categories which are binded and iterated in the template
+   * Domputed property creates the category objects on the load of the page and sets 'All' as the active category
+   * @Returns {Em.Object[]} All created categories which are binded and iterated in the template
    */
   categories: function () {
     return [
@@ -76,13 +107,49 @@ App.WizardStep9View = App.TableView.extend({
     ];
   }.property(),
 
+  /**
+   * True if <code>controller.hostsWithHeartbeatLost</code> contains some values
+   * @type {bool}
+   */
+  isHostHeartbeatLost: function () {
+    return (this.get('controller.hostsWithHeartbeatLost').length > 0);
+  }.property('controller.hostsWithHeartbeatLost.@each'),
+
+  /**
+   * Css-string to overall progress-bar width-property
+   * @type {string}
+   */
+  barWidth: function () {
+    var controller = this.get('controller');
+    return 'width: ' + controller.get('progress') + '%;';
+  }.property('controller.progress'),
+
+  /**
+   * Filter hosts info shown up on bottom of the box. Set by filter function, when 'seletedCategory' changed
+   * @type {string}
+   */
+  filteredHostsInfo: '',
+
+  /**
+   * Message for overall progress
+   * @type {string}
+   */
+  progressMessage: function () {
+    return Em.I18n.t('installer.step9.overallProgress').format(this.get('controller.progress'));
+  }.property('controller.progress'),
+
+  /**
+   * Run <code>countCategoryHosts</code>, <code>filter</code> only once
+   * @method hostStatusObserver
+   */
   hostStatusObserver: function(){
-    Ember.run.once(this, 'countCategoryHosts');
-    Ember.run.once(this, 'filter');
+    Em.run.once(this, 'countCategoryHosts');
+    Em.run.once(this, 'filter');
   }.observes('content.@each.status'),
 
   /**
-   * count each category hosts to update label
+   * Count each category hosts to update label
+   * @method countCategoryHosts
    */
   countCategoryHosts: function () {
     var counters = {
@@ -109,7 +176,8 @@ App.WizardStep9View = App.TableView.extend({
   },
 
   /**
-   * filter hosts by category
+   * Filter hosts by category
+   * @method filter
    */
   filter: function () {
     var self = this;
@@ -136,12 +204,8 @@ App.WizardStep9View = App.TableView.extend({
   }.observes('selectedCategory'),
 
   /**
-   * filter hosts info shown up on bottom of the box. Set by filter function, when 'seletedCategory' changed
-   */
-  filteredHostsInfo: '',
-
-  /**
    * On click handler for 'show all' link
+   * @method showAllHosts
    */
   showAllHosts: function () {
     this.get('categories').forEach(function (category) {
@@ -152,6 +216,7 @@ App.WizardStep9View = App.TableView.extend({
   /**
    * Trigger on Category click
    * @param {Object} event
+   * @method selectCategory
    */
   selectCategory: function (event) {
     var categoryStatus = event.context.get('hostStatus');
@@ -161,24 +226,15 @@ App.WizardStep9View = App.TableView.extend({
   },
 
   didInsertElement: function () {
-    var controller = this.get('controller');
     this.onStatus();
-    controller.navigateStep();
+    this.get('controller').navigateStep();
   },
 
-  isHostHeartbeatLost: function () {
-    return (this.get('controller.hostsWithHeartbeatLost').length > 0);
-  }.property('controller.hostsWithHeartbeatLost.@each'),
-
-  barWidth: function () {
-    var controller = this.get('controller');
-    return 'width: ' + controller.get('progress') + '%;';
-  }.property('controller.progress'),
-
-  progressMessage: function () {
-    return Em.I18n.t('installer.step9.overallProgress').format(this.get('controller.progress'));
-  }.property('controller.progress'),
-
+  /**
+   * Set <code>resultMsg</code>, <code>resultMsg</code>, <code>resultMsgColor</code> according to
+   * <code>controller.status</code>, <code>controller.startCallFailed</code>, <code>isHostHeartbeatLost</code>
+   * @method onStatus
+   */
   onStatus: function () {
     if (this.get('controller.status') === 'info') {
       this.set('resultMsg', '');
@@ -208,17 +264,21 @@ App.WizardStep9View = App.TableView.extend({
     }
   }.observes('controller.status', 'controller.startCallFailed','isHostHeartbeatLost'),
 
-
-  hostWithInstallFailed: function (event, context) {
+  /**
+   * Show popup with info about failed hosts
+   * @return {App.ModalPopup}
+   * @method hostWithInstallFailed
+   */
+  hostWithInstallFailed: function () {
     var controller = this.get('controller');
-    App.ModalPopup.show({
+    return App.ModalPopup.show({
       header: Em.I18n.t('installer.step9.host.heartbeat_lost.header'),
       classNames: ['sixty-percent-width-modal'],
       autoHeight: false,
       secondary: null,
 
-      bodyClass: Ember.View.extend({
-        templateName: require('templates/wizard/step9_install_host_popup'),
+      bodyClass: Em.View.extend({
+        templateName: require('templates/wizard/step9/step9_install_host_popup'),
         c: controller,
         failedHosts: function () {
           return controller.get('hostsWithHeartbeatLost');
@@ -226,28 +286,80 @@ App.WizardStep9View = App.TableView.extend({
       })
     });
   }
+
 });
 
 App.HostStatusView = Em.View.extend({
+
   tagName: 'tr',
+
   /**
    * Current host
+   * @type {Em.Object}
    */
   obj: null,
+
   /**
    * wizardStep9Controller
+   * @type {App.WizardStep9Controller}
    */
   controller: null,
-  barColor: '',
 
-  didInsertElement: function () {
-    this.onStatus();
-  },
+  /**
+   * Progress-bar color for current host
+   * @type {string}
+   */
+  barColor: '',
 
+  /**
+   * Css-string to progress-bar width-property
+   * @type {string}
+   */
   barWidth: function () {
     return 'width: ' + this.get('obj.progress') + '%;';
   }.property('obj.progress'),
 
+  /**
+   * Is current host failed
+   * @type {bool}
+   */
+  isFailed: function () {
+    return (this.get('isHostCompleted') && (this.get('obj.status') === 'failed' || this.get('obj.status') === 'heartbeat_lost'));
+  }.property('obj.status', 'isHostCompleted'),
+
+  /**
+   * Is current host successfully installed
+   * @type {bool}
+   */
+  isSuccess: function () {
+    return (this.get('isHostCompleted') && this.get('obj.status') === 'success');
+  }.property('obj.status', 'isHostCompleted'),
+
+  /**
+   * Current host has warnings
+   * @type {bool}
+   */
+  isWarning: function () {
+    return(this.get('isHostCompleted') && this.get('obj.status') === 'warning');
+  }.property('obj.status', 'isHostCompleted'),
+
+  /**
+   * Current host completed all its tasks
+   * @type {bool}
+   */
+  isHostCompleted: function () {
+    return this.get('obj.progress') == 100;
+  }.property('obj.progress'),
+
+  didInsertElement: function () {
+    this.onStatus();
+  },
+
+  /**
+   * Set <code>barColor</code>, <code>obj.progress</code>, <code>obj.message</code> according to
+   * <code>obj.status</code>, <code>obj.progress</code>, <code>controller.progress</code>
+   * @method onStatus
+   */
   onStatus: function () {
     if (this.get('obj.status') === 'info') {
       this.set('barColor', 'progress-info');
@@ -272,37 +384,34 @@ App.HostStatusView = Em.View.extend({
     }
   }.observes('obj.status', 'obj.progress', 'controller.progress'),
 
-  isFailed: function () {
-    return (this.get('isHostCompleted') && (this.get('obj.status') === 'failed' || this.get('obj.status') === 'heartbeat_lost'));
-  }.property('obj.status', 'isHostCompleted'),
-
-  isSuccess: function () {
-    return (this.get('isHostCompleted') && this.get('obj.status') === 'success');
-  }.property('obj.status', 'isHostCompleted'),
-
-  isWarning: function () {
-    return(this.get('isHostCompleted') && this.get('obj.status') === 'warning');
-  }.property('obj.status', 'isHostCompleted'),
-
-  isHostCompleted: function () {
-    return this.get('obj.progress') == 100;
-  }.property('obj.progress'),
-
-  hostLogPopup: function (event, context) {
+  /**
+   * Show popup with host logs
+   * @return {App.ModalPopup}
+   * @method hostLogPopup
+   */
+  hostLogPopup: function () {
     var controller = this.get('controller');
     var host = this.get('obj');
 
-    App.ModalPopup.show({
+    return App.ModalPopup.show({
+
       header: host.get('name'),
+
       classNames: ['sixty-percent-width-modal'],
+
       autoHeight: false,
+
       secondary: null,
+
       /**
        * Current host
+       * @type {Em.Object}
        */
       host: host,
+
       /**
        * wizardStep9Controller
+       * @type {App.WizardStep9Controller}
        */
       c: controller,
 
@@ -311,175 +420,7 @@ App.HostStatusView = Em.View.extend({
         this.hide();
       },
 
-      bodyClass: Ember.View.extend({
-        templateName: require('templates/wizard/step9HostTasksLogPopup'),
-        isHeartbeatLost: function() {
-          return (host.get('status') === 'heartbeat_lost');
-        }.property('host.status'),
-        isNoTasksScheduled: function() {
-          return host.get('isNoTasksForInstall');
-        }.property('host.isNoTasksForInstall'),
-
-        isLogWrapHidden: true,
-
-        showTextArea: false,
-
-        isEmptyList: true,
-
-        visibleTasks: function () {
-          this.set("isEmptyList", true);
-          if (this.get('category.value')) {
-            var filter = this.get('category.value');
-            var tasks = this.get('tasks');
-            tasks.setEach("isVisible", false);
-
-            if (filter == "all") {
-              tasks.setEach("isVisible", true);
-            }
-            else if (filter == "pending") {
-              tasks.filterProperty("status", "pending").setEach("isVisible", true);
-              tasks.filterProperty("status", "queued").setEach("isVisible", true);
-            }
-            else if (filter == "in_progress") {
-              tasks.filterProperty("status", "in_progress").setEach("isVisible", true);
-            }
-            else if (filter == "failed") {
-              tasks.filterProperty("status", "failed").setEach("isVisible", true);
-            }
-            else if (filter == "completed") {
-              tasks.filterProperty("status", "completed").setEach("isVisible", true);
-            }
-            else if (filter == "aborted") {
-              tasks.filterProperty("status", "aborted").setEach("isVisible", true);
-            }
-            else if (filter == "timedout") {
-              tasks.filterProperty("status", "timedout").setEach("isVisible", true);
-            }
-
-            if (tasks.filterProperty("isVisible", true).length > 0) {
-              this.set("isEmptyList", false);
-            }
-          }
-        }.observes('category', 'tasks'),
-
-        categories: [
-          Ember.Object.create({value: 'all', label: Em.I18n.t('installer.step9.hostLog.popup.categories.all') }),
-          Ember.Object.create({value: 'pending', label: Em.I18n.t('installer.step9.hostLog.popup.categories.pending')}),
-          Ember.Object.create({value: 'in_progress', label: Em.I18n.t('installer.step9.hostLog.popup.categories.in_progress')}),
-          Ember.Object.create({value: 'failed', label: Em.I18n.t('installer.step9.hostLog.popup.categories.failed') }),
-          Ember.Object.create({value: 'completed', label: Em.I18n.t('installer.step9.hostLog.popup.categories.completed') }),
-          Ember.Object.create({value: 'aborted', label: Em.I18n.t('installer.step9.hostLog.popup.categories.aborted') }),
-          Ember.Object.create({value: 'timedout', label: Em.I18n.t('installer.step9.hostLog.popup.categories.timedout') })
-        ],
-
-        category: null,
-
-        tasks: function () {
-          var tasksArr = [];
-          var host = this.get('parentView.host');
-          var tasks = this.getStartedTasks(host);
-          tasks = tasks.sortProperty('Tasks.id');
-          if (tasks.length) {
-            tasks.forEach(function (_task) {
-              var taskInfo = Ember.Object.create({});
-              taskInfo.set('id', _task.Tasks.id);
-              taskInfo.set('requestId', _task.Tasks.request_id);
-              taskInfo.set('command', _task.Tasks.command.toLowerCase() === 'service_check' ? '' : _task.Tasks.command.toLowerCase());
-              taskInfo.set('status', App.format.taskStatus(_task.Tasks.status));
-              taskInfo.set('role', App.format.role(_task.Tasks.role));
-              taskInfo.set('stderr', _task.Tasks.stderr);
-              taskInfo.set('stdout', _task.Tasks.stdout);
-              taskInfo.set('startTime',  date.startTime(_task.Tasks.start_time));
-              taskInfo.set('duration', date.durationSummary(_task.Tasks.start_time, _task.Tasks.end_time));
-              taskInfo.set('isVisible', true);
-              taskInfo.set('icon', '');
-              taskInfo.set('hostName', _task.Tasks.host_name);
-              taskInfo.set('outputLog', Em.I18n.t('common.hostLog.popup.logDir.path') + Em.I18n.t('common.hostLog.popup.outputLog.value').format(_task.Tasks.id));
-              taskInfo.set('errorLog', Em.I18n.t('common.hostLog.popup.logDir.path') + Em.I18n.t('common.hostLog.popup.errorLog.value').format(_task.Tasks.id));
-              if (taskInfo.get('status') == 'pending' || taskInfo.get('status') == 'queued') {
-                taskInfo.set('icon', 'icon-cog');
-              } else if (taskInfo.get('status') == 'in_progress') {
-                taskInfo.set('icon', 'icon-cogs');
-              } else if (taskInfo.get('status') == 'completed') {
-                taskInfo.set('icon', ' icon-ok');
-              } else if (taskInfo.get('status') == 'failed') {
-                taskInfo.set('icon', 'icon-exclamation-sign');
-              } else if (taskInfo.get('status') == 'aborted') {
-                taskInfo.set('icon', 'icon-minus');
-              } else if (taskInfo.get('status') == 'timedout') {
-                taskInfo.set('icon', 'icon-time');
-              }
-              tasksArr.push(taskInfo);
-            }, this);
-          }
-          return tasksArr;
-        }.property('parentView.c.logTasksChangesCounter'),
-
-        backToTaskList: function (event, context) {
-          this.destroyClipBoard();
-          this.set("isLogWrapHidden", true);
-        },
-
-        getStartedTasks: function (host) {
-          return host.logTasks.filter(function (task) {
-            return task.Tasks.status;
-          });
-        },
-
-        openTaskLogInDialog: function () {
-          var newwindow = window.open();
-          var newdocument = newwindow.document;
-          newdocument.write($(".task-detail-log-info").html());
-          newdocument.close();
-        },
-
-        openedTask: function () {
-          return this.get('tasks').findProperty('id', this.get('parentView.c.currentOpenTaskId'))
-        }.property('parentView.c.currentOpenTaskId', 'tasks.@each'),
-
-        toggleTaskLog: function (event, context) {
-          if (this.get('isLogWrapHidden')) {
-            var taskInfo = event.context;
-            this.set("isLogWrapHidden", false);
-            this.set('parentView.c.currentOpenTaskId', taskInfo.id);
-            this.set('parentView.c.currentOpenTaskRequestId', taskInfo.requestId);
-            this.get('parentView.c').loadCurrentTaskLog();
-            $(".modal").scrollTop(0);
-            $(".modal-body").scrollTop(0);
-          }
-          else {
-            this.set("isLogWrapHidden", true);
-            this.set('parentView.c.currentOpenTaskId', 0);
-            this.set('parentView.c.currentOpenTaskRequestId', 0);
-          }
-        },
-
-        textTrigger: function (event) {
-          if ($(".task-detail-log-clipboard").length > 0) {
-            this.destroyClipBoard();
-          }
-          else {
-            this.createClipBoard();
-          }
-        },
-
-        createClipBoard: function () {
-          var log = $(".task-detail-log-maintext");
-          $(".task-detail-log-clipboard-wrap").html('<textarea class="task-detail-log-clipboard"></textarea>');
-          $(".task-detail-log-clipboard")
-            .html("stderr: \n" + $(".stderr").html() + "\n stdout:\n" + $(".stdout").html())
-            .css("display", "block")
-            .width(log.width())
-            .height(log.height())
-            .select();
-          log.css("display", "none")
-        },
-
-        destroyClipBoard: function () {
-          $(".task-detail-log-clipboard").remove();
-          $(".task-detail-log-maintext").css("display", "block");
-        }
-      })
+      bodyClass: App.WizardStep9HostLogPopupBodyView
     });
   }