Repository: ambari
Updated Branches:
  refs/heads/trunk 8ead0a50b -> 2f8de0be4


AMBARI-8659. Rolling upgrade process: implement nested views of upgrade 
process. (atkach via yusaku)


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

Branch: refs/heads/trunk
Commit: 2f8de0be4b46221afc9b66cdc76ed18231fe3b00
Parents: 8ead0a5
Author: Yusaku Sako <yus...@hortonworks.com>
Authored: Thu Dec 11 15:04:26 2014 -0800
Committer: Yusaku Sako <yus...@hortonworks.com>
Committed: Thu Dec 11 15:04:26 2014 -0800

----------------------------------------------------------------------
 ambari-web/app/assets/test/tests.js             |   2 +-
 .../main/admin/stack_and_upgrade_controller.js  | 125 +++++++++++++++-
 ambari-web/app/messages.js                      |   7 +-
 ambari-web/app/routes/stack_upgrade_routes.js   |   4 +
 ambari-web/app/styles/application.less          |  18 ++-
 .../app/templates/common/error_log_body.hbs     |   4 +-
 .../templates/common/host_progress_popup.hbs    |   4 +-
 .../stack_upgrade/stack_upgrade_dialog.hbs      |   8 +-
 .../main/admin/stack_upgrade/upgrade_group.hbs  | 148 +++++++++++++++++++
 .../main/admin/stack_upgrade/upgrade_task.hbs   |  67 ---------
 ambari-web/app/utils/ember_reopen.js            |   6 +-
 ambari-web/app/utils/helper.js                  |  36 +++++
 ambari-web/app/views.js                         |   2 +-
 .../admin/stack_upgrade/upgrade_group_view.js   | 115 ++++++++++++++
 .../admin/stack_upgrade/upgrade_task_view.js    | 115 --------------
 .../admin/stack_upgrade/upgrade_wizard_view.js  |  33 ++++-
 .../admin/stack_and_upgrade_controller_test.js  |  22 ++-
 .../stack_upgrade/upgrade_group_view_test.js    |  40 +++++
 .../stack_upgrade/upgrade_task_view_test.js     |  61 --------
 19 files changed, 539 insertions(+), 278 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/2f8de0be/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 2aab326..dd816dc 100644
--- a/ambari-web/app/assets/test/tests.js
+++ b/ambari-web/app/assets/test/tests.js
@@ -168,7 +168,7 @@ var files = ['test/init_model_test',
   'test/views/main/alerts/manage_alert_notifications_view_test',
   'test/views/main/admin/stack_version/stack_version_details_test',
   'test/views/main/admin/stack_upgrade/upgrade_version_box_view_test',
-  'test/views/main/admin/stack_upgrade/upgrade_task_view_test',
+  'test/views/main/admin/stack_upgrade/upgrade_group_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/2f8de0be/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 f8c687e..2d73ab3 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
@@ -164,11 +164,81 @@ App.MainAdminStackAndUpgradeController = 
Em.Controller.extend(App.LocalStorage,
   loadUpgradeDataSuccessCallback: function (data) {
     App.set('upgradeState', data.Upgrade.request_status);
     if (data.upgrade_groups) {
-      this.set("upgradeData", data);
+      this.updateUpgradeData(data);
     }
   },
 
   /**
+   * update data of Upgrade
+   * @param {object} newData
+   */
+  updateUpgradeData: function (newData) {
+    var oldData = this.get('upgradeData'),
+        groupsMap = {},
+        itemsMap = {},
+        tasksMap = {};
+
+    if (Em.isNone(oldData)) {
+      this.initUpgradeData(newData);
+    } else {
+      //create entities maps
+      newData.upgrade_groups.forEach(function (newGroup) {
+        groupsMap[newGroup.UpgradeGroup.group_id] = newGroup.UpgradeGroup;
+        newGroup.upgrade_items.forEach(function (item) {
+          itemsMap[item.UpgradeItem.stage_id] = item.UpgradeItem;
+          item.tasks.forEach(function (task) {
+            tasksMap[task.Tasks.id] = task.Tasks;
+          });
+        })
+      });
+
+      //update existed entities with new data
+      oldData.upgradeGroups.forEach(function (oldGroup) {
+        oldGroup.set('status', groupsMap[oldGroup.get('group_id')].status);
+        oldGroup.set('progress_percent', 
groupsMap[oldGroup.get('group_id')].progress_percent);
+        oldGroup.upgradeItems.forEach(function (item) {
+          item.set('status', itemsMap[item.get('stage_id')].status);
+          item.set('progress_percent', 
itemsMap[item.get('stage_id')].progress_percent);
+          item.tasks.forEach(function (task) {
+            task.set('status', tasksMap[task.get('id')].status);
+          });
+        })
+      });
+    }
+  },
+
+  /**
+   * change structure of Upgrade
+   * In order to maintain nested views in template object should have direct 
link to its properties, for example
+   * item.UpgradeItem.<properties> -> item.<properties>
+   * @param {object} newData
+   */
+  initUpgradeData: function (newData) {
+    var upgradeGroups = [];
+
+    //wrap all entities into App.upgradeEntity
+    newData.upgrade_groups.forEach(function (newGroup) {
+      var oldGroup = App.upgradeEntity.create(newGroup.UpgradeGroup);
+      var upgradeItems = [];
+      newGroup.upgrade_items.forEach(function (item) {
+        var oldItem = App.upgradeEntity.create(item.UpgradeItem);
+        var tasks = [];
+        item.tasks.forEach(function (task) {
+          tasks.pushObject(App.upgradeEntity.create(task.Tasks));
+        });
+        oldItem.set('tasks', tasks);
+        upgradeItems.pushObject(oldItem);
+      });
+      oldGroup.set('upgradeItems', upgradeItems);
+      upgradeGroups.pushObject(oldGroup);
+    });
+    this.set('upgradeData', Em.Object.create({
+      upgradeGroups: upgradeGroups,
+      Upgrade: newData.Upgrade
+    }));
+  },
+
+  /**
    * make call to start downgrade process
    */
   downgrade: function () {
@@ -292,3 +362,56 @@ App.MainAdminStackAndUpgradeController = 
Em.Controller.extend(App.LocalStorage,
     App.router.transitionTo('admin.stackUpgrade');
   }
 });
+
+
+/**
+ * @type {Ember.Object}
+ * @class
+ */
+App.upgradeEntity = Em.Object.extend({
+
+  /**
+   * @type {boolean}
+   */
+  errorLogOpened: false,
+
+  /**
+   * @type {boolean}
+   */
+  outputLogOpened: false,
+
+  /**
+   * @type {boolean}
+   */
+  isExpanded: false,
+
+  /**
+   * @type {boolean}
+   */
+  isRunning: function () {
+    return ['IN_PROGRESS'].contains(this.get('status'));
+  }.property('status'),
+
+  /**
+   * width style of progress bar
+   * @type {string}
+   */
+  progressWidth: function () {
+    return "width:" + Math.floor(this.get('progress')) + '%;';
+  }.property('progress'),
+
+  /**
+   * @type {number}
+   */
+  progress: function () {
+    return Math.floor(this.get('progress_percent'));
+  }.property('progress_percent'),
+
+  /**
+   * indicate whether entity has active link
+   * @type {boolean}
+   */
+  isActive: function () {
+    return this.get('status') !== 'PENDING';
+  }.property('status')
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/2f8de0be/ambari-web/app/messages.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index 4dbeb91..0705e88 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -243,6 +243,8 @@ Em.I18n.translations = {
   'common.finalize': "Finalize",
   'common.severity': "Severity",
   'common.dismiss': "Dismiss",
+  'common.stdout': "stdout",
+  'common.stderr': "stderr",
 
   'models.alert_instance.tiggered.verbose': "Occured on {0} <br> Checked on 
{1}",
   'models.alert_definition.triggered.verbose': "Occured on {0}",
@@ -1295,10 +1297,11 @@ Em.I18n.translations = {
   'admin.stackUpgrade.state.available': "Upgrade Available",
   'admin.stackUpgrade.state.notAvailable': "No Upgrade Available",
   'admin.stackUpgrade.state.resume': "Resume Upgrade",
-  'admin.stackUpgrade.state.inProgress': "Upgrade in progress",
-  'admin.stackUpgrade.state.paused': "Upgrade is paused",
+  'admin.stackUpgrade.state.inProgress': "Upgrade in Progress",
+  'admin.stackUpgrade.state.paused': "Upgrade Paused",
   'admin.stackUpgrade.state.stopped': "Upgrade Stopped",
   'admin.stackUpgrade.state.completed': "Upgrade Finished",
+  'admin.stackUpgrade.state.failed': "Upgrade Failed",
   'admin.stackUpgrade.state.upgrading': "Upgrading...",
   'admin.stackUpgrade.hosts': "hosts",
   'admin.stackUpgrade.host': "host",

http://git-wip-us.apache.org/repos/asf/ambari/blob/2f8de0be/ambari-web/app/routes/stack_upgrade_routes.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/routes/stack_upgrade_routes.js 
b/ambari-web/app/routes/stack_upgrade_routes.js
index aca2c62..154a95b 100644
--- a/ambari-web/app/routes/stack_upgrade_routes.js
+++ b/ambari-web/app/routes/stack_upgrade_routes.js
@@ -27,12 +27,16 @@ module.exports = App.WizardRoute.extend({
       App.router.get('updateController').set('isWorking', false);
 
       return App.ModalPopup.show({
+        classNames: ['full-width-modal'],
         header: function () {
           return 
Em.I18n.t('admin.stackUpgrade.dialog.header').format(App.router.get('mainAdminStackAndUpgradeController').get('upgradeVersion'));
         
}.property('App.router.mainAdminStackAndUpgradeController.upgradeVersion'),
         bodyClass: App.upgradeWizardView,
         primary: null,
         secondary: null,
+        didInsertElement: function () {
+          this.fitHeight();
+        },
         onClose: function() {
           var self = this;
           var header = Em.I18n.t('admin.stackUpgrade.state.paused');

http://git-wip-us.apache.org/repos/asf/ambari/blob/2f8de0be/ambari-web/app/styles/application.less
----------------------------------------------------------------------
diff --git a/ambari-web/app/styles/application.less 
b/ambari-web/app/styles/application.less
index aa5e8b7..1fa74fc 100644
--- a/ambari-web/app/styles/application.less
+++ b/ambari-web/app/styles/application.less
@@ -6551,8 +6551,8 @@ i.icon-asterisks {
 
 #stack-upgrade-dialog {
   .task-list {
-    overflow: auto;
-    .task-details {
+    overflow-x: hidden;
+    .details-box {
       padding: 2px 4px;
       margin-left: 15px;
       .button-row {
@@ -6569,12 +6569,20 @@ i.icon-asterisks {
     .progress {
       margin-bottom: 0;
     }
-  }
-  .task-list>div {
-    padding: 5px;
+    padding-left: 20px;
     i {
       margin-right: 5px;
     }
+    .task-details {
+      .manage-controls a {
+        cursor: pointer;
+        margin-right: 12px;
+      }
+      textarea {
+        width: 100%;
+        min-height: 100px;
+      }
+    }
   }
 }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/2f8de0be/ambari-web/app/templates/common/error_log_body.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/common/error_log_body.hbs 
b/ambari-web/app/templates/common/error_log_body.hbs
index 62f1b65..b532901 100644
--- a/ambari-web/app/templates/common/error_log_body.hbs
+++ b/ambari-web/app/templates/common/error_log_body.hbs
@@ -20,9 +20,9 @@
   <div class="content-area">
     <div class="task-detail-log-clipboard-wrap"></div>
     <div class="task-detail-log-maintext">
-      <h5>stderr: &nbsp; <span class="muted">{{view.openedTask.errorLog}} 
</span></h5>
+      <h5>{{t common.stderr}}: &nbsp; <span 
class="muted">{{view.openedTask.errorLog}} </span></h5>
       <pre class="stderr">{{view.openedTask.stderr}}</pre>
-      <h5>stdout: &nbsp; <span class="muted"> {{view.openedTask.outputLog}} 
</span></h5>
+      <h5>{{t common.stdout}}: &nbsp; <span class="muted"> 
{{view.openedTask.outputLog}} </span></h5>
       <pre class="stdout">{{view.openedTask.stdout}}</pre>
       {{#if view.openedTask.structuredOut}}
         <h5>structured_out: &nbsp;</h5>

http://git-wip-us.apache.org/repos/asf/ambari/blob/2f8de0be/ambari-web/app/templates/common/host_progress_popup.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/common/host_progress_popup.hbs 
b/ambari-web/app/templates/common/host_progress_popup.hbs
index 01008f3..e5469ec 100644
--- a/ambari-web/app/templates/common/host_progress_popup.hbs
+++ b/ambari-web/app/templates/common/host_progress_popup.hbs
@@ -226,9 +226,9 @@
               </div>
               <hr>
             {{/if}}
-            <h5>stderr: &nbsp; <span 
class="muted">{{view.openedTask.errorLog}} </span></h5>
+            <h5>{{t common.stderr}}: &nbsp; <span 
class="muted">{{view.openedTask.errorLog}} </span></h5>
             <pre class="stderr">{{view.openedTask.stderr}}</pre>
-            <h5>stdout: &nbsp; <span class="muted"> 
{{view.openedTask.outputLog}} </span></h5>
+            <h5>{{t common.stdout}}: &nbsp; <span class="muted"> 
{{view.openedTask.outputLog}} </span></h5>
             <pre class="stdout">{{view.openedTask.stdout}}</pre>
           </div>
         </div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/2f8de0be/ambari-web/app/templates/main/admin/stack_upgrade/stack_upgrade_dialog.hbs
----------------------------------------------------------------------
diff --git 
a/ambari-web/app/templates/main/admin/stack_upgrade/stack_upgrade_dialog.hbs 
b/ambari-web/app/templates/main/admin/stack_upgrade/stack_upgrade_dialog.hbs
index 492e4e9..8509801 100644
--- a/ambari-web/app/templates/main/admin/stack_upgrade/stack_upgrade_dialog.hbs
+++ b/ambari-web/app/templates/main/admin/stack_upgrade/stack_upgrade_dialog.hbs
@@ -19,7 +19,7 @@
 <div id="stack-upgrade-dialog">
   {{#if view.isLoaded}}
     <div class="row-fluid">
-      <div class="span2">{{t common.progress}}</div>
+      <div class="span2">{{statusIcon 
controller.upgradeData.Upgrade.request_status}}&nbsp;{{view.upgradeStatusLabel}}</div>
       <div class="span9">
         <div class="progress progress-striped active">
           <div class="bar" {{bindAttr style="view.progressWidth"}}></div>
@@ -30,9 +30,9 @@
       </div>
     </div>
 
-    <div class="task-list">
-      {{#each group in view.groups}}
-        {{view App.upgradeTaskView contentBinding="group"}}
+    <div class="task-list limited-height-2">
+      {{#each group in controller.upgradeData.upgradeGroups}}
+        {{view App.upgradeGroupView contentBinding="group"}}
       {{/each}}
     </div>
   {{else}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/2f8de0be/ambari-web/app/templates/main/admin/stack_upgrade/upgrade_group.hbs
----------------------------------------------------------------------
diff --git 
a/ambari-web/app/templates/main/admin/stack_upgrade/upgrade_group.hbs 
b/ambari-web/app/templates/main/admin/stack_upgrade/upgrade_group.hbs
new file mode 100644
index 0000000..f9dcdfc
--- /dev/null
+++ b/ambari-web/app/templates/main/admin/stack_upgrade/upgrade_group.hbs
@@ -0,0 +1,148 @@
+{{!
+* 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="row-fluid">
+  <div {{bindAttr class="view.content.isActive::not-active-link :span8"}}>
+    {{statusIcon view.content.status}}
+    <a href="#" {{action toggleExpanded view.content 
controller.upgradeData.upgradeGroups target="view"}}>{{view.content.title}}</a>
+  </div>
+  {{#if view.content.isRunning}}
+    <div class="span3">
+      <div class="progress progress-striped active">
+        <div class="bar" {{bindAttr style="view.content.progressWidth"}}></div>
+      </div>
+    </div>
+    <div class="span1">
+      <div>{{view.content.progress}}%</div>
+    </div>
+  {{/if}}
+</div>
+
+<div {{bindAttr class="view.content.isExpanded::hidden :task-list"}}>
+
+  {{! List of UpgradeItem}}
+  {{#each item in view.content.upgradeItems}}
+    <div class="row-fluid">
+      <div {{bindAttr class="item.isActive::not-active-link :span8"}}>
+        {{statusIcon item.status}}
+        <a href="#" {{action toggleExpanded item view.content.upgradeItems 
target="view"}}>{{item.context}}</a>
+      </div>
+      {{#if item.isRunning}}
+        <div class="span3">
+          <div class="progress progress-striped active">
+            <div class="bar" {{bindAttr style="item.progressWidth"}}></div>
+          </div>
+        </div>
+        <div class="span1">
+          <div>{{item.progress}}%</div>
+        </div>
+      {{/if}}
+    </div>
+    {{#if item.isExpanded}}
+
+      {{! List of Tasks}}
+      <div class="task-list">
+        {{#each task in item.tasks}}
+          <div {{bindAttr 
class="task.isActive::not-active-link"}}>{{statusIcon task.status}}<a href="#" 
{{action toggleExpanded task item.tasks 
target="view"}}>{{task.command_detail}}</a></div>
+          {{#if task.isExpanded}}
+            <div class="task-details task-detail-info">
+              <ul class="nav nav-tabs">
+                <li class="active"><a href="#tab1" data-toggle="tab">{{t 
common.stdout}}</a></li>
+                <li><a href="#tab2" data-toggle="tab">{{t 
common.stderr}}</a></li>
+              </ul>
+              <div class="tab-content">
+                <div class="tab-pane active" id="tab1">
+                  <p>{{task.host_name}}</p>
+                  <div class="row-fluid">
+                    <p class="pull-left">{{task.output_log}}</p>
+                    <div class="manage-controls pull-right">
+                      <a title="Click to Copy" {{action copyOutLog task 
target="view"}} class="task-detail-copy">
+                        <i class="icon-copy"></i> {{t common.copy}}
+                      </a>
+                      <a title="Open in New Window" {{action openLogWindow 
task.stdout target="view"}} class="task-detail-open-dialog">
+                        <i class="icon-external-link"></i> {{t common.open}}
+                      </a>
+                    </div>
+                  </div>
+                  <pre {{bindAttr class="task.outputLogOpened:hidden 
:stdout"}}>{{task.stdout}}</pre>
+                  {{view Ember.TextArea valueBinding="task.stdout" 
classBinding="task.outputLogOpened::hidden" readonly="readonly"}}
+                </div>
+                <div class="tab-pane" id="tab2">
+                  <p>{{task.host_name}}</p>
+                  <div class="row-fluid">
+                    <p class="pull-left">{{task.error_log}}</p>
+                    <div class="manage-controls pull-right">
+                      <a title="Click to Copy" {{action copyErrLog task 
target="view"}} class="task-detail-copy">
+                        <i class="icon-copy"></i> {{t common.copy}}
+                      </a>
+                      <a title="Open in New Window" {{action openLogWindow 
task.stderr target="view"}} class="task-detail-open-dialog">
+                        <i class="icon-external-link"></i> {{t common.open}}
+                      </a>
+                    </div>
+                  </div>
+                  <pre {{bindAttr class="task.errorLogOpened:hidden 
:stderr"}}>{{task.stderr}}</pre>
+                  {{view Ember.TextArea valueBinding="task.stderr" 
classBinding="task.errorLogOpened::hidden" readonly="readonly"}}
+                </div>
+              </div>
+            </div>
+          {{/if}}
+        {{/each}}
+      </div>
+    {{/if}}
+  {{/each}}
+</div>
+
+{{#unless view.content.isExpanded}}
+  {{#if view.showProgressInfo}}
+    <div class="box details-box">
+      <div>
+        {{t admin.stackUpgrade.dialog.inProgress}}&nbsp;
+        <a href>{{view.runningItem.context}}</a>
+      </div>
+    </div>
+  {{/if}}
+  {{#if view.isFailed}}
+    <div class="box details-box">
+      <div>
+        {{t admin.stackUpgrade.dialog.failed}}&nbsp;
+        <a href>{{view.failedItem.name}}</a>
+      </div>
+      <div class="button-row">
+        <button class="btn btn-danger">{{t 
admin.stackUpgrade.dialog.stop}}</button>
+        <button class="btn btn-warning">{{t 
admin.stackUpgrade.dialog.continue}}</button>
+        <button class="btn">{{t common.retry}}</button>
+      </div>
+    </div>
+  {{/if}}
+  {{#if view.isManualOpened}}
+    <div class="box details-box">
+      <p><strong>{{t admin.stackUpgrade.dialog.manual}}</strong></p>
+
+      <div class="message">
+        {{view Em.Checkbox checkedBinding="view.isManualDone"}}
+        {{t admin.stackUpgrade.dialog.manualDone}}
+      </div>
+      <div class="button-row">
+        <button class="btn btn-danger">{{t 
admin.stackUpgrade.dialog.stop}}</button>
+        <button
+          class="btn btn-success" {{bindAttr 
disabled="view.isManualProceedDisabled"}}>{{t common.proceed}}</button>
+      </div>
+    </div>
+  {{/if}}
+{{/unless}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/2f8de0be/ambari-web/app/templates/main/admin/stack_upgrade/upgrade_task.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/admin/stack_upgrade/upgrade_task.hbs 
b/ambari-web/app/templates/main/admin/stack_upgrade/upgrade_task.hbs
deleted file mode 100644
index 511d04a..0000000
--- a/ambari-web/app/templates/main/admin/stack_upgrade/upgrade_task.hbs
+++ /dev/null
@@ -1,67 +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="row-fluid">
-  <div class="span8"><i {{bindAttr 
class="view.iconClass"}}></i><a>{{view.content.UpgradeGroup.title}}</a></div>
-
-  {{#if view.showProgressBar}}
-    <div class="span3">
-      <div class="progress progress-striped active">
-        <div class="bar" {{bindAttr style="view.progressWidth"}}></div>
-      </div>
-    </div>
-    <div class="span1">
-      <div>{{view.progress}}%</div>
-    </div>
-  {{/if}}
-</div>
-{{#if view.isInProgress}}
-  <div class="box task-details">
-    <div>
-      {{t admin.stackUpgrade.dialog.inProgress}}&nbsp;
-      <a href>{{view.runningItem.UpgradeItem.context}}</a>
-    </div>
-  </div>
-{{/if}}
-{{#if view.isFailed}}
-  <div class="box task-details">
-    <div>
-      {{t admin.stackUpgrade.dialog.failed}}&nbsp;
-      <a href>{{view.failedItem.UpgradeItem.name}}</a>
-    </div>
-    <div class="button-row">
-      <button class="btn btn-danger">{{t 
admin.stackUpgrade.dialog.stop}}</button>
-      <button class="btn btn-warning">{{t 
admin.stackUpgrade.dialog.continue}}</button>
-      <button class="btn">{{t common.retry}}</button>
-    </div>
-  </div>
-{{/if}}
-{{#if view.isManualOpened}}
-  <div class="box task-details">
-    <p><strong>{{t admin.stackUpgrade.dialog.manual}}</strong></p>
-    <div class="message">
-      {{view Em.Checkbox checkedBinding="view.isManualDone"}}
-      {{t admin.stackUpgrade.dialog.manualDone}}
-    </div>
-    <div class="button-row">
-      <button class="btn btn-danger">{{t 
admin.stackUpgrade.dialog.stop}}</button>
-      <button class="btn btn-success" {{bindAttr 
disabled="view.isManualProceedDisabled"}}>{{t common.proceed}}</button>
-    </div>
-  </div>
-{{/if}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/2f8de0be/ambari-web/app/utils/ember_reopen.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/ember_reopen.js 
b/ambari-web/app/utils/ember_reopen.js
index 7ac69db..a879cde 100644
--- a/ambari-web/app/utils/ember_reopen.js
+++ b/ambari-web/app/utils/ember_reopen.js
@@ -173,4 +173,8 @@ Em.View.reopen({
       console.debug('Calling set on destroyed view');
     }
   }
-});
\ No newline at end of file
+});
+
+Ember.TextArea.reopen({
+  attributeBindings: ['readonly']
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/2f8de0be/ambari-web/app/utils/helper.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/helper.js b/ambari-web/app/utils/helper.js
index a58b732..8d7dc2a 100644
--- a/ambari-web/app/utils/helper.js
+++ b/ambari-web/app/utils/helper.js
@@ -723,6 +723,42 @@ App.registerBoundHelper('formatWordBreak', Em.View.extend({
 }));
 
 /**
+ * Return <i></i> with class that correspond to status
+ *
+ * @param {string} content - status
+ *
+ * Examples:
+ *
+ * {{statusIcon view.status}}
+ * returns 'icon-cog'
+ *
+ */
+App.registerBoundHelper('statusIcon', Em.View.extend({
+  tagName: 'i',
+
+  /**
+   * relation map between status and icon class
+   * @type {object}
+   */
+  statusIconMap: {
+    'COMPLETED': 'icon-ok',
+    'WARNING': 'icon-warning-sign',
+    'FAILED': 'icon-warning-sign',
+    'PENDING': 'icon-cog',
+    'IN_PROGRESS': 'icon-cogs',
+    'HOLDING': 'icon-pause'
+  },
+
+  classNameBindings: ['iconClass'],
+  /**
+   * @type {string}
+   */
+  iconClass: function () {
+    return this.get('statusIconMap')[this.get('content')] || 
'icon-question-sign';
+  }.property('content')
+}));
+
+/**
  * Ambari overrides the default date transformer.
  * This is done because of the non-standard data
  * sent. For example Nagios sends date as "12345678".

http://git-wip-us.apache.org/repos/asf/ambari/blob/2f8de0be/ambari-web/app/views.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views.js b/ambari-web/app/views.js
index c19139a..ab4acb8 100644
--- a/ambari-web/app/views.js
+++ b/ambari-web/app/views.js
@@ -105,7 +105,7 @@ require('views/main/admin/serviceAccounts_view');
 require('views/main/admin/stack_upgrade');
 require('views/main/admin/stack_upgrade/upgrade_wizard_view');
 require('views/main/admin/stack_upgrade/upgrade_version_box_view');
-require('views/main/admin/stack_upgrade/upgrade_task_view');
+require('views/main/admin/stack_upgrade/upgrade_group_view');
 require('views/main/admin/stack_and_upgrade_view');
 require('views/main/admin/stack_versions/menu');
 require('views/main/admin/stack_versions/repo_version_view');

http://git-wip-us.apache.org/repos/asf/ambari/blob/2f8de0be/ambari-web/app/views/main/admin/stack_upgrade/upgrade_group_view.js
----------------------------------------------------------------------
diff --git 
a/ambari-web/app/views/main/admin/stack_upgrade/upgrade_group_view.js 
b/ambari-web/app/views/main/admin/stack_upgrade/upgrade_group_view.js
new file mode 100644
index 0000000..23968d1
--- /dev/null
+++ b/ambari-web/app/views/main/admin/stack_upgrade/upgrade_group_view.js
@@ -0,0 +1,115 @@
+/**
+ * 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.upgradeGroupView = Em.View.extend({
+  templateName: require('templates/main/admin/stack_upgrade/upgrade_group'),
+
+  /**
+   * @type {boolean}
+   */
+  isManualDone: false,
+
+  /**
+   * progress info is a box that show running UpgradeItem
+   * @type {boolean}
+   */
+  showProgressInfo: function () {
+    return this.get('content.isRunning') && this.get('runningItem');
+  }.property('content.isRunning', 'runningItem'),
+
+  /**
+   * @type {boolean}
+   */
+  isManualProceedDisabled: function () {
+    return !this.get('isManualDone');
+  }.property('isManualDone'),
+
+  /**
+   * @type {boolean}
+   */
+  isFailed: function () {
+    return this.get('content.status') === 'FAILED';
+  }.property('content.status'),
+
+  /**
+   * if upgrade group is in progress it should have currently running item
+   * @type {object|undefined}
+   */
+  runningItem: function () {
+    return this.get('content.upgradeItems').findProperty('status', 
'IN_PROGRESS');
+  }.property('content.upgradeItems.@each.status'),
+
+  /**
+   * if upgrade group is failed it should have failed item
+   * @type {object|undefined}
+   */
+  failedItem: function () {
+    return this.get('content.upgradeItems').findProperty('status', 'FAILED');
+  }.property('content.upgradeItems.@each.status'),
+
+  /**
+   * @type {boolean}
+   */
+  isManualOpened: function () {
+    return this.get('content.status') === 'HOLDING';
+  }.property('content.status'),
+
+  /**
+   * Only one UpgradeGroup or UpgradeItem could be expanded at a time
+   * @param {object} event
+   */
+  toggleExpanded: function (event) {
+    event.contexts[1].forEach(function (item) {
+      if (item == event.context) {
+        item.set('isExpanded', !event.context.get('isExpanded'));
+      } else {
+        item.set('isExpanded', false);
+      }
+    });
+  },
+
+  /**
+   *
+   * @param {object} event
+   */
+  copyErrLog: function(event) {
+    event.context.toggleProperty('errorLogOpened');
+  },
+
+  /**
+   *
+   * @param {object} event
+   */
+  openLogWindow: function(event) {
+    var newWindow = window.open();
+    var newDocument = newWindow.document;
+    newDocument.write(event.context);
+    newDocument.close();
+  },
+
+  /**
+   *
+   * @param {object} event
+   */
+  copyOutLog: function(event) {
+    event.context.toggleProperty('outputLogOpened');
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/2f8de0be/ambari-web/app/views/main/admin/stack_upgrade/upgrade_task_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/admin/stack_upgrade/upgrade_task_view.js 
b/ambari-web/app/views/main/admin/stack_upgrade/upgrade_task_view.js
deleted file mode 100644
index bf57734..0000000
--- a/ambari-web/app/views/main/admin/stack_upgrade/upgrade_task_view.js
+++ /dev/null
@@ -1,115 +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');
-
-App.upgradeTaskView = Em.View.extend({
-  templateName: require('templates/main/admin/stack_upgrade/upgrade_task'),
-
-  /**
-   * relation map between status and icon class
-   * @type {Object}
-   */
-  statusIconMap: {
-    'COMPLETED': 'icon-ok',
-    'WARNING': 'icon-warning-sign',
-    'FAILED': 'icon-warning-sign',
-    'PENDING': 'icon-cog',
-    'IN_PROGRESS': 'icon-cogs'
-  },
-
-  /**
-   * @type {Boolean}
-   */
-  isManualDone: false,
-
-  /**
-   * @type {Boolean}
-   */
-  isManualProceedDisabled: function () {
-    return !this.get('isManualDone');
-  }.property('isManualDone'),
-
-  /**
-   * @type {string}
-   */
-  iconClass: function () {
-    return this.get('statusIconMap')[this.get('content.UpgradeGroup.status')] 
|| 'icon-question-sign';
-  }.property('content.UpgradeGroup.status'),
-
-  /**
-   * @type {Boolean}
-   */
-  isFailed: function () {
-    return this.get('content.UpgradeGroup.status') === 'FAILED';
-  }.property('content.UpgradeGroup.status'),
-
-  /**
-   * @type {Boolean}
-   */
-  showProgressBar: function () {
-    return ['IN_PROGRESS', 
'FAILED'].contains(this.get('content.UpgradeGroup.status')) && 
this.get('content.UpgradeGroup.type') !== 'manual';
-  }.property('content.UpgradeGroup.status'),
-
-  /**
-   * @type {Boolean}
-   */
-  isInProgress: function () {
-    return this.get('content.UpgradeGroup.status') === 'IN_PROGRESS' && 
this.get('content.UpgradeGroup.type') !== 'manual';
-  }.property('content.UpgradeGroup.status'),
-
-  /**
-   * width style of progress bar
-   * @type {String}
-   */
-  progressWidth: function () {
-    return "width:" + Math.floor(this.get('progress')) + '%;';
-  }.property('content.UpgradeGroup.progress_percent'),
-
-  /**
-   * @type {number}
-   */
-  progress: function () {
-    return Math.floor(this.get('content.UpgradeGroup.progress_percent'))
-  }.property('content.UpgradeGroup.progress_percent'),
-
-  /**
-   * if upgrade group is in progress it should have currently running item
-   * @type {Object|null}
-   */
-  runningItem: function () {
-    return 
this.get('content.upgrade_items').findProperty('UpgradeItem.status', 
'IN_PROGRESS');
-  }.property('content.upgrade_items.@each.UpgradeItem.status'),
-
-  /**
-   * if upgrade group is failed it should have failed item
-   * @type {Object|null}
-   */
-  failedItem: function () {
-    return 
this.get('content.upgrade_items').findProperty('UpgradeItem.status', 'FAILED');
-  }.property('content.upgrade_items.@each.UpgradeItem.status'),
-
-  /**
-   * @type {Boolean}
-   */
-  isManualOpened: function () {
-    //TODO modify logic according to actual API
-    return this.get('content.UpgradeGroup.status') === 'IN_PROGRESS' && 
this.get('content.UpgradeGroup.type') === 'manual'
-  }.property('content.UpgradeGroup.status', 'content.UpgradeGroup.type')
-});

http://git-wip-us.apache.org/repos/asf/ambari/blob/2f8de0be/ambari-web/app/views/main/admin/stack_upgrade/upgrade_wizard_view.js
----------------------------------------------------------------------
diff --git 
a/ambari-web/app/views/main/admin/stack_upgrade/upgrade_wizard_view.js 
b/ambari-web/app/views/main/admin/stack_upgrade/upgrade_wizard_view.js
index b3eaab7..d617437 100644
--- a/ambari-web/app/views/main/admin/stack_upgrade/upgrade_wizard_view.js
+++ b/ambari-web/app/views/main/admin/stack_upgrade/upgrade_wizard_view.js
@@ -52,6 +52,30 @@ App.upgradeWizardView = Em.View.extend({
   }.property('controller.upgradeData.Upgrade.progress_percent'),
 
   /**
+   * label of Upgrade status
+   * @type {string}
+   */
+  upgradeStatusLabel: function() {
+    switch (this.get('controller.upgradeData.Upgrade.request_status')) {
+      case 'PENDING':
+      case 'IN_PROGRESS':
+        return Em.I18n.t('admin.stackUpgrade.state.inProgress');
+        break;
+      case 'COMPLETED':
+        return Em.I18n.t('admin.stackUpgrade.state.completed');
+        break;
+      case 'HOLDING':
+        return Em.I18n.t('admin.stackUpgrade.state.paused');
+        break;
+      case 'FAILED':
+        return Em.I18n.t('admin.stackUpgrade.state.paused');
+        break;
+      default:
+        return ""
+    }
+  }.property('controller.upgradeData.Upgrade.request_status'),
+
+  /**
    * start polling upgrade data
    */
   startPolling: function () {
@@ -88,12 +112,5 @@ App.upgradeWizardView = Em.View.extend({
       self.get('controller').loadUpgradeData();
       self.doPolling();
     }, App.bgOperationsUpdateInterval));
-  },
-
-  /**
-   * @type {Array}
-   */
-  groups: function () {
-    return this.get('controller.upgradeData.upgrade_groups');
-  }.property('controller.upgradeData.upgrade_groups')
+  }
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/2f8de0be/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
index 104dfca..f20d9a4 100644
--- 
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
@@ -186,22 +186,28 @@ describe('App.MainAdminStackAndUpgradeController', 
function() {
   });
 
   describe("#loadUpgradeDataSuccessCallback()", function() {
+    beforeEach(function () {
+      sinon.stub(controller, 'updateUpgradeData', Em.K);
+    });
+    afterEach(function () {
+      controller.updateUpgradeData.restore();
+    });
     it("", function() {
       var data = {
         "Upgrade": {
           "request_status": "COMPLETED"
         },
         "upgrade_groups": [
-        {
-          "UpgradeGroup": {
-            "id": 1
-          },
-          "upgrade_items": []
-        }
-      ]};
+          {
+            "UpgradeGroup": {
+              "id": 1
+            },
+            "upgrade_items": []
+          }
+        ]};
       controller.loadUpgradeDataSuccessCallback(data);
-      expect(controller.get('upgradeData')).to.eql(data);
       expect(App.get('upgradeState')).to.equal('COMPLETED');
+      expect(controller.updateUpgradeData.called).to.be.true;
     });
   });
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/2f8de0be/ambari-web/test/views/main/admin/stack_upgrade/upgrade_group_view_test.js
----------------------------------------------------------------------
diff --git 
a/ambari-web/test/views/main/admin/stack_upgrade/upgrade_group_view_test.js 
b/ambari-web/test/views/main/admin/stack_upgrade/upgrade_group_view_test.js
new file mode 100644
index 0000000..f9002e8
--- /dev/null
+++ b/ambari-web/test/views/main/admin/stack_upgrade/upgrade_group_view_test.js
@@ -0,0 +1,40 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+var App = require('app');
+require('views/main/admin/stack_upgrade/upgrade_group_view');
+
+describe('App.upgradeGroupView', function () {
+  var view = App.upgradeGroupView.create({
+    content: Em.Object.create({})
+  });
+
+  describe("#isFailed", function () {
+    it("task is not failed", function () {
+      view.set('content.status', 'COMPLETED');
+      view.propertyDidChange('isFailed');
+      expect(view.get('isFailed')).to.be.false;
+    });
+    it("task is not failed", function () {
+      view.set('content.status', 'FAILED');
+      view.propertyDidChange('isFailed');
+      expect(view.get('isFailed')).to.be.true;
+    });
+  });
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/2f8de0be/ambari-web/test/views/main/admin/stack_upgrade/upgrade_task_view_test.js
----------------------------------------------------------------------
diff --git 
a/ambari-web/test/views/main/admin/stack_upgrade/upgrade_task_view_test.js 
b/ambari-web/test/views/main/admin/stack_upgrade/upgrade_task_view_test.js
deleted file mode 100644
index b40ea28..0000000
--- a/ambari-web/test/views/main/admin/stack_upgrade/upgrade_task_view_test.js
+++ /dev/null
@@ -1,61 +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');
-require('views/main/admin/stack_upgrade/upgrade_task_view');
-
-describe('App.upgradeTaskView', function () {
-  var view = App.upgradeTaskView.create({
-    content: Em.Object.create({
-      UpgradeGroup: {}
-    })
-  });
-
-  describe("#iconClass", function () {
-    it("status has icon", function () {
-      view.set('statusIconMap', {
-        'S1': 'icon1'
-      });
-      view.set('content.UpgradeGroup.status', 'S1');
-      view.propertyDidChange('iconClass');
-      expect(view.get('iconClass')).to.equal('icon1');
-    });
-    it("status undefined", function () {
-      view.set('statusIconMap', {
-        'S1': 'icon1'
-      });
-      view.set('content.UpgradeGroup.status', 'S2');
-      view.propertyDidChange('iconClass');
-      expect(view.get('iconClass')).to.equal('icon-question-sign');
-    });
-  });
-
-  describe("#isFailed", function () {
-    it("task is not failed", function () {
-      view.set('content.UpgradeGroup.status', 'COMPLETED');
-      view.propertyDidChange('isFailed');
-      expect(view.get('isFailed')).to.be.false;
-    });
-    it("task is not failed", function () {
-      view.set('content.UpgradeGroup.status', 'FAILED');
-      view.propertyDidChange('isFailed');
-      expect(view.get('isFailed')).to.be.true;
-    });
-  });
-});

Reply via email to