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/15 16:55:07 UTC

[2/3] AMBARI-5474. Unit tests for steps 3-4. (onechiporenko)

http://git-wip-us.apache.org/repos/asf/ambari/blob/5ef841a0/ambari-web/app/templates/wizard/step3_host_warning_popup_footer.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/wizard/step3_host_warning_popup_footer.hbs b/ambari-web/app/templates/wizard/step3_host_warning_popup_footer.hbs
deleted file mode 100644
index 163374c..0000000
--- a/ambari-web/app/templates/wizard/step3_host_warning_popup_footer.hbs
+++ /dev/null
@@ -1,33 +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 class="update-progress pull-left">
-  {{#if view.isUpdateInProgress}}
-    <div class="progress-info active progress">
-      <div class="bar" {{bindAttr style="view.progressWidth"}}></div>
-    </div>
-  {{else}}
-    <label {{bindAttr class="view.updateStatusClass"}}>{{view.updateStatus}}</label>
-  {{/if}}
-</div>
-{{#if view.parentView.secondary}}
-  <button type="button" class="btn btn-info" {{bindAttr disabled="view.isUpdateInProgress"}} {{action onSecondary target="view.parentView"}}><i class="icon-repeat"></i>&nbsp;{{view.parentView.secondary}}</button>
-{{/if}}
-{{#if view.parentView.primary}}
-  <button type="button" class="btn" {{action onPrimary target="view.parentView"}}>{{view.parentView.primary}}</button>
-{{/if}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/5ef841a0/ambari-web/app/templates/wizard/step3_host_warnings_popup.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/wizard/step3_host_warnings_popup.hbs b/ambari-web/app/templates/wizard/step3_host_warnings_popup.hbs
deleted file mode 100644
index d4fe22c..0000000
--- a/ambari-web/app/templates/wizard/step3_host_warnings_popup.hbs
+++ /dev/null
@@ -1,94 +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="host-warnings">
-  <div class="notice">
-    <span>{{t installer.step3.hostWarningsPopup.checks}} <b>{{view.warningsNotice}}</b>.<br>{{t installer.step3.hostWarningsPopup.notice}}</span>
-  </div>
-  <div class="row-fluid">
-    <div class="span7">
-      {{t common.hosts}}&nbsp;{{view view.hostSelectView}}
-    </div>
-    {{#if view.categoryWarnings.length}}
-    <div class="span3 offset2">
-      <a href="javascript:void(null)" title="Show Details" {{action openWarningsInDialog target="view"}}
-         class="task-detail-open-dialog"><i
-              class="icon-external-link"></i> {{t installer.step3.hostWarningsPopup.report}}</a>
-    </div>
-    {{/if}}
-  </div>
-  <div class="accordion warnings-list" id="accordion2">
-    {{#each category in view.content}}
-    <div class="accordion-group block">
-      <div class="accordion-heading" {{action onToggleBlock category}}>
-        <i {{bindAttr class=":pull-left :accordion-toggle category.isCollapsed:icon-caret-right:icon-caret-down"}}></i>
-        {{#if category.warnings.length}}
-        <i class="pull-right accordion-toggle icon-warning-sign"></i>
-        {{else}}
-        <i class="pull-right accordion-toggle icon-ok"></i>
-        {{/if}}
-        <a class="accordion-toggle">
-          {{category.title}} ({{category.warnings.length}})
-        </a>
-      </div>
-      <div id="{{unbound category.category}}" class="accordion-body collapse in" style="display: none">
-        <div class="accordion-inner">
-          {{#if category.warnings.length}}
-          <table>
-            <thead>
-            <tr>
-              <th colspan="2">{{{category.message}}}</th>
-            </tr>
-            <tr>
-              <th colspan="2"><b>{{category.type}}</b></th>
-            </tr>
-            </thead>
-            <tbody>
-            {{#each warning in category.warnings}}
-            <tr>
-              <td class="warning-name" {{bindAttr data-original-title="warning.command"}} >{{{warning.name}}}</td>
-              {{#if warning.version}}
-                  <td class="package-version">{{warning.version}}</td>
-              {{/if}}
-              {{#if warning.target}}
-                <td class="package-version">{{warning.target}}</td>
-              {{/if}}
-              <td>{{category.action}}
-                <a href="javascript:void(null);" rel='HostsListTooltip' {{bindAttr data-original-title="warning.hostsList"}} {{action showHostsPopup warning.hosts}}>
-                  {{warning.hosts.length}}
-                  {{#if warning.onSingleHost}}
-                  {{t installer.step3.hostWarningsPopup.host}}
-                  {{else}}
-                  {{t installer.step3.hostWarningsPopup.hosts}}
-                  {{/if}}
-                </a>
-              </td>
-            </tr>
-            {{/each}}
-            </tbody>
-          </table>
-          {{else}}
-          {{t installer.step3.hostWarningsPopup.emptyMessage}} {{category.emptyName}}
-          {{/if}}
-        </div>
-      </div>
-    </div>
-    {{/each}}
-  </div>
-</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/5ef841a0/ambari-web/app/templates/wizard/step3_registered_hosts_popup.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/wizard/step3_registered_hosts_popup.hbs b/ambari-web/app/templates/wizard/step3_registered_hosts_popup.hbs
deleted file mode 100644
index c5907a7..0000000
--- a/ambari-web/app/templates/wizard/step3_registered_hosts_popup.hbs
+++ /dev/null
@@ -1,24 +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.
-}}
-
-<p>{{view.message}}</p>
-<ul>
-  {{#each host in view.registeredHosts}}
-    <li>{{host}}</li>
-  {{/each}}
-</ul>

http://git-wip-us.apache.org/repos/asf/ambari/blob/5ef841a0/ambari-web/app/templates/wizard/step4.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/wizard/step4.hbs b/ambari-web/app/templates/wizard/step4.hbs
index 0bd10d3..cda6d39 100644
--- a/ambari-web/app/templates/wizard/step4.hbs
+++ b/ambari-web/app/templates/wizard/step4.hbs
@@ -28,9 +28,8 @@
       <th class="span3">{{t common.service}}
         <span style="margin-left:10px">
           <a href="#" {{action selectAll target="controller"}} {{bindAttr class="isAll:selected:deselected"}}>{{t all}}</a>
-                |
-                <a
-                  href="#" {{action selectMinimum target="controller"}} {{bindAttr class="isMinimum:selected:deselected"}}>{{t none}}</a>
+           |
+          <a href="#" {{action selectMinimum target="controller"}} {{bindAttr class="isMinimum:selected:deselected"}}>{{t none}}</a>
         </span>
       </th>
       <th>{{t common.version}}</th>
@@ -38,24 +37,22 @@
     </tr>
     </thead>
     <tbody>
-    {{#each controller}}
-    {{#unless isHidden}}
-    <tr {{bindAttr class="isSelected:success:"}}>
-      <td><label
-        class="checkbox">{{view Ember.Checkbox disabledBinding="isDisabled" checkedBinding="isSelected"}}{{displayName}}</label>
-      </td>
-      <td>{{version}}</td>
-      <td>{{{description}}}</td>
-    </tr>
-    {{/unless}}
-    {{/each}}
+      {{#each controller}}
+        {{#unless isHidden}}
+        <tr {{bindAttr class="isSelected:success:"}}>
+          <td><label class="checkbox">{{view Ember.Checkbox disabledBinding="isDisabled" checkedBinding="isSelected"}}{{displayName}}</label>
+          </td>
+          <td>{{version}}</td>
+          <td>{{{description}}}</td>
+        </tr>
+        {{/unless}}
+      {{/each}}
     </tbody>
   </table>
 
   <div class="btn-area">
     {{#unless view.parentView.controller.hideBackButton}}
-    <a class="btn pull-left" {{action back}}>&larr; {{t common.back}}
-    </a>
+      <a class="btn pull-left" {{action back}}>&larr; {{t common.back}}</a>
     {{/unless}}
     <a class="btn btn-success pull-right" {{bindAttr disabled="isSubmitDisabled"}} {{action submit target="controller"}}> {{t common.next}} &rarr;</a>
   </div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/5ef841a0/ambari-web/app/views.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views.js b/ambari-web/app/views.js
index 5376166..4af6051 100644
--- a/ambari-web/app/views.js
+++ b/ambari-web/app/views.js
@@ -244,6 +244,9 @@ require('views/wizard/step0_view');
 require('views/wizard/step1_view');
 require('views/wizard/step2_view');
 require('views/wizard/step3_view');
+require('views/wizard/step3/hostLogPopupBody_view');
+require('views/wizard/step3/hostWarningPopupBody_view');
+require('views/wizard/step3/hostWarningPopupFooter_view');
 require('views/wizard/step4_view');
 require('views/wizard/step5_view');
 require('views/wizard/step6_view');

http://git-wip-us.apache.org/repos/asf/ambari/blob/5ef841a0/ambari-web/app/views/wizard/step3/hostLogPopupBody_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/wizard/step3/hostLogPopupBody_view.js b/ambari-web/app/views/wizard/step3/hostLogPopupBody_view.js
new file mode 100644
index 0000000..0cca32d
--- /dev/null
+++ b/ambari-web/app/views/wizard/step3/hostLogPopupBody_view.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');
+
+App.WizardStep3HostLogPopupBody = Em.View.extend({
+
+  templateName: require('templates/wizard/step3/step3_host_log_popup'),
+
+  /**
+   * Host's boot log
+   * @type {string}
+   */
+  bootLog: function() {
+    return this.get('parentView.host.bootLog');
+  }.property('parentView.host.bootLog'),
+
+  /**
+   * Is textarea view active
+   * @type {bool}
+   */
+  isTextArea: false,
+
+  /**
+   * Textbox with host's boot log
+   * @type {Ember.TextArea}
+   */
+  textArea: Em.TextArea.extend({
+
+    didInsertElement: function () {
+      var element = $(this.get('element'));
+      element.width($(this.get('parentView').get('element')).width() - 10);
+      element.height($(this.get('parentView').get('element')).height());
+      element.select();
+      element.css('resize', 'none');
+    },
+
+    /**
+     * Edit disabled
+     * @type {bool}
+     */
+    readOnly: true,
+
+    /**
+     * <code>parentView.bootLog</code>
+     * @type {string}
+     */
+    value: function () {
+      return this.get('content');
+    }.property('content')
+
+  }),
+
+  didInsertElement: function () {
+    var self = this;
+    var button = $(this.get('element')).find('.textTrigger');
+    button.click(function () {
+      $(this).text(self.get('isTextArea') ? Em.I18n.t('installer.step3.hostLogPopup.highlight') : Em.I18n.t('installer.step3.hostLogPopup.copy'));
+      self.set('isTextArea', !self.get('isTextArea'));
+    });
+    $(this.get('element')).find('.content-area').mouseenter(
+      function () {
+        $(this).css('border', '1px solid #dcdcdc');
+        button.css('visibility', 'visible');
+      }).mouseleave(
+      function () {
+        $(this).css('border', 'none');
+        button.css('visibility', 'hidden');
+      })
+  }
+
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/5ef841a0/ambari-web/app/views/wizard/step3/hostWarningPopupBody_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/wizard/step3/hostWarningPopupBody_view.js b/ambari-web/app/views/wizard/step3/hostWarningPopupBody_view.js
new file mode 100644
index 0000000..036de2e
--- /dev/null
+++ b/ambari-web/app/views/wizard/step3/hostWarningPopupBody_view.js
@@ -0,0 +1,386 @@
+/**
+ * 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 lazyloading = require('utils/lazy_loading');
+var numberUtils = require('utils/number_utils');
+
+App.WizardStep3HostWarningPopupBody = Em.View.extend({
+
+  templateName: require('templates/wizard/step3/step3_host_warnings_popup'),
+
+  classNames: ['host-check'],
+
+  bodyControllerBinding: 'App.router.wizardStep3Controller',
+
+  /**
+   * Listbox with host filters
+   * @type {Ember.Select}
+   */
+  hostSelectView: Em.Select.extend({
+
+    selectionBinding: "parentView.category",
+
+    /**
+     * Content has default value "All Hosts" to bind selection to category
+     * @type {string[]}
+     */
+    content: ['All Hosts'],
+
+    /**
+     * List of filtered hostnames
+     * @type {string[]}
+     */
+    hosts: function () {
+      return this.get('parentView.warningsByHost').mapProperty('name');
+    }.property('parentView.warningsByHost'),
+
+    /**
+     * Is data loaded
+     * @type {bool}
+     */
+    isLoaded: false,
+
+    didInsertElement: function () {
+      this.initContent();
+    },
+
+    /**
+     * Check browser and set content for listbox
+     * @method initContent
+     */
+    initContent: function () {
+      this.set('isLoaded', false);
+      //The lazy loading for select elements supported only by Firefox and Chrome
+      var isBrowserSupported = $.browser.mozilla || ($.browser.safari && navigator.userAgent.indexOf('Chrome') !== -1);
+      var isLazyLoading = isBrowserSupported && this.get('hosts').length > 100;
+      this.set('isLazyLoading', isLazyLoading);
+      if (isLazyLoading) {
+        //select need at least 30 hosts to have scrollbar
+        this.set('content', this.get('hosts').slice(0, 30));
+      } else {
+        this.set('content', this.get('hosts'));
+        this.set('isLoaded', true);
+      }
+    }.observes('parentView.warningsByHost'),
+
+    /**
+     * On click start lazy loading
+     * @method click
+     */
+    click: function () {
+      if (!this.get('isLoaded') && this.get('isLazyLoading')) {
+        //filter out hosts, which already pushed in select
+        var source = this.get('hosts').filter(function (_host) {
+          return !this.get('content').contains(_host);
+        }, this).slice();
+        lazyloading.run({
+          destination: this.get('content'),
+          source: source,
+          context: this,
+          initSize: 30,
+          chunkSize: 200,
+          delay: 50
+        });
+      }
+    }
+  }),
+
+  /**
+   * List of warnings grouped by host
+   * Same to <code>bodyController.warningsByHost</code>
+   * @type {Ember.Enumerable}
+   */
+  warningsByHost: function () {
+    return this.get('bodyController.warningsByHost');
+  }.property('bodyController.warningsByHost'),
+
+  /**
+   * List of all warnings
+   * Same to <code>bodyController.warnings</code>
+   * @type {Ember.Enumerable}
+   */
+  warnings: function () {
+    return this.get('bodyController.warnings');
+  }.property('bodyController.warnings'),
+
+  /**
+   * Selected category
+   * @type {string}
+   */
+  category: 'All Hosts',
+
+  /**
+   * List of warnings for selected <code>category</code>
+   * @type {Ember.Enumerable}
+   */
+  categoryWarnings: function () {
+    return this.get('warningsByHost').findProperty('name', this.get('category')).warnings
+  }.property('warningsByHost', 'category'),
+
+  /**
+   * List of warnings grouped by <code>category</code>
+   * @type {Ember.Object[]}
+   */
+  content: function () {
+    var repoCategoryWarnings = this.get('bodyController.repoCategoryWarnings');
+    var diskCategoryWarnings = this.get('bodyController.diskCategoryWarnings');
+    var categoryWarnings = this.get('categoryWarnings');
+    return [
+      Em.Object.create({
+        warnings: diskCategoryWarnings,
+        title: Em.I18n.t('installer.step3.hostWarningsPopup.disk'),
+        message: Em.I18n.t('installer.step3.hostWarningsPopup.disk.message'),
+        type: Em.I18n.t('common.issues'),
+        emptyName: Em.I18n.t('installer.step3.hostWarningsPopup.empty.disk'),
+        action: Em.I18n.t('installer.step3.hostWarningsPopup.action.exists'),
+        category: 'disk',
+        isCollapsed: true
+      }),
+      Em.Object.create({
+        warnings: repoCategoryWarnings,
+        title: Em.I18n.t('installer.step3.hostWarningsPopup.repositories'),
+        message: Em.I18n.t('installer.step3.hostWarningsPopup.repositories.message'),
+        type: Em.I18n.t('common.issues'),
+        emptyName: Em.I18n.t('installer.step3.hostWarningsPopup.empty.repositories'),
+        action: Em.I18n.t('installer.step3.hostWarningsPopup.action.invalid'),
+        category: 'repositories',
+        isCollapsed: true
+      }),
+      Em.Object.create({
+        warnings: categoryWarnings.filterProperty('category', 'firewall'),
+        title: Em.I18n.t('installer.step3.hostWarningsPopup.firewall'),
+        message: Em.I18n.t('installer.step3.hostWarningsPopup.firewall.message'),
+        type: Em.I18n.t('common.issues'),
+        emptyName: Em.I18n.t('installer.step3.hostWarningsPopup.empty.firewall'),
+        action: Em.I18n.t('installer.step3.hostWarningsPopup.action.running'),
+        category: 'firewall',
+        isCollapsed: true
+      }),
+      Em.Object.create({
+        warnings: categoryWarnings.filterProperty('category', 'processes'),
+        title: Em.I18n.t('installer.step3.hostWarningsPopup.process'),
+        message: Em.I18n.t('installer.step3.hostWarningsPopup.processes.message'),
+        type: Em.I18n.t('common.process'),
+        emptyName: Em.I18n.t('installer.step3.hostWarningsPopup.empty.processes'),
+        action: Em.I18n.t('installer.step3.hostWarningsPopup.action.running'),
+        category: 'process',
+        isCollapsed: true
+      }),
+      Em.Object.create({
+        warnings: categoryWarnings.filterProperty('category', 'packages'),
+        title: Em.I18n.t('installer.step3.hostWarningsPopup.package'),
+        message: Em.I18n.t('installer.step3.hostWarningsPopup.packages.message'),
+        type: Em.I18n.t('common.package'),
+        emptyName: Em.I18n.t('installer.step3.hostWarningsPopup.empty.packages'),
+        action: Em.I18n.t('installer.step3.hostWarningsPopup.action.installed'),
+        category: 'package',
+        isCollapsed: true
+      }),
+      Em.Object.create({
+        warnings: categoryWarnings.filterProperty('category', 'fileFolders'),
+        title: Em.I18n.t('installer.step3.hostWarningsPopup.fileAndFolder'),
+        message: Em.I18n.t('installer.step3.hostWarningsPopup.fileFolders.message'),
+        type: Em.I18n.t('common.path'),
+        emptyName: Em.I18n.t('installer.step3.hostWarningsPopup.empty.filesAndFolders'),
+        action: Em.I18n.t('installer.step3.hostWarningsPopup.action.exists'),
+        category: 'fileFolders',
+        isCollapsed: true
+      }),
+      Em.Object.create({
+        warnings: categoryWarnings.filterProperty('category', 'services'),
+        title: Em.I18n.t('installer.step3.hostWarningsPopup.service'),
+        message: Em.I18n.t('installer.step3.hostWarningsPopup.services.message'),
+        type: Em.I18n.t('common.service'),
+        emptyName: Em.I18n.t('installer.step3.hostWarningsPopup.empty.services'),
+        action: Em.I18n.t('installer.step3.hostWarningsPopup.action.notRunning'),
+        category: 'service',
+        isCollapsed: true
+      }),
+      Em.Object.create({
+        warnings: categoryWarnings.filterProperty('category', 'users'),
+        title: Em.I18n.t('installer.step3.hostWarningsPopup.user'),
+        message: Em.I18n.t('installer.step3.hostWarningsPopup.users.message'),
+        type: Em.I18n.t('common.user'),
+        emptyName: Em.I18n.t('installer.step3.hostWarningsPopup.empty.users'),
+        action: Em.I18n.t('installer.step3.hostWarningsPopup.action.exists'),
+        category: 'user',
+        isCollapsed: true
+      }),
+      Em.Object.create({
+        warnings: categoryWarnings.filterProperty('category', 'misc'),
+        title: Em.I18n.t('installer.step3.hostWarningsPopup.misc'),
+        message: Em.I18n.t('installer.step3.hostWarningsPopup.misc.message'),
+        type: Em.I18n.t('installer.step3.hostWarningsPopup.misc.umask'),
+        emptyName: Em.I18n.t('installer.step3.hostWarningsPopup.empty.misc'),
+        action: Em.I18n.t('installer.step3.hostWarningsPopup.action.exists'),
+        category: 'misc',
+        isCollapsed: true
+      }),
+      Em.Object.create({
+        warnings: categoryWarnings.filterProperty('category', 'alternatives'),
+        title: Em.I18n.t('installer.step3.hostWarningsPopup.alternatives'),
+        message: Em.I18n.t('installer.step3.hostWarningsPopup.alternatives.message'),
+        type: Em.I18n.t('installer.step3.hostWarningsPopup.alternatives.umask'),
+        emptyName: Em.I18n.t('installer.step3.hostWarningsPopup.alternatives.emptyinstaller.step3.hostWarningsPopup.alternatives.empty'),
+        action: Em.I18n.t('installer.step3.hostWarningsPopup.action.exists'),
+        category: 'alternatives',
+        isCollapsed: true
+      })
+    ]
+  }.property('category', 'warningsByHost'),
+
+  /**
+   * Message with info about warnings
+   * @return {string}
+   */
+  warningsNotice: function () {
+    var issuesNumber = this.get('warnings.length') + this.get('bodyController.repoCategoryWarnings.length') + this.get('bodyController.diskCategoryWarnings.length');
+    var issues = issuesNumber + ' ' + (issuesNumber.length === 1 ? Em.I18n.t('installer.step3.hostWarningsPopup.issue') : Em.I18n.t('installer.step3.hostWarningsPopup.issues'));
+    var hostsCnt = this.warningHostsNamesCount();
+    var hosts = hostsCnt + ' ' + (hostsCnt === 1 ? Em.I18n.t('installer.step3.hostWarningsPopup.host') : Em.I18n.t('installer.step3.hostWarningsPopup.hosts'));
+    return Em.I18n.t('installer.step3.hostWarningsPopup.summary').format(issues, hosts);
+  }.property('warnings', 'warningsByHost'),
+
+  /**
+   * Detailed content to show it in new window
+   * @return {string}
+   */
+  contentInDetails: function () {
+    var content = this.get('content');
+    var warningsByHost = this.get('warningsByHost').slice();
+    warningsByHost.shift();
+    var newContent = '';
+    newContent += Em.I18n.t('installer.step3.hostWarningsPopup.report.header') + new Date;
+    newContent += Em.I18n.t('installer.step3.hostWarningsPopup.report.hosts');
+    newContent += warningsByHost.filterProperty('warnings.length').mapProperty('name').join(' ');
+    if (content.findProperty('category', 'firewall').warnings.length) {
+      newContent += Em.I18n.t('installer.step3.hostWarningsPopup.report.firewall');
+      newContent += content.findProperty('category', 'firewall').warnings.mapProperty('name').join('<br>');
+    }
+    if (content.findProperty('category', 'fileFolders').warnings.length) {
+      newContent += Em.I18n.t('installer.step3.hostWarningsPopup.report.fileFolders');
+      newContent += content.findProperty('category', 'fileFolders').warnings.mapProperty('name').join(' ');
+    }
+    if (content.findProperty('category', 'process').warnings.length) {
+      newContent += Em.I18n.t('installer.step3.hostWarningsPopup.report.process');
+      content.findProperty('category', 'process').warnings.forEach(function (process, i) {
+        process.hosts.forEach(function (host, j) {
+          if (!!i || !!j) {
+            newContent += ',';
+          }
+          newContent += '(' + host + ',' + process.user + ',' + process.pid + ')';
+        });
+      });
+    }
+    if (content.findProperty('category', 'package').warnings.length) {
+      newContent += Em.I18n.t('installer.step3.hostWarningsPopup.report.package');
+      newContent += content.findProperty('category', 'package').warnings.mapProperty('name').join(' ');
+    }
+    if (content.findProperty('category', 'service').warnings.length) {
+      newContent += Em.I18n.t('installer.step3.hostWarningsPopup.report.service');
+      newContent += content.findProperty('category', 'service').warnings.mapProperty('name').join(' ');
+    }
+    if (content.findProperty('category', 'user').warnings.length) {
+      newContent += Em.I18n.t('installer.step3.hostWarningsPopup.report.user');
+      newContent += content.findProperty('category', 'user').warnings.mapProperty('name').join(' ');
+    }
+    newContent += '</p>';
+    return newContent;
+  }.property('content', 'warningsByHost'),
+
+  didInsertElement: function () {
+    Em.run.next(this, function () {
+      App.tooltip(this.$("[rel='HostsListTooltip']"), {html: true, placement: "right"});
+      App.tooltip(this.$('#process .warning-name'), {html: true, placement: "top"});
+    });
+  }.observes('content'),
+
+  /**
+   * Show popup with selected hostnames
+   * @param {object} hosts
+   * @method showHostsPopup
+   */
+  showHostsPopup: function (hosts) {
+    $('.tooltip').hide();
+    App.ModalPopup.show({
+      header: Em.I18n.t('installer.step3.hostWarningsPopup.allHosts'),
+      bodyClass: Em.View.extend({
+        hosts: hosts.context,
+        template: Em.Handlebars.compile('<ul>{{#each host in view.hosts}}<li>{{host}}</li>{{/each}}</ul>')
+      }),
+      secondary: null
+    });
+  },
+
+  /**
+   * Open/Close selected category
+   * @param {object} category
+   * @method onToggleBlock
+   */
+  onToggleBlock: function (category) {
+    this.$('#' + category.context.category).toggle('blind', 500);
+    category.context.set("isCollapsed", !category.context.get("isCollapsed"));
+  },
+
+  /**
+   * Generate number of hosts which had warnings, avoid duplicated host names in different warnings.
+   * @method warningHostsNamesCount
+   * @return {number}
+   */
+  warningHostsNamesCount: function () {
+    var hostNameMap = Em.Object.create();
+    var warningsByHost = this.get('bodyController.warningsByHost').slice();
+    warningsByHost.shift();
+    warningsByHost.forEach(function (_host) {
+      if (_host.warnings.length) {
+        hostNameMap[_host.name] = true;
+      }
+    });
+    var repoCategoryWarnings = this.get('bodyController.repoCategoryWarnings');
+    var diskCategoryWarnings = this.get('bodyController.diskCategoryWarnings');
+
+    if (repoCategoryWarnings.length) {
+      repoCategoryWarnings[0].hostsNames.forEach(function (_hostName) {
+        if (!hostNameMap[_hostName]) {
+          hostNameMap[_hostName] = true;
+        }
+      });
+    }
+    if (diskCategoryWarnings.length) {
+      diskCategoryWarnings[0].hostsNames.forEach(function (_hostName) {
+        if (!hostNameMap[_hostName]) {
+          hostNameMap[_hostName] = true;
+        }
+      });
+    }
+    return Em.keys(hostNameMap).length;
+  },
+
+  /**
+   * Open new browser tab with detailed content
+   * @method openWarningsInDialog
+   */
+  openWarningsInDialog: function () {
+    var newWindow = window.open('');
+    var newDocument = newWindow.document;
+    newDocument.write(this.get('contentInDetails'));
+    newWindow.focus();
+  }
+
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/5ef841a0/ambari-web/app/views/wizard/step3/hostWarningPopupFooter_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/wizard/step3/hostWarningPopupFooter_view.js b/ambari-web/app/views/wizard/step3/hostWarningPopupFooter_view.js
new file mode 100644
index 0000000..cc15313
--- /dev/null
+++ b/ambari-web/app/views/wizard/step3/hostWarningPopupFooter_view.js
@@ -0,0 +1,64 @@
+/**
+ * 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');
+
+App.WizardStep3HostWarningPopupFooter = Em.View.extend({
+
+  templateName: require('templates/wizard/step3/step3_host_warning_popup_footer'),
+
+  classNames: ['modal-footer', 'host-checks-update'],
+
+  footerControllerBinding: 'App.router.wizardStep3Controller',
+
+  progressWidth: function () {
+    return 'width:' + this.get('footerController.checksUpdateProgress') + '%';
+  }.property('footerController.checksUpdateProgress'),
+
+  isUpdateInProgress: function () {
+    return (this.get('footerController.checksUpdateProgress') > 0) &&
+      (this.get('footerController.checksUpdateProgress') < 100);
+  }.property('footerController.checksUpdateProgress'),
+
+  updateStatusClass: function () {
+    var status = this.get('footerController.checksUpdateStatus');
+    if (status === 'SUCCESS') {
+      return 'text-success';
+    }
+    else {
+      if (status === 'FAILED') {
+        return 'text-error';
+      }
+      else {
+        return null;
+      }
+    }
+  }.property('footerController.checksUpdateStatus'),
+
+  updateStatus: function () {
+    var status = this.get('footerController.checksUpdateStatus');
+    if (status === 'SUCCESS') {
+      return Em.I18n.t('installer.step3.warnings.updateChecks.success');
+    } else if (status === 'FAILED') {
+      return Em.I18n.t('installer.step3.warnings.updateChecks.failed');
+    } else {
+      return null;
+    }
+  }.property('footerController.checksUpdateStatus')
+
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/5ef841a0/ambari-web/app/views/wizard/step3_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/wizard/step3_view.js b/ambari-web/app/views/wizard/step3_view.js
index 278f8dc..f193ff7 100644
--- a/ambari-web/app/views/wizard/step3_view.js
+++ b/ambari-web/app/views/wizard/step3_view.js
@@ -23,30 +23,95 @@ App.WizardStep3View = App.TableView.extend({
 
   templateName: require('templates/wizard/step3'),
 
+  /**
+   * List of hosts
+   * Same to <code>controller.hosts</code>
+   * @type {Ember.Enumerable}
+   */
   content:function () {
     return this.get('controller.hosts');
   }.property('controller.hosts.length'),
 
+  /**
+   * Message with info about registration result
+   * @type {string}
+   */
   message:'',
+
+  /**
+   * Text to link for hosts' warnings popup
+   * @type {string}
+   */
   linkText: '',
+
+  /**
+   * Registration status
+   * @type {string}
+   */
   status: '',
 
+  /**
+   * Active category
+   * @type {string}
+   */
   selectedCategory: function() {
     return this.get('categories').findProperty('isActive');
   }.property('categories.@each.isActive'),
 
+  /**
+   * Message about other registered hosts (not included in current registration)
+   * @type {string}
+   */
   registeredHostsMessage: '',
 
+  /**
+   * Number of visible hosts on page
+   * @type {string}
+   */
   displayLength: "25",
 
+  /**
+   * All checkboxes on page are checked
+   * @type {bool}
+   */
+  pageChecked: false,
+
+  /**
+   * bootStatus category object
+   * @type {Ember.Object}
+   */
+  categoryObject: Em.Object.extend({
+    hostsCount: 0,
+    label: function () {
+      return "%@ (%@)".fmt(this.get('value'), this.get('hostsCount'));
+    }.property('value', 'hostsCount'),
+    isActive: false,
+    itemClass: function () {
+      return this.get('isActive') ? 'active' : '';
+    }.property('isActive')
+  }),
+
+  /**
+   * List of bootStatus categories
+   * @type {categoryObject[]}
+   */
+  categories: function () {
+    return [
+      this.categoryObject.create({value: Em.I18n.t('common.all'), hostsBootStatus: 'ALL', isActive: true}),
+      this.categoryObject.create({value: Em.I18n.t('installer.step3.hosts.status.installing'), hostsBootStatus: 'RUNNING'}),
+      this.categoryObject.create({value: Em.I18n.t('installer.step3.hosts.status.registering'), hostsBootStatus: 'REGISTERING'}),
+      this.categoryObject.create({value: Em.I18n.t('common.success'), hostsBootStatus: 'REGISTERED' }),
+      this.categoryObject.create({value: Em.I18n.t('common.fail'), hostsBootStatus: 'FAILED', last: true })
+    ];
+  }.property(),
+
   didInsertElement: function () {
     this.get('controller').loadStep();
   },
 
-  pageChecked: false,
-
   /**
-   * select checkboxes of hosts on page
+   * Select checkboxes of hosts on page
+   * @method onPageChecked
    */
   onPageChecked: function () {
     if (this.get('selectionInProgress')) return;
@@ -54,28 +119,37 @@ App.WizardStep3View = App.TableView.extend({
   }.observes('pageChecked'),
 
   /**
-   * select checkboxes of all hosts
+   * Select checkboxes of all hosts
+   * @method selectAll
    */
   selectAll: function () {
     this.get('content').setEach('isChecked', true);
   },
 
   /**
-   * reset checkbox of all hosts
+   * Reset checkbox of all hosts
+   * @method unSelectAll
    */
   unSelectAll: function() {
     this.get('content').setEach('isChecked', false);
   },
 
+  /**
+   * Call <code>watchSelection</code> only once
+   * @method watchSelectionOnce
+   */
   watchSelectionOnce: function () {
     Em.run.once(this, 'watchSelection');
   }.observes('content.@each.isChecked', 'pageContent'),
 
   /**
-   * watch selection and calculate such flags as:
-   * - noHostsSelected
-   * - selectedHostsCount
-   * - pageChecked
+   * Watch selection and calculate such flags as:
+   * <ul>
+   *  <li>noHostsSelected</li>
+   *  <li>selectedHostsCount</li>
+   *  <li>pageChecked</li>
+   * </ul>
+   * @method watchSelection
    */
   watchSelection: function() {
     this.set('selectionInProgress', true);
@@ -84,44 +158,35 @@ App.WizardStep3View = App.TableView.extend({
     var noHostsSelected = true;
     var selectedHostsCount = 0;
     this.get('content').forEach(function(host){
-      selectedHostsCount += ~~host.get('isChecked');
+      selectedHostsCount += host.get('isChecked') ? 1 : 0;
       noHostsSelected = (noHostsSelected) ? !host.get('isChecked') : noHostsSelected;
     });
     this.set('noHostsSelected', noHostsSelected);
     this.set('selectedHostsCount', selectedHostsCount);
   },
 
+  /**
+   * Update <code>registeredHostsMessage</code> according to <code>controller.registeredHots.length</code>
+   * @method setRegisteredHosts
+   */
   setRegisteredHosts: function(){
     this.set('registeredHostsMessage',Em.I18n.t('installer.step3.warning.registeredHosts').format(this.get('controller.registeredHosts').length));
   }.observes('controller.registeredHosts'),
 
-  categoryObject: Em.Object.extend({
-    hostsCount: 0,
-    label: function () {
-      return "%@ (%@)".fmt(this.get('value'), this.get('hostsCount'));
-    }.property('value', 'hostsCount'),
-    isActive: false,
-    itemClass: function () {
-      return this.get('isActive') ? 'active' : '';
-    }.property('isActive')
-  }),
-
-  categories: function () {
-    return [
-      this.categoryObject.create({value: Em.I18n.t('common.all'), hostsBootStatus: 'ALL', isActive: true}),
-      this.categoryObject.create({value: Em.I18n.t('installer.step3.hosts.status.installing'), hostsBootStatus: 'RUNNING'}),
-      this.categoryObject.create({value: Em.I18n.t('installer.step3.hosts.status.registering'), hostsBootStatus: 'REGISTERING'}),
-      this.categoryObject.create({value: Em.I18n.t('common.success'), hostsBootStatus: 'REGISTERED' }),
-      this.categoryObject.create({value: Em.I18n.t('common.fail'), hostsBootStatus: 'FAILED', last: true })
-    ];
-  }.property(),
-
+  /**
+   * Call filters and counters one time
+   * @method hostBootStatusObserver
+   */
   hostBootStatusObserver: function(){
-    Ember.run.once(this, 'countCategoryHosts');
-    Ember.run.once(this, 'filter');
-    Ember.run.once(this, 'monitorStatuses');
+    Em.run.once(this, 'countCategoryHosts');
+    Em.run.once(this, 'filter');
+    Em.run.once(this, 'monitorStatuses');
   }.observes('content.@each.bootStatus'),
 
+  /**
+   * Calculate host count grouped by <code>bootStatus</code>
+   * @method countCategoryHosts
+   */
   countCategoryHosts: function () {
     var counters = {
       "RUNNING": 0,
@@ -130,7 +195,7 @@ App.WizardStep3View = App.TableView.extend({
       "FAILED": 0
     };
     this.get('content').forEach(function (host) {
-      if (counters[host.get('bootStatus')] !== undefined) {
+      if (!Em.isNone(counters[host.get('bootStatus')])) {
         counters[host.get('bootStatus')]++;
       }
     }, this);
@@ -142,7 +207,8 @@ App.WizardStep3View = App.TableView.extend({
 
 
   /**
-   * filter hosts by category
+   * Filter hosts by category
+   * @method filter
    */
   filter: function () {
     var self = this;
@@ -157,13 +223,14 @@ App.WizardStep3View = App.TableView.extend({
       self.set('filteredContent', result);
     });
   }.observes('selectedCategory'),
+
   /**
    * Trigger on Category click
    * @param {Object} event
+   * @method selectCategory
    */
   selectCategory: function (event) {
     var categoryStatus = event.context.get('hostsBootStatus');
-    var self = this;
     this.get('categories').forEach(function (category) {
       category.set('isActive', (category.get('hostsBootStatus') === categoryStatus));
     });
@@ -173,6 +240,7 @@ App.WizardStep3View = App.TableView.extend({
   /**
    * Select "All" hosts category
    * run registration of failed hosts again
+   * @method retrySelectedHosts
    */
   retrySelectedHosts: function () {
     var eventObject = {context: Em.Object.create({hostsBootStatus: 'ALL'})};
@@ -180,6 +248,10 @@ App.WizardStep3View = App.TableView.extend({
     this.get('controller').retrySelectedHosts();
   },
 
+  /**
+   * Update <code>status</code>, <code>linkText</code>, <code>message</code> according to hosts statuses
+   * @method monitorStatuses
+   */
   monitorStatuses: function() {
     var hosts = this.get('controller.bootHosts');
     var failedHosts = hosts.filterProperty('bootStatus', 'FAILED').length;
@@ -188,56 +260,81 @@ App.WizardStep3View = App.TableView.extend({
       this.set('status', 'alert-warn');
       this.set('linkText', '');
       this.set('message', Em.I18n.t('installer.step3.warnings.missingHosts'));
-    } else if (!this.get('controller.isWarningsLoaded')) {
-      this.set('status', 'alert-info');
-      this.set('linkText', '');
-      this.set('message', Em.I18n.t('installer.step3.warning.loading'));
-    } else if (this.get('controller.isHostHaveWarnings') || this.get('controller.repoCategoryWarnings.length') || this.get('controller.diskCategoryWarnings.length')) {
-      this.set('status', 'alert-warn');
-      this.set('linkText', Em.I18n.t('installer.step3.warnings.linkText'));
-      this.set('message', Em.I18n.t('installer.step3.warnings.fails').format(hosts.length - failedHosts));
-    } else {
-      this.set('status', 'alert-success');
-      this.set('linkText', Em.I18n.t('installer.step3.noWarnings.linkText'));
-      if (failedHosts == 0) {
-        // all are ok
-        this.set('message', Em.I18n.t('installer.step3.warnings.noWarnings').format(hosts.length));
-      } else if (failedHosts == hosts.length) {
-        // all failed
-        this.set('status', 'alert-warn');
+    }
+    else {
+      if (!this.get('controller.isWarningsLoaded')) {
+        this.set('status', 'alert-info');
         this.set('linkText', '');
-        this.set('message', Em.I18n.t('installer.step3.warnings.allFailed').format(failedHosts));
-      } else {
-        // some failed
-        this.set('message', Em.I18n.t('installer.step3.warnings.someWarnings').format((hosts.length - failedHosts), failedHosts));
+        this.set('message', Em.I18n.t('installer.step3.warning.loading'));
+      }
+      else {
+        if (this.get('controller.isHostHaveWarnings') || this.get('controller.repoCategoryWarnings.length') || this.get('controller.diskCategoryWarnings.length')) {
+          this.set('status', 'alert-warn');
+          this.set('linkText', Em.I18n.t('installer.step3.warnings.linkText'));
+          this.set('message', Em.I18n.t('installer.step3.warnings.fails').format(hosts.length - failedHosts));
+        }
+        else {
+          this.set('status', 'alert-success');
+          this.set('linkText', Em.I18n.t('installer.step3.noWarnings.linkText'));
+          if (failedHosts == 0) {
+            // all are ok
+            this.set('message', Em.I18n.t('installer.step3.warnings.noWarnings').format(hosts.length));
+          }
+          else {
+            if (failedHosts == hosts.length) {
+              // all failed
+              this.set('status', 'alert-warn');
+              this.set('linkText', '');
+              this.set('message', Em.I18n.t('installer.step3.warnings.allFailed').format(failedHosts));
+            }
+            else {
+              // some failed
+              this.set('message', Em.I18n.t('installer.step3.warnings.someWarnings').format((hosts.length - failedHosts), failedHosts));
+            }
+          }
+        }
       }
     }
   }.observes('controller.isWarningsLoaded', 'controller.isHostHaveWarnings', 'controller.repoCategoryWarnings', 'controller.diskCategoryWarnings')
+
 });
 
 //todo: move it inside WizardStep3View
 App.WizardHostView = Em.View.extend({
 
   tagName: 'tr',
+
   classNameBindings: ['hostInfo.bootStatus'],
+
+  /**
+   * Host from parent view
+   * @type {Object}
+   */
   hostInfo: null,
 
+  /**
+   * @type {bool}
+   */
+  isRetryable: function() {
+    // return ['FAILED'].contains(this.get('hostInfo.bootStatus'));
+    return false;
+  }.property('hostInfo.bootStatus'),
+
+  /**
+   * Remove selected host
+   * @method remove
+   */
   remove: function () {
     this.get('controller').removeHost(this.get('hostInfo'));
   },
 
+  /**
+   * Retry register selected host
+   * @method retry
+   */
   retry: function() {
     this.get('controller').retryHost(this.get('hostInfo'));
-  },
-
-  isRemovable: function () {
-    return true;
-  }.property(),
-
-  isRetryable: function() {
-    // return ['FAILED'].contains(this.get('hostInfo.bootStatus'));
-    return false;
-  }.property('hostInfo.bootStatus')
+  }
 
 });
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/5ef841a0/ambari-web/test/controllers/wizard/stack_upgrade/step3_controller_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/wizard/stack_upgrade/step3_controller_test.js b/ambari-web/test/controllers/wizard/stack_upgrade/step3_controller_test.js
index e30a820..092415e 100644
--- a/ambari-web/test/controllers/wizard/stack_upgrade/step3_controller_test.js
+++ b/ambari-web/test/controllers/wizard/stack_upgrade/step3_controller_test.js
@@ -23,11 +23,13 @@ var Ember = require('ember');
 require('models/host');
 require('controllers/wizard/stack_upgrade/step3_controller');
 
-App.router = Ember.Object.create({
-  stackUpgradeController: Ember.Object.create({
-    save: function(val) {}
-  })
-});
+if (!App.router) {
+  App.router = Em.Object.create({});
+}
+
+App.router.set('stackUpgradeController', Em.Object.create({
+  save: Em.K
+}));
 
 describe('App.StackUpgradeStep3Controller', function() {
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/5ef841a0/ambari-web/test/installer/step3_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/installer/step3_test.js b/ambari-web/test/installer/step3_test.js
index 9818f40..1525080 100644
--- a/ambari-web/test/installer/step3_test.js
+++ b/ambari-web/test/installer/step3_test.js
@@ -19,12 +19,20 @@
 
 var Ember = require('ember');
 var App = require('app');
+var c;
 require('utils/http_client');
 require('models/host');
 require('controllers/wizard/step3_controller');
 
 describe('App.WizardStep3Controller', function () {
 
+  beforeEach(function() {
+    c = App.WizardStep3Controller.create({
+      wizardController: App.InstallerController.create(),
+      disablePreviousSteps: Em.K
+    });
+  });
+
   describe('#getAllRegisteredHostsCallback', function () {
     it('One host is already in the cluster, one host is registered', function() {
       var controller = App.WizardStep3Controller.create({
@@ -114,129 +122,6 @@ describe('App.WizardStep3Controller', function () {
 
   });
 
-  var tests = [
-    {
-      bootHosts: [
-        Em.Object.create({name:'wst3_host1', bootStatus: 'REGISTERED', isChecked: false}),
-        Em.Object.create({name:'wst3_host2', bootStatus: 'REGISTERING', isChecked: false})
-      ],
-      m: 'One registered, one registering',
-      visibleHosts: {
-        RUNNING: {
-          e: 0
-        },
-        REGISTERING: {
-          e: 1
-        },
-        REGISTERED: {
-          e: 1
-        },
-        FAILED: {
-          e: 0
-        }
-      },
-      onAllChecked: {
-        e: [true, true]
-      }
-    },
-    {
-      bootHosts: [
-        Em.Object.create({name:'wst3_host1', bootStatus: 'REGISTERED', isChecked: false}),
-        Em.Object.create({name:'wst3_host2', bootStatus: 'REGISTERED', isChecked: false})
-      ],
-      m: 'Two registered',
-      visibleHosts: {
-        RUNNING: {
-          e: 0
-        },
-        REGISTERING: {
-          e: 0
-        },
-        REGISTERED: {
-          e: 2
-        },
-        FAILED: {
-          e: 0
-        }
-      },
-      onAllChecked: {
-        e: [true, true]
-      }
-    },
-    {
-      bootHosts: [
-        Em.Object.create({name:'wst3_host1', bootStatus: 'FAILED', isChecked: false}),
-        Em.Object.create({name:'wst3_host2', bootStatus: 'REGISTERED', isChecked: false})
-      ],
-      m: 'One registered, one failed',
-      visibleHosts: {
-        RUNNING: {
-          e: 0
-        },
-        REGISTERING: {
-          e: 0
-        },
-        REGISTERED: {
-          e: 1
-        },
-        FAILED: {
-          e: 1
-        }
-      },
-      onAllChecked: {
-        e: [true, true]
-      }
-    },
-    {
-      bootHosts: [
-        Em.Object.create({name:'wst3_host1', bootStatus: 'FAILED', isChecked: false}),
-        Em.Object.create({name:'wst3_host2', bootStatus: 'FAILED', isChecked: false})
-      ],
-      m: 'Two failed',
-      visibleHosts: {
-        RUNNING: {
-          e: 0
-        },
-        REGISTERING: {
-          e: 0
-        },
-        REGISTERED: {
-          e: 0
-        },
-        FAILED: {
-          e: 2
-        }
-      },
-      onAllChecked: {
-        e: [true, true]
-      }
-    },
-    {
-      bootHosts: [
-        Em.Object.create({name:'wst3_host1', bootStatus: 'REGISTERING', isChecked: false}),
-        Em.Object.create({name:'wst3_host2', bootStatus: 'REGISTERING', isChecked: false})
-      ],
-      m: 'Two registering',
-      visibleHosts: {
-        RUNNING: {
-          e: 0
-        },
-        REGISTERING: {
-          e: 2
-        },
-        REGISTERED: {
-          e: 0
-        },
-        FAILED: {
-          e: 0
-        }
-      },
-      onAllChecked: {
-        e: [true, true]
-      }
-    }
-  ];
-
   describe('#registrationTimeoutSecs', function() {
     it('Manual install', function() {
       var controller = App.WizardStep3Controller.create({
@@ -281,4 +166,1147 @@ describe('App.WizardStep3Controller', function () {
       });
     });
   });
+
+  describe('#isWarningsBoxVisible', function() {
+    it('for testMode should be always true', function() {
+      App.testMode = true;
+      expect(c.get('isWarningsBoxVisible')).to.equal(true);
+      App.testMode = false;
+    });
+    it('for "real" mode should be based on isRegistrationInProgress', function() {
+      c.set('disablePreviousSteps', Em.K);
+      App.testMode = false;
+      c.set('isRegistrationInProgress', false);
+      expect(c.get('isWarningsBoxVisible')).to.equal(true);
+      c.set('isRegistrationInProgress', true);
+      expect(c.get('isWarningsBoxVisible')).to.equal(false);
+      App.testMode = true;
+    });
+  });
+
+  describe('#clearStep', function() {
+    it('should clear hosts', function() {
+      c.set('hosts', [{}, {}]);
+      c.clearStep();
+      expect(c.get('hosts')).to.eql([]);
+    });
+    it('should clear bootHosts', function() {
+      c.set('bootHosts', [{}, {}]);
+      c.clearStep();
+      expect(c.get('bootHosts').length).to.equal(0);
+    });
+    it('should set stopBootstrap to false', function() {
+      c.set('stopBootstrap', true);
+      c.clearStep();
+      expect(c.get('stopBootstrap')).to.equal(false);
+    });
+    it('should set wizardController DBProperty bootStatus to false', function() {
+      c.get('wizardController').setDBProperty('bootStatus', true);
+      c.clearStep();
+      expect(c.get('wizardController').getDBProperty('bootStatus')).to.equal(false);
+    });
+    it('should set isSubmitDisabled to true', function() {
+      c.set('isSubmitDisabled', false);
+      c.clearStep();
+      expect(c.get('isSubmitDisabled')).to.equal(true);
+    });
+    it('should set isSubmitDisabled to true', function() {
+      c.set('isRetryDisabled', false);
+      c.clearStep();
+      expect(c.get('isRetryDisabled')).to.equal(true);
+    });
+  });
+
+  describe('#loadStep', function() {
+    it('should set registrationStartedAt to null', function() {
+      c.set('disablePreviousSteps', Em.K);
+      c.set('registrationStartedAt', {});
+      c.loadStep();
+      expect(c.get('registrationStartedAt')).to.be.null;
+    });
+    it('should set isLoaded to false', function() {
+      c.set('disablePreviousSteps', Em.K);
+      c.set('clearStep', Em.K);
+      c.set('loadHosts', Em.K);
+      c.set('isLoaded', true);
+      c.loadStep();
+      expect(c.get('isLoaded')).to.equal(false);
+    });
+    it('should call clearStep', function() {
+      c.set('disablePreviousSteps', Em.K);
+      c.set('loadHosts', Em.K);
+      sinon.spy(c, 'clearStep');
+      c.loadStep();
+      expect(c.get('clearStep').calledOnce).to.equal(true);
+      c.clearStep.restore();
+    });
+    it('should call loadHosts', function() {
+      c.set('disablePreviousSteps', Em.K);
+      c.set('loadHosts', Em.K);
+      sinon.spy(c, 'loadHosts');
+      c.loadStep();
+      expect(c.get('loadHosts').calledOnce).to.equal(true);
+      c.loadHosts.restore();
+    });
+    it('should call disablePreviousSteps', function() {
+      c.set('disablePreviousSteps', Em.K);
+      c.set('loadHosts', Em.K);
+      sinon.spy(c, 'disablePreviousSteps');
+      c.loadStep();
+      expect(c.get('disablePreviousSteps').calledOnce).to.equal(true);
+      c.disablePreviousSteps.restore();
+    });
+  });
+
+  describe('#loadHosts', function() {
+    it('should set isLoaded to true', function() {
+      c.set('navigateStep', Em.K);
+      c.set('content', {hosts: {}});
+      c.loadHosts();
+      expect(c.get('isLoaded')).to.equal(true);
+    });
+    it('should set bootStatus REGISTERED on testMode', function() {
+      App.testMode = true;
+      c.set('navigateStep', Em.K);
+      c.set('content', {hosts: {c: {name: 'name'}}});
+      c.loadHosts();
+      expect(c.get('hosts').everyProperty('bootStatus', 'REGISTERED')).to.equal(true);
+    });
+    it('should set bootStatus DONE on "real" mode and when installOptions.manualInstall is selected', function() {
+      App.testMode = false;
+      c.set('navigateStep', Em.K);
+      c.set('content', {installOptions:{manualInstall: true}, hosts: {c: {name: 'name'}}});
+      c.loadHosts();
+      expect(c.get('hosts').everyProperty('bootStatus', 'DONE')).to.equal(true);
+      App.testMode = true;
+    });
+    it('should set bootStatus PENDING on "real" mode and when installOptions.manualInstall is not selected', function() {
+      App.testMode = false;
+      c.set('navigateStep', Em.K);
+      c.set('content', {installOptions:{manualInstall: false}, hosts: {c: {name: 'name'}}});
+      c.loadHosts();
+      expect(c.get('hosts').everyProperty('bootStatus', 'PENDING')).to.equal(true);
+      App.testMode = true;
+    });
+    it('should set bootStatus PENDING on "real" mode and when installOptions.manualInstall is not selected', function() {
+      c.set('navigateStep', Em.K);
+      c.set('content', {hosts: {c: {name: 'name'}, d: {name: 'name1'}}});
+      c.loadHosts();
+      expect(c.get('hosts').everyProperty('isChecked', false)).to.equal(true);
+    });
+  });
+
+  describe('#parseHostInfo', function() {
+
+    var tests = Em.A([
+      {
+        bootHosts: Em.A([
+          Em.Object.create({name: 'c1', bootStatus: 'REGISTERED', bootLog: ''}),
+          Em.Object.create({name: 'c2', bootStatus: 'REGISTERING', bootLog: ''}),
+          Em.Object.create({name: 'c3', bootStatus: 'RUNNING', bootLog: ''})
+        ]),
+        hostsStatusFromServer: Em.A([
+          {hostName: 'c1', status: 'REGISTERED', log: 'c1'},
+          {hostName: 'c2', status: 'REGISTERED', log: 'c2'},
+          {hostName: 'c3', status: 'RUNNING', log: 'c3'}
+        ]),
+        m: 'bootHosts not empty, hostsStatusFromServer not empty, one is RUNNING',
+        e: {
+          c: true,
+          r: true
+        }
+      },
+      {
+        bootHosts: Em.A([]),
+        hostsStatusFromServer: Em.A([
+          {hostName: 'c1', status: 'REGISTERED', log: 'c1'},
+          {hostName: 'c2', status: 'REGISTERED', log: 'c2'},
+          {hostName: 'c3', status: 'RUNNING', log: 'c3'}
+        ]),
+        m: 'bootHosts is empty',
+        e: {
+          c: false,
+          r: false
+        }
+      },
+      {
+        bootHosts: Em.A([
+          Em.Object.create({name: 'c1', bootStatus: 'REGISTERED', bootLog: ''}),
+          Em.Object.create({name: 'c2', bootStatus: 'REGISTERING', bootLog: ''}),
+          Em.Object.create({name: 'c3', bootStatus: 'REGISTERED', bootLog: ''})
+        ]),
+        hostsStatusFromServer: Em.A([
+          {hostName: 'c1', status: 'REGISTERED', log: 'c1'},
+          {hostName: 'c2', status: 'REGISTERED', log: 'c2'},
+          {hostName: 'c3', status: 'REGISTERED', log: 'c3'}
+        ]),
+        m: 'bootHosts not empty, hostsStatusFromServer not empty, no one is RUNNING',
+        e: {
+          c: true,
+          r: false
+        }
+      }
+    ]);
+
+    tests.forEach(function(test) {
+      it(test.m, function() {
+        c.set('bootHosts', test.bootHosts);
+        var r = c.parseHostInfo(test.hostsStatusFromServer);
+        expect(r).to.equal(test.e.r);
+        if (test.e.c) {
+          test.hostsStatusFromServer.forEach(function(h) {
+            var r = c.get('bootHosts').findProperty('name', h.hostName);
+            if (!['REGISTERED', 'REGISTERING'].contains(r.get('bootStatus'))) {
+              expect(r.get('bootStatus')).to.equal(h.status);
+              expect(r.get('bootLog')).to.equal(h.log);
+            }
+          });
+        }
+      });
+    });
+  });
+
+  describe('#removeHosts', function() {
+    it('should call App.showConfirmationPopup', function() {
+      sinon.spy(App, 'showConfirmationPopup');
+      c.removeHosts(Em.A([]));
+      expect(App.showConfirmationPopup.calledOnce).to.equal(true);
+      App.showConfirmationPopup.restore();
+    });
+    it('primary should disable Submit if no more hosts', function() {
+      var hosts = [{}];
+      c.set('hosts', hosts);
+      var popup = c.removeHosts(hosts);
+      popup.onPrimary();
+      expect(c.get('isSubmitDisabled')).to.equal(true);
+    });
+  });
+
+  describe('#removeHost', function() {
+    it('should call removeHosts with array as arg', function() {
+      var host = {a:''};
+      sinon.spy(c, 'removeHosts');
+      c.removeHost(host);
+      expect(c.removeHosts.calledWith([host]));
+      c.removeHosts.restore();
+    });
+  });
+
+  describe('#removeSelectedHosts', function() {
+    it('should remove selected hosts', function() {
+      c = App.WizardStep3Controller.create({
+        wizardController: App.InstallerController.create(),
+        hosts: [
+          {isChecked: true, name: 'c1'},
+          {isChecked: false, name: 'c2'}
+        ]
+      });
+      c.removeSelectedHosts().onPrimary();
+      expect(c.get('hosts').mapProperty('name')).to.eql(['c2']);
+    });
+  });
+
+  describe('#selectedHostsPopup', function() {
+    it('should show App.ModalPopup', function() {
+      sinon.spy(App.ModalPopup, 'show');
+      c.selectedHostsPopup();
+      expect(App.ModalPopup.show.calledOnce).to.equal(true);
+      App.ModalPopup.show.restore();
+    });
+  });
+
+  describe('#retryHosts', function() {
+    it('should set numPolls to 0', function() {
+      var s = sinon.stub(App.router, 'get', function() {
+        return {launchBootstrap: Em.K}
+      });
+      c.set('content', {installOptions: {}});
+      c.set('doBootstrap', Em.K);
+      c.set('numPolls', 123);
+      c.retryHosts(Em.A([]));
+      expect(c.get('numPolls')).to.equal(0);
+      s.restore();
+    });
+    it('should set registrationStartedAt to null', function() {
+      var s = sinon.stub(App.router, 'get', function() {
+        return {launchBootstrap: Em.K}
+      });
+      c.set('content', {installOptions: {}});
+      c.set('doBootstrap', Em.K);
+      c.retryHosts(Em.A([]));
+      expect(c.get('registrationStartedAt')).to.be.null;
+      s.restore();
+    });
+    it('should startRegistration if installOptions.manualInstall is true', function() {
+      var s = sinon.stub(App.router, 'get', function() {
+        return {launchBootstrap: Em.K}
+      });
+      sinon.spy(c, 'startRegistration');
+      c.set('content', {installOptions: {manualInstall: true}});
+      c.set('doBootstrap', Em.K);
+      c.retryHosts(Em.A([]));
+      expect(c.startRegistration.calledOnce).to.equal(true);
+      s.restore();
+      c.startRegistration.restore();
+    });
+    it('should doBootstrap if installOptions.manualInstall is false', function() {
+      var s = sinon.stub(App.router, 'get', function() {
+        return {launchBootstrap: Em.K}
+      });
+      c.set('content', {installOptions: {manualInstall: false}});
+      c.set('doBootstrap', Em.K);
+      sinon.spy(c, 'doBootstrap');
+      c.retryHosts(Em.A([]));
+      expect(c.doBootstrap.calledOnce).to.equal(true);
+      s.restore();
+      c.doBootstrap.restore();
+    });
+  });
+
+  describe('#retryHost', function() {
+    it('should callretryHosts with array as arg', function() {
+      var host = {n: 'c'}, s = sinon.stub(App.router, 'get', function() {
+        return {launchBootstrap: Em.K}
+      });
+      sinon.spy(c, 'retryHosts');
+      c.set('content', {installOptions: {}});
+      c.set('doBootstrap', Em.K);
+      c.retryHost(host);
+      expect(c.retryHosts.calledWith([host])).to.equal(true);
+      c.retryHosts.restore();
+      s.restore();
+    });
+  });
+
+  describe('#retrySelectedHosts', function() {
+    it('shouldn\'t do nothing if isRetryDisabled is true', function() {
+      c.set('isRetryDisabled', true);
+      sinon.spy(c, 'retryHosts');
+      c.retrySelectedHosts();
+      expect(c.retryHosts.called).to.equal(false);
+      c.retryHosts.restore();
+    });
+    it('should retry hosts with FAILED bootStatus and set isRetryDisabled to true', function() {
+      var s = sinon.stub(App.router, 'get', function() {
+        return {launchBootstrap: Em.K}
+      });
+      c = App.WizardStep3Controller.create({
+        wizardController: App.InstallerController.create(),
+        bootHosts: Em.A([{name: 'c1', bootStatus: 'FAILED'}, {name: 'c2', bootStatus: 'REGISTERED'}]),
+        content: {installOptions: {}},
+        doBootstrap: Em.K
+      });
+      sinon.spy(c, 'retryHosts');
+      c.retrySelectedHosts();
+      expect(c.retryHosts.calledWith([{name: 'c1', bootStatus: 'RUNNING'}]));
+      expect(c.get('isRetryDisabled')).to.equal(true);
+      c.retryHosts.restore();
+      s.restore();
+    });
+  });
+
+  describe('#startBootstrap', function() {
+    it('should drop numPolls and registrationStartedAt', function() {
+      c.set('numPolls', 123);
+      c.set('registrationStartedAt', 1234);
+      c.set('doBootstrap', Em.K);
+      c.startBootstrap();
+      expect(c.get('numPolls')).to.equal(0);
+      expect(c.get('registrationStartedAt')).to.be.null;
+    });
+    it('should drop numPolls and registrationStartedAt', function() {
+      var hosts = Em.A([{name: 'c1'}, {name: 'c2'}]);
+      c = App.WizardStep3Controller.create({
+        wizardController: App.InstallerController.create(),
+        doBootstrap: Em.K,
+        setRegistrationInProgressOnce: Em.K,
+        hosts: hosts
+      });
+      c.startBootstrap();
+      expect(c.get('bootHosts').mapProperty('name')).to.eql(['c1','c2']);
+    });
+  });
+
+  describe('#setRegistrationInProgressOnce', function() {
+    it('should call Ember.run.once with "setRegistrationInProgress"', function() {
+      sinon.spy(Em.run, 'once');
+      c.setRegistrationInProgressOnce();
+      expect(Em.run.once.firstCall.args[1]).to.equal('setRegistrationInProgress');
+      Em.run.once.restore();
+    });
+  });
+
+  describe('#setRegistrationInProgress', function() {
+    var tests = Em.A([
+      {
+        bootHosts: [],
+        isLoaded: false,
+        e: true,
+        m: 'no bootHosts and isLoaded is false'
+      },
+      {
+        bootHosts: [],
+        isLoaded: true,
+        e: false,
+        m: 'no bootHosts and isLoaded is true'
+      },
+      {
+        bootHosts: [
+          Em.Object.create({bootStatus: 'RUNNING'}),
+          Em.Object.create({bootStatus: 'RUNNING'})
+        ],
+        isLoaded: true,
+        e: false,
+        m: 'bootHosts without REGISTERED/FAILED and isLoaded is true'
+      },
+      {
+        bootHosts: [
+          Em.Object.create({bootStatus: 'RUNNING'}),
+          Em.Object.create({bootStatus: 'RUNNING'})
+        ],
+        isLoaded: false,
+        e: true,
+        m: 'bootHosts without REGISTERED/FAILED and isLoaded is false'
+      },
+      {
+        bootHosts: [
+          Em.Object.create({bootStatus: 'REGISTERED'}),
+          Em.Object.create({bootStatus: 'RUNNING'})
+        ],
+        isLoaded: false,
+        e: true,
+        m: 'bootHosts with one REGISTERED and isLoaded is false'
+      },
+      {
+        bootHosts: [
+          Em.Object.create({bootStatus: 'FAILED'}),
+          Em.Object.create({bootStatus: 'RUNNING'})
+        ],
+        isLoaded: false,
+        e: true,
+        m: 'bootHosts with one FAILED and isLoaded is false'
+      },
+      {
+        bootHosts: [
+          Em.Object.create({bootStatus: 'REGISTERED'}),
+          Em.Object.create({bootStatus: 'RUNNING'})
+        ],
+        isLoaded: true,
+        e: false,
+        m: 'bootHosts with one REGISTERED and isLoaded is true'
+      },
+      {
+        bootHosts: [
+          Em.Object.create({bootStatus: 'FAILED'}),
+          Em.Object.create({bootStatus: 'RUNNING'})
+        ],
+        isLoaded: true,
+        e: false,
+        m: 'bootHosts with one FAILED and isLoaded is true'
+      }
+    ]);
+
+    tests.forEach(function(test) {
+      it(test.m, function() {
+        sinon.stub(c, 'disablePreviousSteps', Em.K);
+        c.set('bootHosts', test.bootHosts);
+        c.set('isLoaded', test.isLoaded);
+        c.setRegistrationInProgress();
+        expect(c.get('isRegistrationInProgress')).to.equal(test.e);
+        c.disablePreviousSteps.restore();
+      });
+    });
+  });
+
+  describe('#doBootstrap', function() {
+    beforeEach(function() {
+      sinon.spy(App.ajax, 'send');
+    });
+    afterEach(function() {
+      App.ajax.send.restore();
+    });
+    it('shouldn\'t do nothing if stopBootstrap is true', function() {
+      c.set('stopBootstrap', true);
+      c.doBootstrap();
+      expect(App.ajax.send.called).to.equal(false);
+    });
+    it('should increment numPolls if stopBootstrap is false', function() {
+      c.set('stopBootstrap', false);
+      c.set('numPolls', 0);
+      c.doBootstrap();
+      expect(c.get('numPolls')).to.equal(1);
+    });
+    it('should do ajax call if stopBootstrap is false', function() {
+      c.set('stopBootstrap', false);
+      c.doBootstrap();
+      expect(App.ajax.send.called).to.equal(true);
+    });
+  });
+
+  describe('#startRegistration', function() {
+    it('shouldn\'t do nothing if registrationStartedAt isn\'t null', function() {
+      c.set('registrationStartedAt', 1234);
+      sinon.spy(c, 'isHostsRegistered');
+      c.startRegistration();
+      expect(c.isHostsRegistered.called).to.equal(false);
+      expect(c.get('registrationStartedAt')).to.equal(1234);
+      c.isHostsRegistered.restore();
+    });
+    it('shouldn\'t do nothing if registrationStartedAt isn\'t null', function() {
+      c.set('registrationStartedAt', null);
+      sinon.spy(c, 'isHostsRegistered');
+      c.startRegistration();
+      expect(c.isHostsRegistered.calledOnce).to.equal(true);
+      c.isHostsRegistered.restore();
+    });
+  });
+
+  describe('#isHostsRegistered', function() {
+    beforeEach(function() {
+      sinon.spy(App.ajax, 'send');
+    });
+    afterEach(function() {
+      App.ajax.send.restore();
+    });
+    it('shouldn\'t do nothing if stopBootstrap is true', function() {
+      c.set('stopBootstrap', true);
+      c.isHostsRegistered();
+      expect(App.ajax.send.called).to.equal(false);
+    });
+    it('should do ajax call if stopBootstrap is false', function() {
+      c.set('stopBootstrap', false);
+      c.isHostsRegistered();
+      expect(App.ajax.send.called).to.equal(true);
+
+    });
+  });
+
+  describe('#isHostsRegisteredSuccessCallback', function() {
+    var tests = Em.A([
+      {
+        bootHosts: Em.A([
+          Em.Object.create({bootStatus: 'DONE'})
+        ]),
+        data: {items:[]},
+        m: 'one host DONE',
+        e: {
+          bs: 'REGISTERING',
+          getHostInfoCalled: false
+        }
+      },
+      {
+        bootHosts: Em.A([
+          Em.Object.create({bootStatus: 'REGISTERING', name: 'c1'})
+        ]),
+        data: {items:[{Hosts: {host_name: 'c1'}}]},
+        m: ' one host REGISTERING',
+        e: {
+          bs: 'FAILED',
+          getHostInfoCalled: true
+        }
+      },
+      {
+        bootHosts: Em.A([
+          Em.Object.create({bootStatus: 'REGISTERING', name: 'c1'})
+        ]),
+        data: {items:[{Hosts: {host_name: 'c2'}}]},
+        m: 'one host REGISTERING but data without info about it',
+        e: {
+          bs: 'FAILED',
+          getHostInfoCalled: true
+        }
+      },
+      {
+        bootHosts: Em.A([
+          Em.Object.create({bootStatus: 'RUNNING', name: 'c1'})
+        ]),
+        data: {items:[{Hosts: {host_name: 'c1'}}]},
+        m: ' one host RUNNING',
+        e: {
+          bs: 'RUNNING',
+          getHostInfoCalled: false
+        }
+      }
+    ]);
+    tests.forEach(function(test) {
+      it(test.m, function() {
+        sinon.spy(c, 'getHostInfo');
+        c.set('bootHosts', test.bootHosts);
+        c.isHostsRegisteredSuccessCallback(test.data);
+        expect(c.get('bootHosts')[0].get('bootStatus')).to.equal(test.e.bs);
+        expect(c.getHostInfo.called).to.equal(test.e.getHostInfoCalled);
+        c.getHostInfo.restore();
+      });
+    });
+  });
+
+  describe('#getAllRegisteredHosts', function() {
+    it('should call App.ajax.send', function() {
+      sinon.spy(App.ajax, 'send');
+      c.getAllRegisteredHosts();
+      expect(App.ajax.send.calledOnce).to.equal(true);
+      App.ajax.send.restore();
+    });
+  });
+
+  describe('#getAllRegisteredHostsCallback', function() {
+    var tests = Em.A([
+      {
+        hostsInCluster: ['c3'],
+        bootHosts: [{name:'c1'},{name:'c2'}],
+        hosts: Em.A([
+          {Hosts: {host_name:'c1'}},
+          {Hosts: {host_name:'c2'}}
+        ]),
+        m: 'No registered hosts',
+        e: {
+          hasMoreRegisteredHosts: false,
+          registeredHosts: ''
+        }
+      },
+      {
+        hostsInCluster: ['c4'],
+        bootHosts: [{name:'c3'},{name:'c5'}],
+        hosts: Em.A([
+          {Hosts: {host_name:'c1'}},
+          {Hosts: {host_name:'c2'}}
+        ]),
+        m: '2 registered hosts',
+        e: {
+          hasMoreRegisteredHosts: true,
+          registeredHosts: ['c1','c2']
+        }
+      },
+      {
+        hostsInCluster: ['c4'],
+        bootHosts: [{name:'c1'},{name:'c5'}],
+        hosts: Em.A([
+          {Hosts: {host_name:'c1'}},
+          {Hosts: {host_name:'c2'}}
+        ]),
+        m: '1 registered host',
+        e: {
+          hasMoreRegisteredHosts: true,
+          registeredHosts: ['c2']
+        }
+      },
+      {
+        hostsInCluster: ['c1'],
+        bootHosts: [{name:'c3'},{name:'c5'}],
+        hosts: Em.A([
+          {Hosts: {host_name:'c1'}},
+          {Hosts: {host_name:'c2'}}
+        ]),
+        m: '1 registered host (2)',
+        e: {
+          hasMoreRegisteredHosts: true,
+          registeredHosts: ['c2']
+        }
+      }
+    ]);
+    tests.forEach(function(test) {
+      it(test.m, function() {
+        c.reopen({hostsInCluster: test.hostsInCluster, setRegistrationInProgress: Em.K});
+        c.set('bootHosts', test.bootHosts);
+        c.getAllRegisteredHostsCallback({items:test.hosts});
+        expect(c.get('hasMoreRegisteredHosts')).to.equal(test.e.hasMoreRegisteredHosts);
+        expect(c.get('registeredHosts')).to.eql(test.e.registeredHosts);
+      });
+    });
+  });
+
+  describe('#registerErrPopup', function() {
+    it('should call App.ModalPopup.show', function() {
+      sinon.spy(App.ModalPopup, 'show');
+      c.registerErrPopup();
+      expect(App.ModalPopup.show.calledOnce).to.equal(true);
+      App.ModalPopup.show.restore();
+    });
+  });
+
+  describe('#getHostInfo', function() {
+    it('should do ajax request', function() {
+      sinon.spy(App.ajax, 'send');
+      c.getHostInfo();
+      expect(App.ajax.send.calledOnce).to.equal(true);
+      App.ajax.send.restore();
+    });
+  });
+
+  describe('#getHostInfoErrorCallback', function() {
+    it('should call registerErrPopup', function() {
+      sinon.spy(c, 'registerErrPopup');
+      c.getHostInfoErrorCallback();
+      expect(c.registerErrPopup.calledOnce).to.equal(true);
+      c.registerErrPopup.restore();
+    });
+  });
+
+  describe('#stopRegistration', function() {
+    var tests = Em.A([
+      {
+        bootHosts: [{bootStatus: 'REGISTERED'}, {bootStatus: 'RUNNING'}],
+        e: {isSubmitDisabled: false, isRetryDisabled: true}
+      },
+      {
+        bootHosts: [{bootStatus: 'FAILED'}, {bootStatus: 'RUNNING'}],
+        e: {isSubmitDisabled: true, isRetryDisabled: false}
+      },
+      {
+        bootHosts: [{bootStatus: 'FAILED'}, {bootStatus: 'REGISTERED'}],
+        e: {isSubmitDisabled: false, isRetryDisabled: false}
+      },
+      {
+        bootHosts: [{bootStatus: 'RUNNING'}, {bootStatus: 'RUNNING'}],
+        e: {isSubmitDisabled: true, isRetryDisabled: true}
+      }
+    ]);
+    tests.forEach(function(test) {
+      it(test.bootHosts.mapProperty('bootStatus').join(', '), function() {
+        c.reopen({bootHosts: test.bootHosts});
+        c.stopRegistration();
+        expect(c.get('isSubmitDisabled')).to.equal(test.e.isSubmitDisabled);
+        expect(c.get('isRetryDisabled')).to.equal(test.e.isRetryDisabled);
+      });
+    });
+  });
+
+  describe('#submit', function() {
+    it('if isHostHaveWarnings should show confirmation popup', function() {
+      c.reopen({isHostHaveWarnings: true});
+      sinon.spy(App.ModalPopup, 'show');
+      c.submit();
+      expect(App.ModalPopup.show.calledOnce).to.equal(true);
+      App.ModalPopup.show.restore();
+    });
+    it('if isHostHaveWarnings should show confirmation popup. on Primary should set bootHosts to content.hosts', function() {
+      var bootHosts = [{name: 'c1'}];
+      c.reopen({isHostHaveWarnings: true, bootHosts: bootHosts, hosts: []});
+      c.submit().onPrimary();
+      expect(c.get('content.hosts')).to.eql(bootHosts);
+    });
+    it('if isHostHaveWarnings is false should set bootHosts to content.hosts', function() {
+      var bootHosts = [{name: 'c1'}];
+      c.reopen({isHostHaveWarnings: false, bootHosts: bootHosts, hosts: []});
+      c.submit();
+      expect(c.get('content.hosts')).to.eql(bootHosts);
+    });
+  });
+
+  describe('#hostLogPopup', function() {
+    it('should show App.ModalPopup', function() {
+      sinon.spy(App.ModalPopup, 'show');
+      c.hostLogPopup({context:Em.Object.create({})});
+      expect(App.ModalPopup.show.calledOnce).to.equal(true);
+      App.ModalPopup.show.restore();
+    });
+  });
+
+  describe('#rerunChecksSuccessCallback', function() {
+    beforeEach(function() {
+      sinon.stub(c, 'parseWarnings', Em.K);
+    });
+    afterEach(function() {
+      c.parseWarnings.restore();
+    });
+    it('should set checksUpdateProgress to 100', function() {
+      c.set('checksUpdateProgress', 0);
+      c.rerunChecksSuccessCallback({});
+      expect(c.get('checksUpdateProgress')).to.equal(100);
+    });
+    it('should set checksUpdateStatus to SUCCESS', function() {
+      c.set('checksUpdateStatus', '');
+      c.rerunChecksSuccessCallback({});
+      expect(c.get('checksUpdateStatus')).to.equal('SUCCESS');
+    });
+    it('should set call parseWarnings', function() {
+      c.rerunChecksSuccessCallback({});
+      expect(c.parseWarnings.calledOnce).to.equal(true);
+    });
+  });
+
+  describe('#rerunChecksErrorCallback', function() {
+    it('should set checksUpdateProgress to 100', function() {
+      c.set('checksUpdateProgress', 0);
+      c.rerunChecksErrorCallback({});
+      expect(c.get('checksUpdateProgress')).to.equal(100);
+    });
+    it('should set checksUpdateStatus to FAILED', function() {
+      c.set('checksUpdateStatus', '');
+      c.rerunChecksErrorCallback({});
+      expect(c.get('checksUpdateStatus')).to.equal('FAILED');
+    });
+  });
+
+  describe('#filterBootHosts', function() {
+    var tests = Em.A([
+      {
+        bootHosts: [
+          Em.Object.create({name: 'c1'}),
+          Em.Object.create({name: 'c2'})
+        ],
+        data: {
+          items: [
+            {Hosts: {host_name: 'c1'}}
+          ]
+        },
+        m: 'one host',
+        e: ['c1']
+      },
+      {
+        bootHosts: [
+          Em.Object.create({name: 'c1'}),
+          Em.Object.create({name: 'c2'})
+        ],
+        data: {
+          items: [
+            {Hosts: {host_name: 'c3'}}
+          ]
+        },
+        m: 'no hosts',
+        e: []
+      },
+      {
+      bootHosts: [
+        Em.Object.create({name: 'c1'}),
+        Em.Object.create({name: 'c2'})
+      ],
+        data: {
+        items: [
+          {Hosts: {host_name: 'c1'}},
+          {Hosts: {host_name: 'c2'}}
+        ]
+      },
+      m: 'many hosts',
+        e: ['c1', 'c2']
+    }
+    ]);
+    tests.forEach(function(test) {
+      it(test.m, function() {
+        c.reopen({bootHosts: test.bootHosts});
+        var filteredData = c.filterBootHosts(test.data);
+        expect(filteredData.items.mapProperty('Hosts.host_name')).to.eql(test.e);
+      });
+    });
+  });
+
+  describe('#hostWarningsPopup', function() {
+    it('should show App.ModalPopup', function() {
+      sinon.spy(App.ModalPopup, 'show');
+      c.hostWarningsPopup();
+      expect(App.ModalPopup.show.calledOnce).to.equal(true);
+      App.ModalPopup.show.restore();
+    });
+  });
+
+  describe('#registeredHostsPopup', function() {
+    it('should show App.ModalPopup', function() {
+      sinon.spy(App.ModalPopup, 'show');
+      c.registeredHostsPopup();
+      expect(App.ModalPopup.show.calledOnce).to.equal(true);
+      App.ModalPopup.show.restore();
+    });
+  });
+
+  describe('#parseWarnings', function() {
+    it('no warnings if last_agent_env isn\'t specified', function() {
+      c.set('warnings', [{}]);
+      c.set('warningsByHost', [{},{}]);
+      c.parseWarnings({items:[{Hosts:{host_name:'c1'}}]});
+      expect(c.get('warnings')).to.eql([]);
+      expect(c.get('warningsByHost.length')).to.equal(1); // default group
+      expect(c.get('isWarningsLoaded')).to.equal(true);
+    });
+
+    Em.A([
+        {
+          m: 'parse stackFoldersAndFiles',
+          tests : Em.A([
+            {
+              items: [{Hosts:{host_name: 'c1', last_agent_env: {stackFoldersAndFiles: []}}}],
+              m: 'empty stackFoldersAndFiles',
+              e: {
+                warnings: [],
+                warningsByHost: [0]
+              }
+            },
+            {
+              items: [{Hosts:{host_name: 'c1', last_agent_env: {stackFoldersAndFiles: [{name: 'n1'}]}}}],
+              m: 'not empty stackFoldersAndFiles',
+              e: {
+                warnings: [{
+                  name: 'n1',
+                  hosts: ['c1'],
+                  onSingleHost: true,
+                  category: 'fileFolders'
+                }],
+                warningsByHost: [1]
+              }
+            },
+            {
+              items: [
+                {Hosts:{host_name: 'c1', last_agent_env: {stackFoldersAndFiles: [{name: 'n1'}]}}},
+                {Hosts:{host_name: 'c2', last_agent_env: {stackFoldersAndFiles: [{name: 'n1'}]}}}
+              ],
+              m: 'not empty stackFoldersAndFiles on two hosts',
+              e: {
+                warnings: [{
+                  name: 'n1',
+                  hosts: ['c1', 'c2'],
+                  onSingleHost: false,
+                  category: 'fileFolders'
+                }],
+                warningsByHost: [1]
+              }
+            }
+          ])
+        },
+        {
+          m: 'parse installedPackages',
+          tests : Em.A([
+            {
+              items: [{Hosts:{host_name: 'c1', last_agent_env: {installedPackages: []}}}],
+              m: 'empty installedPackages',
+              e: {
+                warnings: [],
+                warningsByHost: [0]
+              }
+            },
+            {
+              items: [{Hosts:{host_name: 'c1', last_agent_env: {installedPackages: [{name: 'n1'}]}}}],
+              m: 'not empty installedPackages',
+              e: {
+                warnings: [{
+                  name: 'n1',
+                  hosts: ['c1'],
+                  onSingleHost: true,
+                  category: 'packages'
+                }],
+                warningsByHost: [1]
+              }
+            },
+            {
+              items: [
+                {Hosts:{host_name: 'c1', last_agent_env: {installedPackages: [{name: 'n1'}]}}},
+                {Hosts:{host_name: 'c2', last_agent_env: {installedPackages: [{name: 'n1'}]}}}
+              ],
+              m: 'not empty installedPackages on two hosts',
+              e: {
+                warnings: [{
+                  name: 'n1',
+                  hosts: ['c1', 'c2'],
+                  onSingleHost: false,
+                  category: 'packages'
+                }],
+                warningsByHost: [1]
+              }
+            }
+          ])
+        },
+        {
+          m: 'parse hostHealth.liveServices',
+          tests : Em.A([
+            {
+              items: [{Hosts:{host_name: 'c1', last_agent_env: {hostHealth: []}}}],
+              m: 'empty hostHealth',
+              e: {
+                warnings: [],
+                warningsByHost: [0]
+              }
+            },
+            {
+              items: [{Hosts:{host_name: 'c1', last_agent_env: {hostHealth:{liveServices: []}}}}],
+              m: 'empty liveServices',
+              e: {
+                warnings: [],
+                warningsByHost: [0]
+              }
+            },
+            {
+              items: [{Hosts:{host_name: 'c1', last_agent_env: {hostHealth:{liveServices: [{status: 'Unhealthy', name: 'n1'}]}}}}],
+              m: 'not empty hostHealth.liveServices',
+              e: {
+                warnings: [{
+                  name: 'n1',
+                  hosts: ['c1'],
+                  onSingleHost: true,
+                  category: 'services'
+                }],
+                warningsByHost: [1]
+              }
+            },
+            {
+              items: [
+                {Hosts:{host_name: 'c1', last_agent_env: {hostHealth:{liveServices: [{status: 'Unhealthy', name: 'n1'}]}}}},
+                {Hosts:{host_name: 'c2', last_agent_env: {hostHealth:{liveServices: [{status: 'Unhealthy', name: 'n1'}]}}}}
+              ],
+              m: 'not empty hostHealth.liveServices on two hosts',
+              e: {
+                warnings: [{
+                  name: 'n1',
+                  hosts: ['c1', 'c2'],
+                  onSingleHost: false,
+                  category: 'services'
+                }],
+                warningsByHost: [1, 1]
+              }
+            }
+          ])
+        },
+        {
+          m: 'parse existingUsers',
+          tests : Em.A([
+            {
+              items: [{Hosts:{host_name: 'c1', last_agent_env: {existingUsers: []}}}],
+              m: 'empty existingUsers',
+              e: {
+                warnings: [],
+                warningsByHost: [0]
+              }
+            },
+            {
+              items: [{Hosts:{host_name: 'c1', last_agent_env: {existingUsers: [{userName: 'n1'}]}}}],
+              m: 'not empty existingUsers',
+              e: {
+                warnings: [{
+                  name: 'n1',
+                  hosts: ['c1'],
+                  onSingleHost: true,
+                  category: 'users'
+                }],
+                warningsByHost: [1]
+              }
+            },
+            {
+              items: [
+                {Hosts:{host_name: 'c1', last_agent_env: {existingUsers: [{userName: 'n1'}]}}},
+                {Hosts:{host_name: 'c2', last_agent_env: {existingUsers: [{userName: 'n1'}]}}}
+              ],
+              m: 'not empty existingUsers on two hosts',
+              e: {
+                warnings: [{
+                  name: 'n1',
+                  hosts: ['c1', 'c2'],
+                  onSingleHost: false,
+                  category: 'users'
+                }],
+                warningsByHost: [1, 1]
+              }
+            }
+          ])
+        },
+        {
+          m: 'parse alternatives',
+          tests : Em.A([
+            {
+              items: [{Hosts:{host_name: 'c1', last_agent_env: {alternatives: []}}}],
+              m: 'empty alternatives',
+              e: {
+                warnings: [],
+                warningsByHost: [0]
+              }
+            },
+            {
+              items: [{Hosts:{host_name: 'c1', last_agent_env: {alternatives: [{name: 'n1'}]}}}],
+              m: 'not empty alternatives',
+              e: {
+                warnings: [{
+                  name: 'n1',
+                  hosts: ['c1'],
+                  onSingleHost: true,
+                  category: 'alternatives'
+                }],
+                warningsByHost: [1]
+              }
+            },
+            {
+              items: [
+                {Hosts:{host_name: 'c1', last_agent_env: {alternatives: [{name: 'n1'}]}}},
+                {Hosts:{host_name: 'c2', last_agent_env: {alternatives: [{name: 'n1'}]}}}
+              ],
+              m: 'not empty alternatives on two hosts',
+              e: {
+                warnings: [{
+                  name: 'n1',
+                  hosts: ['c1', 'c2'],
+                  onSingleHost: false,
+                  category: 'alternatives'
+                }],
+                warningsByHost: [1, 1]
+              }
+            }
+          ])
+        },
+        {
+          m: 'parse hostHealth.activeJavaProcs',
+          tests : Em.A([
+            {
+              items: [{Hosts:{host_name: 'c1', last_agent_env: {hostHealth: [], javaProcs: []}}}],
+              m: 'empty hostHealth',
+              e: {
+                warnings: [],
+                warningsByHost: [0]
+              }
+            },
+            {
+              items: [{Hosts:{host_name: 'c1', last_agent_env: {hostHealth:{activeJavaProcs: []}}}}],
+              m: 'empty activeJavaProcs',
+              e: {
+                warnings: [],
+                warningsByHost: [0]
+              }
+            },
+            {
+              items: [{Hosts:{host_name: 'c1', last_agent_env: {hostHealth:{activeJavaProcs: [{pid: 'n1', command: ''}]}}}}],
+              m: 'not empty hostHealth.activeJavaProcs',
+              e: {
+                warnings: [{
+                  pid: 'n1',
+                  hosts: ['c1'],
+                  onSingleHost: true,
+                  category: 'processes'
+                }],
+                warningsByHost: [1]
+              }
+            },
+            {
+              items: [
+                {Hosts:{host_name: 'c1', last_agent_env: {hostHealth:{activeJavaProcs: [{pid: 'n1', command: ''}]}}}},
+                {Hosts:{host_name: 'c2', last_agent_env: {hostHealth:{activeJavaProcs: [{pid: 'n1', command: ''}]}}}}
+              ],
+              m: 'not empty hostHealth.activeJavaProcs on two hosts',
+              e: {
+                warnings: [{
+                  pid: 'n1',
+                  hosts: ['c1', 'c2'],
+                  onSingleHost: false,
+                  category: 'processes'
+                }],
+                warningsByHost: [1, 1]
+              }
+            }
+          ])
+        }
+    ]).forEach(function(category) {
+      describe(category.m, function() {
+        category.tests.forEach(function(test) {
+          it(test.m, function() {
+            c.parseWarnings({items: test.items});
+            c.get('warnings').forEach(function(w, i) {
+              Em.keys(test.e.warnings[i]).forEach(function(k) {
+                expect(w[k]).to.eql(test.e.warnings[i][k]);
+              });
+            });
+            for(var i in test.e.warningsByHost) {
+              if(test.e.warningsByHost.hasOwnProperty(i)) {
+                expect(c.get('warningsByHost')[i].warnings.length).to.equal(test.e.warningsByHost[i]);
+              }
+            }
+          });
+        });
+      });
+    });
+
+  });
+
 });
\ No newline at end of file