You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by at...@apache.org on 2014/11/25 14:03:17 UTC

ambari git commit: AMBARI-8441 Stack and Upgrade: pre-upgrade flow(containers with current and upgrade versions). (atkach)

Repository: ambari
Updated Branches:
  refs/heads/trunk accbbf273 -> b35464a7f


AMBARI-8441 Stack and Upgrade: pre-upgrade flow(containers with current and upgrade versions). (atkach)


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

Branch: refs/heads/trunk
Commit: b35464a7f4967fabae72426d55aaa6dad83ead73
Parents: accbbf2
Author: Andrii Tkach <at...@hortonworks.com>
Authored: Tue Nov 25 14:47:25 2014 +0200
Committer: Andrii Tkach <at...@hortonworks.com>
Committed: Tue Nov 25 14:47:25 2014 +0200

----------------------------------------------------------------------
 .../data/stack_versions/stack_version_all.json  |  25 ++--
 ambari-web/app/assets/test/tests.js             |   3 +-
 .../main/admin/stack_and_upgrade_controller.js  |  69 +++++++++-
 ambari-web/app/messages.js                      |   9 +-
 ambari-web/app/styles/application.less          | 134 +++----------------
 .../templates/main/admin/stack_and_upgrade.hbs  |  83 +++++++++---
 ambari-web/app/utils/ajax/ajax.js               |   2 +-
 ambari-web/app/views.js                         |   1 +
 .../views/main/admin/stack_and_upgrade_view.js  |  66 ++++++++-
 .../main/admin/upgrade_version_box_view.js      |  41 ++++++
 .../admin/stack_and_upgrade_controller_test.js  | 108 +++++++++++++++
 .../main/admin/stack_and_upgrade_test.js        |  43 ------
 .../main/admin/upgrade_version_box_view_test.js |  57 ++++++++
 13 files changed, 448 insertions(+), 193 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/b35464a7/ambari-web/app/assets/data/stack_versions/stack_version_all.json
----------------------------------------------------------------------
diff --git a/ambari-web/app/assets/data/stack_versions/stack_version_all.json b/ambari-web/app/assets/data/stack_versions/stack_version_all.json
index 59b05f7..29fb41e 100644
--- a/ambari-web/app/assets/data/stack_versions/stack_version_all.json
+++ b/ambari-web/app/assets/data/stack_versions/stack_version_all.json
@@ -1,8 +1,9 @@
 {
   "items": [
     {
-      "StackVersion": {
-        "name": "HDP 2.2.0.1",
+      "ClusterStackVersions": {
+        "state": "CURRENT",
+        "stack": "HDP",
         "version": "2.2.0.1-885",
         "installed_hosts": ["dev01.hortonworks.com", "dev02.hortonworks.com"],
         "current_hosts": ["dev01.hortonworks.com", "dev02.hortonworks.com"],
@@ -26,8 +27,9 @@
       }
     },
     {
-      "StackVersion": {
-        "name": "HDP 2.2.1.1",
+      "ClusterStackVersions": {
+        "state": "INSTALLED",
+        "stack": "HDP",
         "version": "2.2.1.1-885",
         "installed_hosts": ["dev01.hortonworks.com", "dev02.hortonworks.com"],
         "current_hosts": ["dev01.hortonworks.com", "dev02.hortonworks.com"],
@@ -51,8 +53,9 @@
       }
     },
     {
-      "StackVersion": {
-        "name": "HDP 2.2.2.0",
+      "ClusterStackVersions": {
+        "state": "INSTALLED",
+        "stack": "HDP",
         "version": "2.2.2.1-885",
         "installed_hosts": ["dev01.hortonworks.com", "dev02.hortonworks.com"],
         "current_hosts": ["dev01.hortonworks.com", "dev02.hortonworks.com"],
@@ -91,8 +94,9 @@
       }
     },
     {
-      "StackVersion": {
-        "name": "HDP 2.3.0.1",
+      "ClusterStackVersions": {
+        "state": "INSTALLED",
+        "stack": "HDP",
         "version": "2.3.0.1-885",
         "installed_hosts": [],
         "current_hosts": [],
@@ -116,8 +120,9 @@
       }
     },
     {
-      "StackVersion": {
-        "name": "HDP 2.2.0.2",
+      "ClusterStackVersions": {
+        "state": "INSTALLED",
+        "stack": "HDP",
         "version": "2.2.0.3",
         "installed_hosts": ["dev01.hortonworks.com", "dev02.hortonworks.com", "dev03.hortonworks.com"],
         "current_hosts": ["dev01.hortonworks.com", "dev02.hortonworks.com", "dev03.hortonworks.com"],

http://git-wip-us.apache.org/repos/asf/ambari/blob/b35464a7/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 3d98fec..6ab35fe 100644
--- a/ambari-web/app/assets/test/tests.js
+++ b/ambari-web/app/assets/test/tests.js
@@ -50,7 +50,7 @@ var files = ['test/init_model_test',
   'test/controllers/main/alerts/definitions_details_controller_test',
   'test/controllers/main/alerts/alert_instances_controller_test',
   'test/controllers/main/alerts/add_alert_definition/step1_controller_test',
-  'test/controllers/main/admin/stack_and_upgrade_test',
+  'test/controllers/main/admin/stack_and_upgrade_controller_test',
   'test/controllers/main/admin/stack_version/stack_version_details_controller_test',
   'test/controllers/main/admin/serviceAccounts_controller_test',
   'test/controllers/main/admin/highAvailability_controller_test',
@@ -158,6 +158,7 @@ var files = ['test/init_model_test',
   'test/views/main/dashboard_test',
   'test/views/main/menu_test',
   'test/views/main/admin/stack_version/stack_version_details_test',
+  'test/views/main/admin/upgrade_version_box_view_test',
   'test/views/main/dashboard/config_history_view_test',
   'test/views/main/dashboard/widget_test',
   'test/views/main/dashboard/widgets_test',

http://git-wip-us.apache.org/repos/asf/ambari/blob/b35464a7/ambari-web/app/controllers/main/admin/stack_and_upgrade_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/admin/stack_and_upgrade_controller.js b/ambari-web/app/controllers/main/admin/stack_and_upgrade_controller.js
index 7549e6b..54f853d 100644
--- a/ambari-web/app/controllers/main/admin/stack_and_upgrade_controller.js
+++ b/ambari-web/app/controllers/main/admin/stack_and_upgrade_controller.js
@@ -23,15 +23,82 @@ App.MainAdminStackAndUpgradeController = Em.Controller.extend({
 
   serviceToInstall: null,
 
+  /**
+   * version that currently applied to server
+   */
+  currentVersion: null,
+  /**
+   * versions to which cluster could be upgraded
+   */
+  targetVersions: [],
+
   services: function() {
     return App.StackService.find().map(function(s) {
-      s.set('isInstalled', App.Service.find().someProperty('id', s.get('serviceName')));
+      s.set('isInstalled', App.Service.find().someProperty('serviceName', s.get('serviceName')));
       return s;
     });
   }.property('App.router.clusterController.isLoaded'),
 
+  /**
+   * launch Add Service wizard
+   * @param event
+   */
   goToAddService: function (event) {
     this.set('serviceToInstall', event.context);
     App.get('router').transitionTo('main.serviceAdd');
+  },
+
+  /**
+   * call to fetch cluster stack versions
+   * @return {$.ajax}
+   */
+  loadVersionsInfo: function () {
+    return App.ajax.send({
+      name: 'admin.stack_versions.all',
+      sender: this,
+      data: {},
+      success: 'loadVersionsInfoSuccessCallback'
+    });
+  },
+
+  /**
+   * parse stack versions and
+   * set <code>currentVersion</code>
+   * set <code>targetVersions</code>
+   * @param data
+   */
+  loadVersionsInfoSuccessCallback: function (data) {
+    var current = data.items.findProperty('ClusterStackVersions.state', 'CURRENT');
+    var target = data.items.without(current);
+
+    this.set('currentVersion', current.ClusterStackVersions);
+    this.set('targetVersions', target.map(function (ver) {
+      return ver.ClusterStackVersions;
+    }));
+  },
+
+  /**
+   * start cluster downgrade
+   */
+  downgrade: function () {
+    //TODO start actual downgrade
+  },
+  /**
+   * start cluster upgrade
+   */
+  upgrade: function () {
+    //TODO start actual upgrade
+  },
+  /**
+   * resume upgrade process
+   */
+  resumeUpgrade: function () {
+    //TODO resume upgrade
+  },
+  /**
+   * finish upgrade process
+   */
+  finalize: function () {
+    //TODO start actual finalize
   }
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/b35464a7/ambari-web/app/messages.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index 2de90bc..5a7ff55 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -91,7 +91,7 @@ Em.I18n.translations = {
   'common.failed':'Failed',
   'common.service': 'Service',
   'common.version':'Version',
-  'common.versions':'Versions',
+  'common.downgrade':'Downgrade',
   'common.description':'Description',
   'common.default':'Default',
   'common.client':'Client',
@@ -1248,6 +1248,13 @@ Em.I18n.translations = {
   'admin.stackVersions.hosts.popup.primary': "Go to Hosts",
 
   'admin.stackUpgrade.title': "Stack and upgrade",
+  'admin.stackUpgrade.hostsOnline': "{0}/{1} hosts online",
+  'admin.stackUpgrade.state.available': "Upgrade Available",
+  'admin.stackUpgrade.state.notAvailable': "No Upgrade Available",
+  'admin.stackUpgrade.state.resume': "Resume Upgrade",
+  'admin.stackUpgrade.state.finalize': "Finalize",
+  'admin.stackUpgrade.hosts': "hosts",
+  'admin.stackUpgrade.host': "host",
 
   'services.service.start':'Start',
   'services.service.stop':'Stop',

http://git-wip-us.apache.org/repos/asf/ambari/blob/b35464a7/ambari-web/app/styles/application.less
----------------------------------------------------------------------
diff --git a/ambari-web/app/styles/application.less b/ambari-web/app/styles/application.less
index e834cab..ab0558b 100644
--- a/ambari-web/app/styles/application.less
+++ b/ambari-web/app/styles/application.less
@@ -4168,13 +4168,6 @@ table.graphs {
 .header {
   margin-bottom: 20px;
 }
-.admin-cluster {
-  a.inactive {
-    cursor: default;
-    color: #333333;
-    text-decoration: none;
-  }
-}
 
  .btn-padding {
    margin-left: 20px
@@ -6661,116 +6654,27 @@ i.icon-asterisks {
   }
 }
 
-.admin-cluster {
-  .repositories-table {
-    margin-bottom: 10px;
-    border: 1px solid #dddddd;
-    overflow: auto;
-    div {
-      float: left;
-      min-height: 1px;
-    }
-    .thead {
-      width: 100%;
-      .th {
-        font-weight: bold;
-        padding: 8px;
-      }
-      .os-th {
-        width: 10%;
-      }
-      .name-th {
-        width: 16%;
-      }
-      .url-th {
-        width: 66%;
+#stack-upgrade-page {
+  .upgrade-flow {
+    margin: 50px 0;
+    .box {
+      text-align: center;
+      .version-name {
+        padding: 15px 10px;
       }
-    }
-    .tbody {
-      width: 100%;
-      .trow {
-        width: 100%;
-        border-top: 1px solid #dddddd;
-        padding-top: 8px;
-        .os-td {
-          padding-top: 4px;
-          padding-left: 8px;
-          width: 9%;
-        }
-        .sub-trow {
-          width: 100%;
-          min-height: 39px;
-          .name-td {
-            width: 16%;
-            padding-top: 4px;
-          }
-          .url-td {
-            width: 60%;
-            .ember-text-field {
-              width: 100%;
-              margin: -1px -1px -1px -2px;
-            }
-          }
-          .url-text-td {
-            width: 70%;
-            padding-top: 4px;
-            padding-left: 3px;
-            overflow: auto;
-          }
-          .edit-td {
-            width: 8%;
-            padding-top: 4px;
-            padding-left: 5px;
-            a {
-              cursor: pointer;
-            }
-          }
-          .edit-buttons-td {
-            // save or cancel
-            width: 9%;
-          }
-          .clear-td {
-            width: 3%;
-            padding-top: 5px;
-            padding-left: 12px;
-            a {
-              cursor: pointer;
-              text-decoration: none;
-            }
-            .icon-remove-sign {
-              color: #808080;
-            }
-          }
-        }
+      select {
+        width: 80%;
+        margin: 10px;
       }
     }
-    .textfield-error input {
-      border-color: #b94a48;
-      -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-      -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-      box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-    }
-    .disabled-textfield input {
-      color: #808080;
-      disabled: disabled;
-      pointer-events: none;
-      cursor: default;
-      background: #E4E4E4;
-    }
-    .disabled-label {
-      color: #808080;
-    }
   }
-  #skip-validation {
-    margin-top: 10px;
-    .checkbox {
-     margin-left: 3px;
-     margin-right: 8px;
-     margin-top: 0px;
-    }
-    .icon-question-sign {
-     color: @blue;
-    }
+  .upgrade-flow>div {
+    height: 100px;
+  }
+  .go-to {
+    margin-top: 40px;
+    background-position: left top;
+    height: 20px;
   }
 }
 
@@ -7303,4 +7207,8 @@ i.icon-asterisks {
 .path-link {
   white-space: nowrap;
   cursor: pointer;
+}
+
+.bottom-border {
+  border-bottom: 1px solid #dddddd;
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/b35464a7/ambari-web/app/templates/main/admin/stack_and_upgrade.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/admin/stack_and_upgrade.hbs b/ambari-web/app/templates/main/admin/stack_and_upgrade.hbs
index f40519f..a0dfc69 100644
--- a/ambari-web/app/templates/main/admin/stack_and_upgrade.hbs
+++ b/ambari-web/app/templates/main/admin/stack_and_upgrade.hbs
@@ -16,34 +16,73 @@
 * limitations under the License.
 }}
 
-<div class="admin-cluster">
-    <div class="header">
-      <strong>{{t common.stack}}: {{App.currentStackVersion}}</strong>
+<div id="stack-upgrade-page">
+  {{#if App.supports.stackUpgrade}}
+    <div class="header bottom-border">
+      <strong>{{t common.upgrade}}</strong>
+      <span class="pull-right">{{view.hostsOnlineLabel}}</span>
     </div>
-    <table class="table table-bordered table-striped">
-      <thead>
-      <tr>
-        <th>{{t common.service}}</th>
-        <th>{{t common.version}}</th>
-        <th>{{t common.status}}</th>
-        <th>{{t common.description}}</th>
-      </tr>
-      </thead>
-      <tbody>
-      {{#each service in services}}
+    <div class="row-fluid upgrade-flow">
+      {{#view view.sourceVersionView classNames="span2 offset3 box"}}
+        <div class="version-name"><strong>{{view.versionName}}</strong></div>
+        <div>
+          {{#if view.action.label}}
+            <button {{bindAttr class=":btn view.btnClass"}} {{action runAction view.action.method target="view"}}>{{view.action.label}}</button>
+          {{else}}
+            {{view.hostsCount}}
+            &nbsp;{{pluralize view.hostsCount singular="t:admin.stackUpgrade.host" plural="t:admin.stackUpgrade.hosts"}}
+          {{/if}}
+        </div>
+      {{/view}}
+      <div class="span1">
+        <div class="go-to"></div>
+        <div>{{view.upgradeStateLabel}}</div>
+      </div>
+      {{#view view.targetVersionView classNames="span2 box"}}
+        {{view Em.Select classBinding="view.hasVersionsToUpgrade::hidden"
+        contentBinding="view.versionsSelectContent"
+        optionValuePath="content.value"
+        optionLabelPath="content.label"
+        selectionBinding="view.selectedVersion"
+        }}
+        <div {{bindAttr class="view.hasVersionsToUpgrade:hidden :version-name"}}><strong>{{view.versionName}}</strong>
+        </div>
+        <div>
+          {{#if view.action.label}}
+            <button {{bindAttr class=":btn view.btnClass"}} {{action runAction view.action.method target="view"}}>{{view.action.label}}</button>
+          {{/if}}
+        </div>
+      {{/view}}
+    </div>
+  {{/if}}
+  <div class="header bottom-border">
+    <strong>{{t common.stack}}: {{App.currentStackVersion}}</strong>
+  </div>
+  <table class="table table-bordered table-striped">
+    <thead>
+    <tr>
+      <th>{{t common.service}}</th>
+      <th>{{t common.version}}</th>
+      <th>{{t common.status}}</th>
+      <th>{{t common.description}}</th>
+    </tr>
+    </thead>
+    <tbody>
+    {{#each service in services}}
       <tr>
         <td>{{service.displayName}}</td>
         <td>{{service.stackVersion}}</td>
         <td>
-        {{#if service.isInstalled}}
-          <span class="label label-success">{{t common.installed}}</span>
-        {{else}}
-          <a class="path-link" {{action goToAddService service.serviceName target="controller"}}>{{t services.service.add}}</a>
-        {{/if}}
+          {{#if service.isInstalled}}
+            <span class="label label-success">{{t common.installed}}</span>
+          {{else}}
+            <a
+              class="path-link" {{action goToAddService service.serviceName target="controller"}}>{{t services.service.add}}</a>
+          {{/if}}
         </td>
         <td>{{{service.comments}}}</td>
       </tr>
-      {{/each}}
-      </tbody>
-    </table>
+    {{/each}}
+    </tbody>
+  </table>
 </div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/b35464a7/ambari-web/app/utils/ajax/ajax.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/ajax/ajax.js b/ambari-web/app/utils/ajax/ajax.js
index cbc8996..4c3d74d 100644
--- a/ambari-web/app/utils/ajax/ajax.js
+++ b/ambari-web/app/utils/ajax/ajax.js
@@ -1268,7 +1268,7 @@ var urls = {
     'mock': '/data/wizard/{mock}'
   },
   'admin.stack_versions.all': {
-    'real': '/clusters/{clusterName}/stack_versions',
+    'real': '/clusters/{clusterName}/stack_versions?fields=ClusterStackVersions/*&minimal_response=true',
     'mock': '/data/stack_versions/stack_version_all.json'
   },
   'admin.stack_version.install.repo_version': {

http://git-wip-us.apache.org/repos/asf/ambari/blob/b35464a7/ambari-web/app/views.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views.js b/ambari-web/app/views.js
index da680a7..a36006f 100644
--- a/ambari-web/app/views.js
+++ b/ambari-web/app/views.js
@@ -104,6 +104,7 @@ require('views/main/admin/highAvailability/resourceManager/step3_view');
 require('views/main/admin/highAvailability/resourceManager/step4_view');
 require('views/main/admin/serviceAccounts_view');
 require('views/main/admin/stack_upgrade');
+require('views/main/admin/upgrade_version_box_view');
 require('views/main/admin/stack_and_upgrade_view');
 require('views/main/admin/stack_versions/stack_version_view');
 require('views/main/admin/stack_versions/stack_version_details_view');

http://git-wip-us.apache.org/repos/asf/ambari/blob/b35464a7/ambari-web/app/views/main/admin/stack_and_upgrade_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/admin/stack_and_upgrade_view.js b/ambari-web/app/views/main/admin/stack_and_upgrade_view.js
index ffa41c8..b23cd9a 100644
--- a/ambari-web/app/views/main/admin/stack_and_upgrade_view.js
+++ b/ambari-web/app/views/main/admin/stack_and_upgrade_view.js
@@ -20,6 +20,70 @@ var App = require('app');
 var stringUtils = require('utils/string_utils');
 
 App.MainAdminStackAndUpgradeView = Em.View.extend({
-  templateName: require('templates/main/admin/stack_and_upgrade')
+  templateName: require('templates/main/admin/stack_and_upgrade'),
+
+  hostsOnlineLabel: function () {
+    var hostsCountMap = App.router.get('mainHostController.hostsCountMap');
+    return Em.I18n.t('admin.stackUpgrade.hostsOnline').format(hostsCountMap.HEALTHY, hostsCountMap.TOTAL);
+  }.property('App.router.mainHostController.hostsCountMap'),
+
+  upgradeStateLabel: function () {
+    return "";
+  }.property(),
+
+  willInsertElement: function () {
+    if (App.get('supports.stackUpgrade')) this.get('controller').loadVersionsInfo();
+  },
+
+  sourceVersionView: App.UpgradeVersionBoxView.extend({
+    version: function () {
+      return this.get('controller.currentVersion');
+    }.property('controller.currentVersion'),
+    btnClass: 'btn-danger',
+    action: function () {
+      return {
+        method: ['UPGRADING', 'UPGRADED', 'UPGRADE_FAILED'].contains(this.get('version.state')) && 'downgrade',
+        label: ['UPGRADING', 'UPGRADED', 'UPGRADE_FAILED'].contains(this.get('version.state')) && Em.I18n.t('common.downgrade')
+      };
+    }.property('version.state'),
+    hostsCount: function () {
+      return this.get('version.current_hosts.length');
+    }.property('version.current_hosts.length')
+  }),
+  targetVersionView: App.UpgradeVersionBoxView.extend({
+    versions: function () {
+      return this.get('controller.targetVersions');
+    }.property('controller.targetVersions'),
+    btnClass: 'btn-success',
+    versionName: function () {
+      if (!this.get('hasVersionsToUpgrade')) return Em.I18n.t('admin.stackUpgrade.state.notAvailable');
+      return this.get('version.stack') + "-" + this.get('version.version');
+    }.property('version.stack', 'version.version', 'hasVersionsToUpgrade'),
+    hasVersionsToUpgrade: function () {
+      return this.get('versions.length') > 0;
+    }.property('versions.length'),
+    selectedVersion: null,
+    versionsSelectContent: function () {
+      return this.get('versions').map(function (version) {
+        return {
+          label: version.stack + "-" + version.version,
+          value: version.id
+        }
+      });
+    }.property('versions.length'),
+    action: function () {
+      var methodName = null,
+          labelName = null,
+          versions = this.get('versions');
+      if (versions.length > 0) {
+        labelName = Em.I18n.t('common.upgrade');
+        methodName = 'upgrade';
+      }
+      return {
+        method: methodName,
+        label: labelName
+      };
+    }.property('versions.length')
+  })
 });
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/b35464a7/ambari-web/app/views/main/admin/upgrade_version_box_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/admin/upgrade_version_box_view.js b/ambari-web/app/views/main/admin/upgrade_version_box_view.js
new file mode 100644
index 0000000..a071324
--- /dev/null
+++ b/ambari-web/app/views/main/admin/upgrade_version_box_view.js
@@ -0,0 +1,41 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+var App = require('app');
+
+App.UpgradeVersionBoxView = Em.View.extend({
+  version: null,
+  versionName: function () {
+    return this.get('version.stack') + "-" + this.get('version.version');
+  }.property('version.stack', 'version.version'),
+  btnClass: 'btn-default',
+  hostsCount: null,
+  /**
+   * run action by name of method
+   * @param event
+   * @return {Boolean}
+   */
+  runAction: function (event) {
+    if (typeof this.get('controller')[event.context] === 'function') {
+      this.get('controller')[event.context]();
+      return true;
+    }
+    return false;
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/b35464a7/ambari-web/test/controllers/main/admin/stack_and_upgrade_controller_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/admin/stack_and_upgrade_controller_test.js b/ambari-web/test/controllers/main/admin/stack_and_upgrade_controller_test.js
new file mode 100644
index 0000000..4e9e1bf
--- /dev/null
+++ b/ambari-web/test/controllers/main/admin/stack_and_upgrade_controller_test.js
@@ -0,0 +1,108 @@
+/**
+ * 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 controller;
+require('controllers/main/admin/stack_and_upgrade_controller');
+
+describe('App.MainAdminStackAndUpgradeController', function() {
+
+  beforeEach(function() {
+    controller = App.MainAdminStackAndUpgradeController.create({});
+  });
+
+  describe("#services", function () {
+    before(function () {
+      sinon.stub(App.StackService, 'find').returns([
+        Em.Object.create({serviceName: 'S1', isInstalled: false}),
+        Em.Object.create({serviceName: 'S2', isInstalled: false})
+      ]);
+      sinon.stub(App.Service, 'find').returns([
+        Em.Object.create({serviceName: 'S1'})
+      ]);
+    });
+    after(function () {
+      App.StackService.find.restore();
+      App.Service.find.restore();
+    });
+    it("", function () {
+      controller.propertyDidChange('services');
+      expect(controller.get('services')).to.eql([
+        Em.Object.create({serviceName: 'S1', isInstalled: true}),
+        Em.Object.create({serviceName: 'S2', isInstalled: false})
+      ])
+    });
+  });
+
+  describe("#goToAddService()" , function() {
+    beforeEach(function() {
+      sinon.stub(App.get('router'), 'transitionTo', Em.K);
+    });
+    afterEach(function() {
+     App.get('router').transitionTo.restore();
+    });
+    it("routes to Add Service Wizard", function() {
+      controller.goToAddService({context: "serviceName"});
+      expect(App.get('router').transitionTo.calledOnce).to.be.true;
+      expect(controller.get('serviceToInstall')).to.be.equal("serviceName");
+    });
+  });
+
+  describe("#loadVersionsInfo()", function() {
+    before(function () {
+      sinon.stub(App.ajax, 'send', Em.K);
+    });
+    after(function () {
+      App.ajax.send.restore();
+    });
+    it("make ajax call", function() {
+      controller.loadVersionsInfo();
+      expect(App.ajax.send.getCall(0).args[0]).to.eql({
+        name: 'admin.stack_versions.all',
+        sender: controller,
+        data: {},
+        success: 'loadVersionsInfoSuccessCallback'
+      })
+    });
+  });
+
+  describe("#loadVersionsInfoSuccessCallback()", function() {
+    it("", function() {
+      var data = {"items": [
+        {
+          "ClusterStackVersions": {
+            "state": "CURRENT"
+          }
+        },
+        {
+          "ClusterStackVersions": {
+            "state": "INSTALLED"
+          }
+        }
+      ]};
+      controller.loadVersionsInfoSuccessCallback(data);
+      expect(controller.get('currentVersion')).to.eql({
+        "state": "CURRENT"
+      });
+      expect(controller.get('targetVersions')).to.eql([{
+        "state": "INSTALLED"
+      }]);
+    });
+  });
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/b35464a7/ambari-web/test/controllers/main/admin/stack_and_upgrade_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/admin/stack_and_upgrade_test.js b/ambari-web/test/controllers/main/admin/stack_and_upgrade_test.js
deleted file mode 100644
index 6e5478f..0000000
--- a/ambari-web/test/controllers/main/admin/stack_and_upgrade_test.js
+++ /dev/null
@@ -1,43 +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.
- */
-
-
-var App = require('app');
-var controller;
-require('controllers/main/admin/stack_and_upgrade_controller');
-
-describe('App.MainAdminStackAndUpgradeController', function() {
-
-  beforeEach(function() {
-    controller = App.MainAdminStackAndUpgradeController.create({});
-  });
-
-  describe("goToAddService" , function() {
-    beforeEach(function() {
-      sinon.stub(App.get('router'), 'transitionTo', Em.K);
-    });
-    afterEach(function() {
-     App.get('router').transitionTo.restore();
-    });
-    it("routes to Addservice Wizard", function() {
-      controller.goToAddService({context: "serviceName"});
-      expect(App.get('router').transitionTo.calledOnce).to.be.true;
-      expect(controller.get('serviceToInstall')).to.be.equal("serviceName");
-    });
-  })
-});

http://git-wip-us.apache.org/repos/asf/ambari/blob/b35464a7/ambari-web/test/views/main/admin/upgrade_version_box_view_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/admin/upgrade_version_box_view_test.js b/ambari-web/test/views/main/admin/upgrade_version_box_view_test.js
new file mode 100644
index 0000000..79436ac
--- /dev/null
+++ b/ambari-web/test/views/main/admin/upgrade_version_box_view_test.js
@@ -0,0 +1,57 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+var App = require('app');
+require('views/main/admin/upgrade_version_box_view');
+
+describe('App.UpgradeVersionBoxView', function () {
+  var view = App.UpgradeVersionBoxView.create({
+    controller: Em.Object.create({
+      upgrade: Em.K
+    })
+  });
+
+  describe("#versionName", function () {
+    it("", function () {
+      view.set('version', Em.Object.create({
+        stack: 'HDP',
+        version: '2.2'
+      }));
+      view.propertyDidChange('versionName');
+      expect(view.get('versionName')).to.equal('HDP-2.2');
+    });
+  });
+
+  describe("#runAction()", function () {
+    beforeEach(function () {
+      sinon.spy(view.get('controller'), 'upgrade');
+    });
+    afterEach(function () {
+      view.get('controller').upgrade.restore();
+    });
+    it("call upgrade()", function () {
+      expect(view.runAction({context: 'upgrade'})).to.be.true;
+      expect(view.get('controller').upgrade.calledOnce).to.be.true;
+    });
+    it("no method should be called", function () {
+      expect(view.runAction({context: null})).to.be.false;
+      expect(view.get('controller').upgrade.called).to.be.false;
+    });
+  });
+});
\ No newline at end of file