You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by al...@apache.org on 2015/05/01 08:42:56 UTC

[1/2] ambari git commit: AMBARI-10871. Ambari Views. CapSch View, config elements for ordering policies (alexantonenko)

Repository: ambari
Updated Branches:
  refs/heads/trunk 2c7e0340c -> 968ff91d9


http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/serializers.js
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/serializers.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/serializers.js
index a746eb8..4dfb5e5 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/serializers.js
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/serializers.js
@@ -82,36 +82,32 @@ App.SerializerMixin = Em.Mixin.create({
     return this.extractSave(store, type, payload);
   },
   extractQueue: function(data, props) {
-    var q = { name: data.name, parentPath: data.parentPath, depth: data.depth };
-    var prefix = this.PREFIX;
-
-    if (q.parentPath == null || q.parentPath.length == 0){
-        q.path = q.name;
-      } else {
-        q.path = q.parentPath + '.' + q.name;
-      }
-    q.id = q.path.dasherize();
-
-    var base_path = prefix + "." + q.path;
-
-    var labelsPath =                base_path + ".accessible-node-labels";
-    var qLabels;
-
-    q.unfunded_capacity =           props[base_path + ".unfunded.capacity"] || null;
-
-    q.state =                       props[base_path + ".state"] || null;
-    q.acl_administer_queue =        props[base_path + ".acl_administer_queue"] || null;
-    q.acl_submit_applications =     props[base_path + ".acl_submit_applications"] || null;
-
-    q.capacity =                    (props[base_path + ".capacity"])?+props[base_path + ".capacity"]:null;
-    q.maximum_capacity =            (props[base_path + ".maximum-capacity"])?+props[base_path + ".maximum-capacity"]:null;
-
-    q.user_limit_factor =           (props[base_path + ".user-limit-factor"])?+props[base_path + ".user-limit-factor"]:null;
-    q.minimum_user_limit_percent =  (props[base_path + ".minimum-user-limit-percent"])?+props[base_path + ".minimum-user-limit-percent"]:null;
-    q.maximum_applications =        (props[base_path + ".maximum-applications"])?+props[base_path + ".maximum-applications"]:null;
-    q.maximum_am_resource_percent = (props[base_path + ".maximum-am-resource-percent"])?+props[base_path + ".maximum-am-resource-percent"]:null;
-
-    //TODO what if didn't set??
+    var path = (data.parentPath == null || data.parentPath.length == 0)?data.name:[data.parentPath, data.name].join('.'),
+        base_path = this.PREFIX + "." + path,
+        labelsPath = base_path + ".accessible-node-labels",
+        q = {
+          id: path.toLowerCase(),
+          name: data.name,
+          path: path,
+          parentPath: data.parentPath,
+          depth: data.depth,
+          //sort queue list to avoid of parsing different sorting as changed value
+          queues:                        (props[base_path + ".queues"] || '').split(',').sort().join(',') || null,
+          unfunded_capacity:             props[base_path + ".unfunded.capacity"] || null,
+          state:                         props[base_path + ".state"] || null,
+          acl_administer_queue:          props[base_path + ".acl_administer_queue"] || null,
+          acl_submit_applications:       props[base_path + ".acl_submit_applications"] || null,
+          capacity:                      (props[base_path + ".capacity"])?+props[base_path + ".capacity"]:null,
+          maximum_capacity:              (props[base_path + ".maximum-capacity"])?+props[base_path + ".maximum-capacity"]:null,
+          user_limit_factor:             (props[base_path + ".user-limit-factor"])?+props[base_path + ".user-limit-factor"]:null,
+          minimum_user_limit_percent:    (props[base_path + ".minimum-user-limit-percent"])?+props[base_path + ".minimum-user-limit-percent"]:null,
+          maximum_applications:          (props[base_path + ".maximum-applications"])?+props[base_path + ".maximum-applications"]:null,
+          maximum_am_resource_percent:   (props[base_path + ".maximum-am-resource-percent"])?+props[base_path + ".maximum-am-resource-percent"]*100:null, // convert to percent
+          ordering_policy:               props[base_path + ".ordering-policy"] || null,
+          enable_size_based_weight:      props[base_path + ".ordering-policy.fair.enable-size-based-weight"] || null,
+          default_node_label_expression: props[base_path + ".default-node-label-expression"] || null,
+          labelsEnabled: props.hasOwnProperty(labelsPath)
+        };
 
     switch ((props.hasOwnProperty(labelsPath))?props[labelsPath]:'') {
       case '*':
@@ -133,28 +129,22 @@ App.SerializerMixin = Em.Mixin.create({
           return [q.id,labelName].join('.');
         }.bind(this)).compact();
         break;
-
     }
 
-    if (q.maximum_am_resource_percent)
-      q.maximum_am_resource_percent = q.maximum_am_resource_percent*100; // convert to percent
-
-    q.queues = props[prefix + "." + q.path + ".queues"] || null;
-
     return q;
   },
   normalizePayload: function (properties) {
-    if (properties.hasOwnProperty('queue')) {
+    if ((properties && properties.hasOwnProperty('queue')) || !properties) {
       return properties;
     }
     var labels = [], queues = [];
 
     var scheduler = [{
-      id:'scheduler',
-      maximum_am_resource_percent:properties[this.PREFIX + ".maximum-am-resource-percent"]*100, // convert to percent
-      maximum_applications:properties[this.PREFIX + ".maximum-applications"],
-      node_locality_delay:properties[this.PREFIX + ".node-locality-delay"],
-      resource_calculator:properties[this.PREFIX + ".resource-calculator"]
+      id:                          'scheduler',
+      maximum_am_resource_percent: properties[this.PREFIX + ".maximum-am-resource-percent"]*100 || null, // convert to percent
+      maximum_applications:        properties[this.PREFIX + ".maximum-applications"] || null,
+      node_locality_delay:         properties[this.PREFIX + ".node-locality-delay"] || null,
+      resource_calculator:         properties[this.PREFIX + ".resource-calculator"] || null
     }];
     _recurseQueues(null, "root", 0, properties, queues, this.get('store'));
     this._setupLabels(properties,queues,labels,this.PREFIX);
@@ -172,7 +162,8 @@ App.SerializerMixin = Em.Mixin.create({
         labels.push({
           id:labelId,
           capacity:properties.hasOwnProperty(cp)?+properties[cp]:0,
-          maximum_capacity:properties.hasOwnProperty(mcp)?+properties[mcp]:100
+          maximum_capacity:properties.hasOwnProperty(mcp)?+properties[mcp]:100,
+          queue:(queue.labels.contains([queue.id,label.name].join('.')))?queue.id:null
         });
       });
 
@@ -226,6 +217,12 @@ App.QueueSerializer = DS.RESTSerializer.extend(App.SerializerMixin,{
     json[this.PREFIX + "." + record.get('path') + ".state"] = record.get('state');
     json[this.PREFIX + "." + record.get('path') + ".capacity"] = record.get('capacity');
     json[this.PREFIX + "." + record.get('path') + ".queues"] = record.get('queues')||null;
+    json[this.PREFIX + "." + record.get('path') + ".default-node-label-expression"] = record.get('default_node_label_expression')||null;
+    json[this.PREFIX + "." + record.get('path') + ".ordering-policy"] = record.get('ordering_policy')||null;
+
+    if (record.get('ordering_policy') == 'fair') {
+      json[this.PREFIX + "." + record.get('path') + ".ordering-policy.fair.enable-size-based-weight"] = record.get('enable_size_based_weight');
+    }
 
     // do not set property if not set
     var ma = record.get('maximum_applications')||'';
@@ -252,6 +249,7 @@ App.QueueSerializer = DS.RESTSerializer.extend(App.SerializerMixin,{
   },
   serializeHasMany:function (record, json, relationship) {
     var key = relationship.key;
+    json[[this.PREFIX, record.get('path'), 'accessible-node-labels'].join('.')] = (record.get('labelsEnabled'))?'':null;
     record.get(key).map(function (l,idx,labels) {
       json[[this.PREFIX, record.get('path'), 'accessible-node-labels'].join('.')] = (record.get('accessAllLabels'))?'*':labels.mapBy('name').join(',');
       if (!record.get('store.nodeLabels').findBy('name',l.get('name')).notExist) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/store.js
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/store.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/store.js
index 1eb867e..3a61a3b 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/store.js
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/store.js
@@ -30,7 +30,7 @@ function _fetchTagged(adapter, store, type, sinceToken) {
 
     store.set('tag',store.get('current_tag'));
 
-    if (!Em.isArray(config.queue)) {
+    if (Em.isEmpty(config) || !Em.isArray(config.queue)) {
       return;
     }
 
@@ -45,21 +45,32 @@ function _fetchTagged(adapter, store, type, sinceToken) {
     });
 
     store.findAll('queue').then(function (queues) {
+      store.set('deletedQueues',[]);
       queues.forEach(function  (queue) {
         var new_version = config.queue.findBy('id',queue.id);
         if (new_version) {
           new_version['isNewQueue'] = queue.get('isNewQueue');
           store.findByIds('label',new_version.labels).then(function(labels) {
             labels.forEach(function (label){
-              label.setProperties(config.label.findBy('id',label.get('id')));
+              var props = config.label.findBy('id',label.get('id'));
+              label.eachAttribute(function (attr,meta) {
+                this.set(attr, props[attr]);
+              },label);
             });
-            queue.updateHasMany('labels',labels);
+            queue.get('labels').clear().pushObjects(labels);
             queue.set('version',v);
           });
-          delete new_version.labels;
-          queue.setProperties(new_version);
+          queue.eachAttribute(function (attr,meta) {
+            if (meta.type === 'boolean') {
+              this.set(attr, (new_version[attr] === 'false' || !new_version[attr])?false:true);
+            } else {
+              this.set(attr, new_version[attr]);
+            }
+          },queue);
         } else {
-          store.unloadRecord(queue);
+          if (Em.isEmpty(store.get('deletedQueues').findBy('path',queue.get('path')))) {
+            store.recurceRemoveQueue(queue);
+          }
         }
         config.queue.removeObject(new_version);
       });
@@ -97,6 +108,168 @@ App.ApplicationStore = DS.Store.extend({
 
   current_tag: '',
 
+  hasDeletedQueues:Em.computed.notEmpty('deletedQueues.[]'),
+
+  deletedQueues:[],
+
+  buildConfig: function (fmt) {
+    var records = [],
+        config = '',
+        props,
+        serializer = this.serializerFor('queue');
+
+    records.pushObjects(this.all('scheduler').toArray());
+    records.pushObjects(this.all('queue').toArray());
+    props = serializer.serializeConfig(records).properties;
+
+    if (fmt === 'txt') {
+      Object.keys(props).forEach(function (propKey) {
+        config += propKey + '=' + props[propKey] + '\n';
+      });
+    } else if (fmt === 'xml') {
+      Object.keys(props).forEach(function (propKey) {
+        config += '<property>\n' +
+        '  <name>' + propKey + '</name>\n' +
+        '  <value>' + props[propKey] + '</value>\n' +
+        '</property>\n';
+      });
+    }
+
+    return config;
+  },
+
+  recurceRemoveQueue: function (queue) {
+    if (Em.isEmpty(queue)) {
+      return;
+    } else if (!queue.get('isNewQueue') && !queue.get('isNew')) {
+      queue.get('queuesArray').forEach(function (queueName) {
+        this.recurceRemoveQueue(this.getById('queue',[queue.get('path'),queueName].join('.').toLowerCase()));
+      }.bind(this));
+
+      this.get('deletedQueues').pushObject(this.buildDeletedQueue(queue));
+
+    }
+    this.all('queue').findBy('path',queue.get('parentPath')).set('queuesArray',{'exclude':queue.get('name')});
+    return queue.destroyRecord();
+
+  },
+
+  buildDeletedQueue: function (queue) {
+    var deletedQueue = queue.serialize({clone:true});
+    delete deletedQueue.id;
+
+    Em.merge(deletedQueue,{
+      'isDeletedQueue': true,
+      'changedAttributes': queue.changedAttributes(),
+      'isLabelsDirty': queue.get('isLabelsDirty'),
+      'labelsEnabled': queue.labelsEnabled
+    });
+
+    deletedQueue.changedAttributes.labels = [queue.get('initialLabels').sort(),queue.get('labels').mapBy('id').sort()];
+
+    return Em.Object.create(deletedQueue);
+  },
+
+  saveAndUpdateQueue: function(record, updates){
+    return record.save().then(function (queue) {
+      if (updates) {
+        queue.eachAttribute(function (attr,meta) {
+          if (updates.changedAttributes.hasOwnProperty(attr)) {
+            this.set(attr, updates.changedAttributes[attr].objectAt(1));
+          }
+        },queue);
+
+        queue.eachRelationship(function (relationship,meta) {
+          queue.get(relationship).clear();
+          if (updates.changedAttributes.hasOwnProperty(relationship)) {
+            updates.changedAttributes[relationship][1].forEach(function (label) {
+              queue.get('labels').pushObject(queue.store.recordForId('label', [queue.get('path'),label.split('.').get('lastObject')].join('.')));
+            });
+          } else {
+            this.set(relationship, updates.get(relationship));
+          }
+
+          queue.notifyPropertyChange('labels');
+        },queue);
+
+      }
+      return queue;
+    }.bind(this));
+  },
+
+  createFromDeleted: function (deletedQueue) {
+    var newQueue = this.createRecord('queue', {
+      id: [deletedQueue.parentPath,deletedQueue.name].join('.').toLowerCase(),
+      name: deletedQueue.name,
+      parentPath: deletedQueue.parentPath,
+      depth: deletedQueue.parentPath.split('.').length
+    });
+
+    this.get('deletedQueues').removeObject(deletedQueue);
+
+    newQueue.eachAttribute(function (attr,meta) {
+      if (deletedQueue.changedAttributes.hasOwnProperty(attr)) {
+        this.set(attr, deletedQueue.changedAttributes[attr].objectAt(0));
+      } else {
+        this.set(attr, deletedQueue.get(attr));
+      }
+    },newQueue);
+
+    newQueue.eachRelationship(function (relationship,meta) {
+      if (deletedQueue.changedAttributes.hasOwnProperty(relationship)) {
+        deletedQueue.changedAttributes[relationship][0].forEach(function (label) {
+          newQueue.get('labels').pushObject(newQueue.store.recordForId('label', label));
+        });
+      } else {
+        this.set(relationship, deletedQueue.get(relationship));
+      }
+    },newQueue);
+
+    newQueue.notifyPropertyChange('labels');
+
+    newQueue.set('labelsEnabled',deletedQueue.labelsEnabled);
+
+    return newQueue;
+  },
+
+  copyFromDeleted: function (deletedQueue, parent, name) {
+    var newQueue = this.createRecord('queue', {
+      id: [parent,name].join('.').toLowerCase(),
+      name: name,
+      path: [parent,name].join('.'),
+      parentPath: parent,
+      depth: parent.split('.').length
+    });
+
+    newQueue.eachAttribute(function (attr,meta) {
+      if (!newQueue.changedAttributes().hasOwnProperty(attr)) {
+        if (deletedQueue.changedAttributes.hasOwnProperty(attr)) {
+          this.set(attr, deletedQueue.changedAttributes[attr].objectAt(0));
+        } else {
+          this.set(attr, deletedQueue[attr]);
+        }
+      }
+    },newQueue);
+
+    newQueue.set('isNewQueue',true);
+
+    newQueue.eachRelationship(function (relationship,meta) {
+      if (deletedQueue.changedAttributes.hasOwnProperty(relationship)) {
+        deletedQueue.changedAttributes[relationship][0].forEach(function (label) {
+          newQueue.get('labels').pushObject(newQueue.store.recordForId('label', [newQueue.get('path'),label.split('.').get('lastObject')].join('.')));
+        });
+      } else {
+        this.set(relationship, deletedQueue.get(relationship));
+      }
+    },newQueue);
+
+    newQueue.notifyPropertyChange('labels');
+
+    newQueue.set('labelsEnabled',deletedQueue.labelsEnabled);
+
+    return newQueue;
+  },
+
   nodeLabels: function () {
     var adapter = this.get('defaultAdapter');
     return Ember.ArrayProxy.extend(Ember.PromiseProxyMixin).create({
@@ -116,7 +289,9 @@ App.ApplicationStore = DS.Store.extend({
 
   flushPendingSave: function() {
     var pending = this._pendingSave.slice(),
-        newPending = [[]];
+        newPending = [[]],
+        notLabel = false,
+        isDeleteOperation = false;
 
     if (pending.length == 1) {
       this._super();
@@ -125,11 +300,25 @@ App.ApplicationStore = DS.Store.extend({
 
     pending.forEach(function (tuple) {
       var record = tuple[0], resolver = tuple[1];
+
       newPending[0].push(record);
-      newPending[1] = resolver;
+      //resolve previous resolver to fire susscess callback
+      if (record.constructor.typeKey !== 'label') {
+        if (newPending[1]) {
+          newPending[1].resolve(true);
+        }
+        newPending[1] = resolver;
+        notLabel = true;
+      } else {
+        resolver.resolve(true);
+      }
+      if (record.get('isDeleted')) {
+        isDeleteOperation = true;
+      }
     });
-
-    this._pendingSave = [newPending];
+    if (notLabel && !isDeleteOperation) {
+      this._pendingSave = [newPending];
+    }
     this._super();
   },
   didSaveRecord: function(record, data) {
@@ -160,5 +349,8 @@ App.ApplicationStore = DS.Store.extend({
   },
   checkOperator:function () {
     return this.get('defaultAdapter').getPrivilege();
+  },
+  checkCluster:function () {
+    return this.get('defaultAdapter').checkCluster();
   }
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/styles/application.less
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/styles/application.less b/contrib/views/capacity-scheduler/src/main/resources/ui/app/styles/application.less
index 4499097..82fbc07 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/styles/application.less
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/styles/application.less
@@ -53,6 +53,14 @@
 
 @import './../../bower_components/bootstrap/less/bootstrap.less';
 
+// ----- LOADER -----
+
+.loader {
+  h5 {
+    text-transform: uppercase;
+  }
+}
+
 // ----- TWO BUTTONS -----
 
 .add-queue {
@@ -75,9 +83,15 @@
       border-top-right-radius: 4px;
       border-bottom-right-radius: 4px;
     }
-    .dropdown-menu .btn {
-      border-radius: 0;
-      text-align: left;
+    .dropdown-menu {
+      .btn {
+        border-radius: 0;
+        text-align: left;
+      }
+      .btn-group-justified .btn {
+        text-align: center;
+        border: none;
+      }
     }
     .btn-group {
       width: 100%;
@@ -103,8 +117,11 @@
     }
   }
   .progress {
-    height: 3px;
-    margin: 0;
+    width: 40px;
+    display: inline-block;
+    //vertical-align: bottom;
+    height: 8px;
+    margin-bottom: 0;
   }
   .spacer {
     height: 25px;
@@ -120,7 +137,10 @@
   }
   .list-group-item {
     cursor: pointer;
-    .base-transition (background-color .1s linear 0s);
+    &.disabled {
+      color: #ccc;
+    }
+    //.base-transition (background-color .1s linear 0s);
   }
   .list-group-item.last {
     border-bottom-left-radius: 4px;
@@ -165,10 +185,18 @@
       display: inline-block;
       .input-int-width;
     }
+    .input-expand {
+      width: 85%;
+      display: inline-block;
+    }
+    .expanded-wrap .input-expand {
+      width: 92%;
+    }
     .input-percent-wrap {
       & > div {
         display: table-cell;
         .input-percent {
+          z-index: 1;
           .input-percent-width;
         }
         &.btn-group {
@@ -206,7 +234,7 @@
     margin-bottom: 20px;
     min-height: 20px;
     padding: 15px;
-    padding-top: 0px;
+    padding-top: 0;
 
     // ----- QUEUE -> HEADING ROW -----
 
@@ -290,9 +318,13 @@
             padding: 15px;
             background-color: #fefefe;
             .base-shadow(inset 0 0 2px #ccc);
+            a.labels-enabler {
+              color: #737373;
+            }
           }
 
           .input-percent {
+            z-index: 1;
             .input-percent-width;
           }
           .progress {
@@ -311,6 +343,15 @@
 
             padding-bottom: 4px;
 
+            .toggle-default-label{
+              color: #737373;
+            }
+
+            .label {
+              display: inline-block;
+              margin-bottom: 5px;
+            }
+
             &.active {
               .progress {
                 .base-shadow(0px 0px 0px #888888);
@@ -531,6 +572,7 @@
               }
             }
             .labels-toggle {
+              z-index: 1;
               margin-bottom: 5px;
               display: inline-table;
 
@@ -679,6 +721,7 @@
           & > div {
             display: table-cell;
             .input-percent {
+              z-index: 1;
               .input-percent-width;
             }
             &.btn-group {
@@ -749,9 +792,35 @@
   font-size: .9em;
 }
 
+.expanded-wrap {
+  position: relative;
+  @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {
+      height: 55px;
+    }
+    @media (min-width: @screen-lg-min) {
+      height: 30px;
+    }
+  .expanded {
+    @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {
+      top: 25px;
+    }
+    @media (min-width: @screen-lg-min) {
+      left: 40%;
+      top: -8px;
+    }
+    position: absolute;
+    width: 450px !important;
+    z-index: 4;
+    padding: 8px;
+    border-radius: 4px;
+    background-color: #fff;
+    box-shadow: 0 0 5px;
+  }
+}
+
 .btn {
   padding: 6px 10px;
-  .base-transition(background-color .08s linear 0s)
+  //.base-transition(background-color .08s linear 0s)
 }
 
 .input-error  {
@@ -775,3 +844,7 @@
 .modal-backdrop.in {
   opacity: 0;
 }
+
+.tooltip-label span:first-child {
+  cursor: help;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates.js
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates.js
index 4f08004..25e2aa9 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates.js
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates.js
@@ -20,6 +20,7 @@
 
 require('templates/error');
 require('templates/trace');
+require('templates/refuse');
 require('templates/loading');
 require('templates/queue');
 require('templates/queues');
@@ -36,3 +37,5 @@ require('templates/components/pathInput');
 require('templates/components/userGroupInput');
 require('templates/components/queueContainer');
 require('templates/components/dropdownConfirmation');
+require('templates/components/dropdownDownload');
+require('templates/components/queueBadge');

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capacityEditForm.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capacityEditForm.hbs b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capacityEditForm.hbs
index d5a3fb6..dd55396 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capacityEditForm.hbs
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capacityEditForm.hbs
@@ -20,7 +20,10 @@
 
   {{#if parentController.isOperator}}
     <div class="form-group">
-      <label>Capacity:</label>
+      {{tooltip-label
+        label='Capacity:'
+        message= 'The minimum guaranteed capacity as a percentage of total capacity that is allocated to the queue.'
+      }}
       <div class="form-group">
         <div class="input-group input-percent">
           {{capacity-input class='input-sm' value=this.capacity totalCapacity=view.totalCapacity queue=this maxVal=100}}
@@ -41,7 +44,10 @@
     <div class="rollback-wrap-ghost"></div>
 
     <div {{bind-attr class=":form-group this.isValid::has-error" }}>
-      <label>Max Capacity:</label>
+      {{tooltip-label
+        label='Max Capacity:'
+        message= 'The cap (maximum capacity) as a percentage of total capacity that this quire can utilize.'
+      }}
       <div class="form-group">
         <div class="input-group input-percent">
           {{max-capacity-input class='input-sm' value=this.maximum_capacity totalCapacity=view.totalCapacity queue=this maxVal=100}}
@@ -64,14 +70,20 @@
     </div>
   {{else}}
     <div class="form-group">
-      <label>Capacity:
+      {{#tooltip-label
+        label='Capacity:'
+        message= 'The minimum guaranteed capacity as a percentage of total capacity that is allocated to the queue.'
+      }}
         <span>{{capacity}}%</span>
-      </label>
+      {{/tooltip-label}}
     </div>
     <div class="form-group">
-      <label>Max Capacity:
+      {{#tooltip-label
+        label='Max Capacity:'
+        message= 'The cap (maximum capacity) as a percentage of total capacity that this quire can utilize.'
+      }}
         <span>{{maximum_capacity}}%</span>
-      </label>
+      {{/tooltip-label}}
     </div>
   {{capacity-bar capacityValue=capacity maxCapacityValue=maximum_capacity}}
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/dropdownConfirmation.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/dropdownConfirmation.hbs b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/dropdownConfirmation.hbs
index 8f3193b..be93c05 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/dropdownConfirmation.hbs
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/dropdownConfirmation.hbs
@@ -16,14 +16,14 @@
 * limitations under the License.
 }}
 
-{{#view view.button action="showRestartConfirmation" classBinding=":btn needRestart::disabled"}}<i class="fa fa-fw fa fa-cogs"></i> Save and Restart ResourceManager{{/view}}
+{{#view view.button action="showRestartConfirmation" classBinding=":btn isNotOperator:disabled needRestart::disabled"}}<i class="fa fa-fw fa fa-cogs"></i> Save and Restart ResourceManager{{/view}}
 {{#if view.restartConfirming}}
   <div class="btn-group btn-group-justified">
     <div class="btn-group">
-      <a {{action showRestartConfirmation target="view"}} class="btn btn-sm btn-danger"><i class="fa fa-fw fa-lg fa-times"></i> Cancel</a>
+      <a {{action 'showRestartConfirmation' target="view"}} class="btn btn-sm btn-danger"><i class="fa fa-fw fa-lg fa-times"></i> Cancel</a>
     </div>
     <div class="btn-group">
-      <a {{action 'confirm' target="view"}} class="btn btn-sm btn-success"><i class="fa fa-fw fa-lg fa-check"></i> Restart</a>
+      <a {{action 'confirm' 'restart' target="view"}} class="btn btn-sm btn-success"><i class="fa fa-fw fa-lg fa-check"></i> Restart</a>
     </div>
   </div>
 {{/if}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/dropdownDownload.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/dropdownDownload.hbs b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/dropdownDownload.hbs
new file mode 100644
index 0000000..3d5c03c
--- /dev/null
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/dropdownDownload.hbs
@@ -0,0 +1,29 @@
+{{!
+* 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.
+}}
+
+{{#view view.button action="showRestartConfirmation" classBinding=":btn"}}<i class="fa fa-fw fa fa-download"></i> Download config{{/view}}
+{{#if view.restartConfirming}}
+  <div class="btn-group btn-group-justified">
+    <div class="btn-group">
+      <a {{action 'confirm' 'txt' target="view"}} class="btn btn-sm btn-default">TXT</a>
+    </div>
+    <div class="btn-group">
+      <a {{action 'confirm' 'xml' target="view"}} class="btn btn-sm btn-default">XML</a>
+    </div>
+  </div>
+{{/if}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueBadge.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueBadge.hbs b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueBadge.hbs
new file mode 100644
index 0000000..7cea916
--- /dev/null
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueBadge.hbs
@@ -0,0 +1,22 @@
+{{!
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+}}
+
+{{#if warning}}
+  <i class="fa fa-fw fa-lg red fa-warning"></i>
+{{/if}}
+{{diff-tooltip queue=q isActive=tooltip classNameBindings="q.isSaving:fa-spin color icon"}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueContainer.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueContainer.hbs b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueContainer.hbs
index 77c33f6..cdc9044 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueContainer.hbs
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueContainer.hbs
@@ -18,16 +18,27 @@
 
 
 <div class="queue-container">
+  {{#if controller.isOperator}}
+    <div class="btn-group btn-group-xs pull-right">
+      <a href="#" {{action 'toggleProperty' 'labelsEnabled' this}} class="btn btn-default labels-enabler">
+        <i {{bind-attr class=":fa labelsEnabled:fa-check-square-o:fa-square-o"}}></i> Enable node labels
+      </a>
+    </div>
+  {{/if}}
   <div class="queue-capacity">
     <h5><strong>{{this.name}}</strong></h5>
     {{render "capacityEditForm" this}}
   </div>
-  <div class="labels-toggle-wrap">
-    <small> Node Labels Access</small>
+  <div {{bind-attr class=":labels-toggle-wrap labelsEnabled::hide"}}>
+      {{tooltip-label
+        tagName='small'
+        label='Node Labels Access'
+        message= 'Whether this queue has associated node labels that applications submitted to the queue can access.'
+      }}
       {{#if controller.isOperator}}
-      <div class="btn-group btn-group-xs labels-toggle-all" >
-        <button {{action 'toggleProperty' 'accessAllLabels' this}} type="button" {{bind-attr class=":btn this.accessAllLabels:btn-success:btn-default"}}><i class="fa fa-asterisk"></i></button>
-      </div>
+        <div class="btn-group btn-group-xs labels-toggle-all" >
+          <button {{action 'toggleProperty' 'accessAllLabels' this}} type="button" {{bind-attr class=":btn this.accessAllLabels:btn-success:btn-default"}}><i class="fa fa-asterisk"></i></button>
+        </div>
       {{else}}
         {{#if accessAllLabels}}
           <i class="fa fa-asterisk sign"></i>
@@ -48,7 +59,7 @@
     {{/each}}
   </div>
   {{#if this.labels}}
-    <div class="labels-capacity-wrap">
+    <div {{bind-attr class=":labels-capacity-wrap labelsEnabled::hide" }}>
       {{#each label in  this.sortedLabels}}
         <div class="label-capacity">
           <div class="queue-capacity">
@@ -57,6 +68,12 @@
               <small>Label is not exist on cluster</small>
             {{else}}
               <span {{bind-attr class=":label label.overCapacity:label-danger:label-success"}}>{{label.name}}</span>
+              <span {{bind-attr class=":label :label-default label.isDefault::hide"}}>default</span>
+
+              <div class="btn-group btn-group-xs pull-right">
+                <a  {{action 'toggleDefaultLabel' this label}} class="btn btn-default toggle-default-label"><i {{bind-attr class=":fa label.isDefault:fa-dot-circle-o:fa-circle-o"}}></i> Use by default</a>
+              </div>
+
             {{/if}}
             {{render "capacityEditForm" label}}
           </div>
@@ -64,4 +81,4 @@
       {{/each}}
     </div>
   {{/if}}
-</div>
\ No newline at end of file
+</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueListItem.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueListItem.hbs b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueListItem.hbs
index 423139e..5659781 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueListItem.hbs
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueListItem.hbs
@@ -17,48 +17,28 @@
 }}
 
 {{#each queue in view.leaf }}
-  {{#link-to "queue" queue classNameBindings=":list-group-item queue.overCapacity:list-group-item-danger queue.isNew:list-group-item-info" }}
+  {{#link-to "queue" queue classNameBindings=":list-group-item queue.overCapacity:list-group-item-danger queue.isNew:list-group-item-info" disabled=queue.isDeletedQueue }}
     <span class="col-md-offset-{{unbound queue.depth}} col-sm-offset-{{unbound queue.depth}} col-xs-offset-{{unbound queue.depth}}">
 
+    <div class="progress">
+      {{view 'view.capacityBarView' queue=queue}}
+    </div>
     {{queue.name}} ({{queue.capacity}}%)
 
     {{#if queue.version}}
         <span class="label label-info">v{{queue.version}}</span>
     {{/if}}
 
-    <span class="badge pull-right">
-      {{#if queue.overCapacity}}
-        <i class="fa fa-fw fa-lg red fa-warning"></i>
-      {{/if}}
-      {{#if queue.isNewQueue }}
-        {{#if queue.isSaving}}
-        <i class="fa fa-fw fa-lg gray fa-spinner fa-spin"></i>
-        {{else}}
-        <i class="fa fa-fw fa-lg blue fa-refresh"></i>
-        {{/if}}
-      {{else}}
-        {{#if queue.isError}}
-          {{#if queue.isSaving}}
-            <i class="fa fa-fw fa-lg gray fa-spinner fa-spin"></i>
-          {{else}}
-            <span> queue was not saved </span> <i class="fa fa-fw fa-lg red fa-warning"></i>
-          {{/if}}
-        {{else}}
-          {{#if queue.isAnyDirty}}
-            {{#if queue.isSaving}}
-              <i class="fa fa-fw fa-lg gray fa-spinner fa-spin"></i>
-            {{else}}
-              {{diff-tooltip queue=queue}}
-            {{/if}}
-          {{else}}
-            <i class="fa fa-fw fa-lg green fa-check"></i>
-          {{/if}}
-        {{/if}}
-      {{/if}}
-    </span>
+    {{queue-badge q=queue class="badge pull-right"}}
+
+    {{#if queue.isDeletedQueue}}
+      <span class="btn-group btn-group-xs pull-right">
+        <span  {{action 'addQ' queue.parentPath queue.name }} {{bind-attr class=":btn :btn-default view.parentIsDeleted:disabled" }} >Restore</span>
+      </span>
+    {{/if}}
 
     </span>
   {{/link-to}}
 
-  {{recurce-queues depth=view.childDepth parent=queue.path}}
+  {{recurce-queues depth=view.childDepth parent=queue.path parentIsDeleted=queue.isDeletedQueue}}
 {{/each}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/loading.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/loading.hbs b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/loading.hbs
index 8017bcf..6c0fd9f 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/loading.hbs
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/loading.hbs
@@ -16,4 +16,7 @@
    limitations under the License.
 }}
 
-<div class="spinner spinner-bg"></div>
+<div class="col-md-12 text-center loader">
+  <h1><i class="fa fa-spin fa-spinner"></i></h1>
+  <h5><small>{{content.message}}</small></h5>
+</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/queue.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/queue.hbs b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/queue.hbs
index 395f436..a6fdfd7 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/queue.hbs
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/queue.hbs
@@ -82,7 +82,11 @@
         <div class="panel-body">
           <form class="form-horizontal form-acl" role="form">
           <div class="form-group row">
-              <label class="col-lg-4 col-xs-4 control-label">State</label>
+              {{tooltip-label
+                class="col-lg-4 col-xs-4 control-label"
+                label='State'
+                message= 'Whether this queue is running or disabled. <br/> Running: Can accept applications <br/> Stopped: Cannot accept applications'
+              }}
               <div class="col-lg-6 col-md-6 col-sm-8 col-xs-7 control-value">
                 <div class="btn-group btn-group-xs" data-toggle="buttons" >
                   <label  {{action 'setState' 'running'}} {{bind-attr class=":btn isRunning:btn-success:btn-default isRunning:active isNotOperator:disabled" }}>
@@ -94,7 +98,7 @@
                </div>
                 {{#if queueDirtyFilelds.state}}
                 <div class="btn-group btn-group-xs" >
-                    <a {{action 'rollbackProp' 'state'}} href="#" class="btn btn-default btn-warning"><i class="fa fa-undo"></i></a>
+                    <a {{action 'rollbackProp' 'state' content}} href="#" class="btn btn-default btn-warning"><i class="fa fa-undo"></i></a>
                 </div>
                 {{/if}}
               </div>
@@ -103,7 +107,11 @@
           {{#if isOperator}}
 
             <div class="form-group row">
-              <label class="col-lg-4 col-xs-4 control-label">Administer Queue</label>
+              {{tooltip-label
+                class="col-lg-4 col-xs-4 control-label"
+                label='Administer Queue'
+                message='The access control list of users and groups that have authorization to perform administrative functions on this queue.'
+              }}
               <div class="col-lg-6 col-md-6 col-sm-8 col-xs-7 control-value">
                 <div class="btn-group btn-group-xs" data-toggle="buttons">
                   {{radio-button label="Anyone" selectionBinding="acl_administer_queue" value="*"}}
@@ -111,7 +119,7 @@
                 </div>
                 {{#if queueDirtyFilelds.acl_administer_queue}}
                 <div class="btn-group btn-group-xs" >
-                    <a {{action 'rollbackProp' 'acl_administer_queue'}} href="#" class="btn btn-default btn-warning"><i class="fa fa-undo"></i></a>
+                    <a {{action 'rollbackProp' 'acl_administer_queue' content}} href="#" class="btn btn-default btn-warning"><i class="fa fa-undo"></i></a>
                 </div>
                 {{/if}}
               </div>
@@ -121,7 +129,11 @@
             {{/unless}}
 
             <div class="form-group row">
-              <label class="col-lg-4 col-xs-4 control-label">Submit Applications</label>
+              {{tooltip-label
+                class="col-lg-4 col-xs-4 control-label"
+                label='Submit Applications'
+                message='The access control list of users and groups that have authorizatioN to submit applications to this queue.'
+              }}
               <div class="col-lg-6 col-md-6 col-sm-8 col-xs-7 control-value">
                 <div class="btn-group btn-group-xs" data-toggle="buttons">
                   {{radio-button label="Anyone" selectionBinding="acl_submit_applications" value="*"}}
@@ -129,7 +141,7 @@
                 </div>
                 {{#if queueDirtyFilelds.acl_submit_applications}}
                 <div class="btn-group btn-group-xs" >
-                    <a {{action 'rollbackProp' 'acl_submit_applications'}} href="#" class="btn btn-default btn-warning"><i class="fa fa-undo"></i></a>
+                    <a {{action 'rollbackProp' 'acl_submit_applications' content}} href="#" class="btn btn-default btn-warning"><i class="fa fa-undo"></i></a>
                 </div>
                 {{/if}}
               </div>
@@ -140,7 +152,11 @@
 
           {{else}}
             <div class="form-group">
-              <label class="col-lg-4 col-xs-4 control-label">Administer Queue</label>
+              {{tooltip-label
+                class="col-lg-4 col-xs-4 control-label"
+                label='Administer Queue'
+                message='The access control list of users and groups that have authorization to perform administrative functions on this queue.'
+              }}
               <div class="col-lg-8 col-xs-8 control-value">
                <p class="form-control-static">
                 {{escapeACL content.acl_administer_queue}}
@@ -149,7 +165,11 @@
             </div>
 
             <div class="form-group">
-              <label class="col-lg-4 col-xs-4 control-label">Submit Applications</label>
+              {{tooltip-label
+                class="col-lg-4 col-xs-4 control-label"
+                label='Submit Applications'
+                message='The access control list of users and groups that have authorizatioN to submit applications to this queue.'
+              }}
               <div class="col-lg-8 col-xs-8 control-value">
                <p class="form-control-static">
                 {{escapeACL content.acl_submit_applications}}
@@ -172,13 +192,17 @@
         <div class="panel-body">
           <form class="form-horizontal" role="form">
             <div class="form-group">
-              <label class="col-xs-6 control-label">User Limit Factor</label>
+              {{tooltip-label
+                class="col-xs-6 control-label"
+                label='User Limit Factor'
+                message='The upper limit multiple of the queue’s configured minimum capacity that one user’s applications can utilize.'
+              }}
               {{#if isOperator}}
                 <div class="col-xs-6 control-value">
                   {{int-input value=content.user_limit_factor maxlength=10 class="input-sm input-int" defaultVal=1 placeholder=1}}
                   {{#if queueDirtyFilelds.user_limit_factor}}
                   <div class="btn-group btn-group-xs" >
-                    <a {{action 'rollbackProp' 'user_limit_factor'}} href="#" class="btn btn-default btn-warning"><i class="fa fa-undo"></i></a>
+                    <a {{action 'rollbackProp' 'user_limit_factor' content}} href="#" class="btn btn-default btn-warning"><i class="fa fa-undo"></i></a>
                   </div>
                   {{/if}}
                 </div>
@@ -189,7 +213,11 @@
               {{/if}}
             </div>
             <div class="form-group">
-              <label class="col-xs-6 control-label">Minimum User Limit</label>
+              {{tooltip-label
+                class="col-xs-6 control-label"
+                label='Minimum User Limit'
+                message='The minimum guaranteed percentage of queue capacity allocated for a user\'s applications.'
+              }}
               {{#if isOperator}}
                 <div class="col-xs-6 control-value input-percent-wrap">
                   <div>
@@ -200,7 +228,7 @@
                   </div>
                   {{#if queueDirtyFilelds.minimum_user_limit_percent}}
                     <div class="btn-group btn-group-xs" >
-                        <a {{action 'rollbackProp' 'minimum_user_limit_percent'}} href="#" class="btn btn-default btn-warning"><i class="fa fa-undo"></i></a>
+                        <a {{action 'rollbackProp' 'minimum_user_limit_percent' content}} href="#" class="btn btn-default btn-warning"><i class="fa fa-undo"></i></a>
                     </div>
                   {{/if}}
                 </div>
@@ -215,13 +243,17 @@
               {{/if}}
             </div>
             <div class="form-group">
-              <label class="col-xs-6 control-label">Maximum Applications</label>
+              {{tooltip-label
+                class="col-xs-6 control-label"
+                label='Maximum Applications'
+                message='The maximum number of applications that can be running or pending in this specific queue at any point of time.'
+              }}
               {{#if isOperator}}
                 <div class="col-xs-6 control-value">
                   {{int-input placeholder="Inherited" maxlength=15 value=content.maximum_applications class="input-sm input-int"}}
                   {{#if queueDirtyFilelds.maximum_applications}}
                     <div class="btn-group btn-group-xs" >
-                      <a {{action 'rollbackProp' 'maximum_applications'}} href="#" class="btn btn-default btn-warning"><i class="fa fa-undo"></i></a>
+                      <a {{action 'rollbackProp' 'maximum_applications' content}} href="#" class="btn btn-default btn-warning"><i class="fa fa-undo"></i></a>
                     </div>
                   {{/if}}
                 </div>
@@ -236,7 +268,11 @@
               {{/if}}
             </div>
             <div class="form-group">
-              <label class="col-xs-6 control-label">Maximum AM Resource</label>
+                {{tooltip-label
+                  class="col-xs-6 control-label"
+                  label='Maximum AM Resource'
+                  message='The maximum percentage of total capacity of this specific queue that can be utilized by application masters at any point in time.'
+                }}
                 {{#if isOperator}}
                 <div class="col-xs-6 control-value input-percent-wrap">
                   <div>
@@ -247,7 +283,7 @@
                   </div>
                   {{#if queueDirtyFilelds.maximum_am_resource_percent}}
                     <div class="btn-group btn-group-xs" >
-                      <a {{action 'rollbackProp' 'maximum_am_resource_percent'}} href="#" class="btn btn-default btn-warning"><i class="fa fa-undo"></i></a>
+                      <a {{action 'rollbackProp' 'maximum_am_resource_percent' content}} href="#" class="btn btn-default btn-warning"><i class="fa fa-undo"></i></a>
                     </div>
                   {{/if}}
                 </div>
@@ -261,6 +297,68 @@
                 </div>
                 {{/if}}
             </div>
+            <div class="form-group">
+                {{tooltip-label
+                  class="col-xs-6 control-label"
+                  label='Ordering policy'
+                  message='The ordering policy to use for applications scheduled to this queue. <br/> FIFO: Applications get available capacity based on order they are submitted <br/> Fair: Applications will get fair share of capacity, regardless of order submitted'}}
+                {{#if isOperator}}
+                <div class="col-xs-6 control-value input-percent-wrap">
+                  <div>
+                    {{view Ember.Select class="form-control input-sm" content=orderingPolicyValues value=currentOP }}
+                  </div>
+                  {{#if queueDirtyFilelds.ordering_policy}}
+                    <div class="btn-group btn-group-xs" >
+                      <a {{action 'rollbackProp' 'ordering_policy' content}} href="#" class="btn btn-default btn-warning"><i class="fa fa-undo"></i></a>
+                    </div>
+                  {{/if}}
+                </div>
+                {{else}}
+                <div class="col-xs-6">
+                    <p class="form-control-static">{{content.ordering_policy}}</p>
+                </div>
+                {{/if}}
+            </div>
+            {{#if isFairOP}}
+              <div class="form-group">
+                {{tooltip-label
+                  class="col-xs-6 control-label"
+                  label='Enable Size Based Weight Ordering'
+                  message='If true, then Fair Ordering Policy will use resource needs of an application as way to prioritize capacity allocation - larger applications will get higher priority'
+                }}
+                  {{#if isOperator}}
+                  <div class="col-xs-6 control-value input-percent-wrap">
+                    <div>
+                      <div class="btn-group btn-group-sm pull-right">
+                        <a href="#" {{action 'toggleProperty' 'enable_size_based_weight' this}} class="btn btn-default labels-enabler">
+                          <i {{bind-attr class=":fa enable_size_based_weight:fa-check-square-o:fa-square-o"}}></i>
+                          {{#if enable_size_based_weight}}
+                            Enabled
+                          {{else}}
+                            Disabled
+                          {{/if}}
+                        </a>
+                      </div>
+                    </div>
+                    {{#if queueDirtyFilelds.enable_size_based_weight}}
+                      <div class="btn-group btn-group-xs" >
+                        <a {{action 'rollbackProp' 'enable_size_based_weight' content}} href="#" class="btn btn-default btn-warning"><i class="fa fa-undo"></i></a>
+                      </div>
+                    {{/if}}
+                  </div>
+                  {{else}}
+                  <div class="col-xs-6">
+                    <p class="form-control-static">
+                    {{#if enable_size_based_weight}}
+                      Enabled
+                    {{else}}
+                      Disabled
+                    {{/if}}
+                    </p>
+                  </div>
+                  {{/if}}
+              </div>
+            {{/if}}
           </form>
         </div>
       </div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/queues.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/queues.hbs b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/queues.hbs
index 2591fa5..b412d7b 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/queues.hbs
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/queues.hbs
@@ -30,30 +30,27 @@
         <div class="add-queue col-sm-6">
           <div class="btn-group btn-group-justified btn-group-save">
             <div class="btn-group">
-                <button type="button" {{bind-attr class=":btn :btn-success canNotSave:disabled :dropdown-toggle"}} data-toggle="dropdown">
-                  Actions
-                  <span class="caret"></span>
-                </button>
+                {{save-button data-toggle="dropdown"
+                  canNotSaveBinding='canNotSave'
+                  restartBinding='needRestart'
+                  refreshBinding='needRefresh'
+                  needSaveBinding='needSave'
+                  }}
                 <ul class="dropdown-menu pull-right" role="menu">
-                    {{dropdown-confirmation action='saveModal' targetObject=view needRestart=needRestart}}
-                      {{!-- {{#view view.button action='showRestartConfirmation' classBinding=":btn needRestart::disabled"}}<i class="fa fa-fw fa fa-cogs"></i> Save and Restart ResourceManager{{/view}}
-                      {{#if view.restartConfirming}}
-                        <div class="btn-group btn-group-justified">
-                          <div class="btn-group">
-                            <a {{action showRestartConfirmation target="view"}} class="btn btn-sm btn-danger"><i class="fa fa-fw fa-lg fa-times"></i> Cancel</a>
-                          </div>
-                          <div class="btn-group">
-                            <a {{action confirm target="view"}} class="btn btn-sm btn-success"><i class="fa fa-fw fa-lg fa-check"></i> Restart</a>
-                          </div>
-                        </div>
-                      {{/if}}
-                    {{/dropdown-confirmation}} --}}
+                  {{dropdown-buttons
+                    action='saveModal'
+                    layoutName='components/dropdownConfirmation'
+                    targetObject=view
+                    needRestart=needRestart
+                    isNotOperator=isNotOperator
+                  }}
                   <li >
-                    <a href="#" {{action 'saveModal' 'refresh' target="view"}} {{bind-attr class=":btn needRefresh::disabled"}}><i class="fa fa-fw fa-refresh"></i> Save and Refresh Queues</a>
+                    <a href="#" {{action 'saveModal' 'refresh' target="view"}} {{bind-attr class=":btn isNotOperator:disabled needRefresh::disabled"}}><i class="fa fa-fw fa-refresh"></i> Save and Refresh Queues</a>
                   </li>
                   <li>
-                    <a href="#" {{action 'saveModal' target="view"}} {{bind-attr class=":btn needSave::disabled"}}><i class="fa fa-fw fa-save"></i> Save Only</a>
+                    <a href="#" {{action 'saveModal' target="view"}} {{bind-attr class=":btn isNotOperator:disabled needSave::disabled"}}><i class="fa fa-fw fa-save"></i> Save Only</a>
                   </li>
+                  {{dropdown-buttons action='downloadConfig' layoutName='components/dropdownDownload'}}
                 </ul>
             </div>
           </div>
@@ -87,7 +84,7 @@
                   <span class="label label-success">Current</span>
                 {{/if}}
               </td>
-              <td>{{changed}}</td>
+              <td>{{timeAgo changed}}</td>
               <td>
                 <div class="btn-group btn-group-xs btn-block">
                   <button type="button" class="btn btn-default btn-xs btn-block" {{action 'loadTagged' tag}} >load</button>

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/refuse.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/refuse.hbs b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/refuse.hbs
new file mode 100644
index 0000000..8671d7d
--- /dev/null
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/refuse.hbs
@@ -0,0 +1,25 @@
+{{!
+* 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="col-md-12">
+  <div class="page-header">
+    <div class="pull-right"> {{#link-to 'queues'}} <i class="fa fa-refresh" ></i> Retry connection {{/link-to}} </div>
+    <h3>Couldn't connect to the cluster</h3>
+  </div>
+  <div>{{content.message}}</div>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/schedulerPanel.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/schedulerPanel.hbs b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/schedulerPanel.hbs
index b30f05b..a5385fc 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/schedulerPanel.hbs
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/schedulerPanel.hbs
@@ -20,21 +20,17 @@
   <div class="panel-heading">
     <div class="panel-title">
       Scheduler
-    {{#if scheduler.isDirty}}
-            {{#if scheduler.isSaving}}
-              <i class="fa fa-fw fa-lg gray fa-spinner fa-spin"></i>
-            {{else}}
-              {{diff-tooltip queue=scheduler}}
-            {{/if}}
-          {{else}}
-            <i class="fa fa-fw fa-lg green fa-check"></i>
-          {{/if}}
+      {{queue-badge q=scheduler class="pull-right"}}
     </div>
   </div>
   <div class="panel-body">
     <form class="form-horizontal" role="form">
       <div class="form-group">
-        <label class="col-xs-5 control-label">Maximum Applications</label>
+        {{tooltip-label
+          label='Maximum Applications'
+          class="col-xs-5 control-label"
+          message='For entire cluster, maximum number of applications that can be running or pending at any point of time'
+        }}
         {{#if isOperator}}
         <div class="col-xs-6 control-value">
           {{int-input value=scheduler.maximum_applications maxlength=15 class="input-sm input-int"}}
@@ -51,7 +47,11 @@
         {{/if}}
       </div>
       <div class="form-group">
-        <label class="col-xs-5 control-label">Maximum AM Resource</label>
+        {{tooltip-label
+          label='Maximum AM Resource'
+          class="col-xs-5 control-label"
+          message='For entire cluster, maximum percentage of total capacity that can be utilized by application masters at any point in time.'
+        }}
         {{#if isOperator}}
         <div class="col-xs-6 control-value input-percent-wrap">
           <div>
@@ -77,7 +77,11 @@
         {{/if}}
       </div>
       <div class="form-group">
-        <label class="col-xs-5 control-label">Node Locality Delay</label>
+        {{tooltip-label
+          label='Node Locality Delay'
+          class="col-xs-5 control-label"
+          message='Number of missed scheduling cycles after which the scheduler attempts to schedule rack-local containers.'
+        }}
         {{#if isOperator}}
           <div class="col-xs-6 control-value">
             {{int-input value=scheduler.node_locality_delay maxlength=10 class="input-sm input-int"}}
@@ -98,11 +102,14 @@
         {{/if}}
       </div>
       {{#if isOperator}}
-        {{#if scheduler.resource_calculator}}
           <div class="form-group">
-            <label class="col-xs-5 control-label">Calculator</label>
-            <div class="col-xs-12 control-value">
-              {{input value=scheduler.resource_calculator class="input-sm form-control"}}
+            {{tooltip-label
+              label='Calculator'
+              class="col-xs-5 control-label"
+              message='The method by which the scheduler calculates resource capacity across resource types.'
+            }}
+            <div class="col-xs-7 control-value">
+              {{expandable-input value=scheduler.resource_calculator class="input-sm form-control input-expand"}}
               {{#if schedulerDirtyFilelds.resource_calculator}}
                 <div class="btn-group btn-group-xs" >
                     <a {{action 'rollbackProp' 'resource_calculator' scheduler}} href="#" class="btn btn-default btn-warning"><i class="fa fa-undo"></i></a>
@@ -110,7 +117,6 @@
               {{/if}}
             </div>
           </div>
-        {{/if}}
       {{/if}}
     </form>
   </div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/bower.json
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/bower.json b/contrib/views/capacity-scheduler/src/main/resources/ui/bower.json
index ebfafe7..911856f 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/bower.json
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/bower.json
@@ -11,7 +11,9 @@
     "ember-i18n": "~1.6.0",
     "font-awesome": "~4.1",
     "bootstrap3-typeahead": "~3.0.3",
-    "perfect-scrollbar": "~0.5.8"
+    "perfect-scrollbar": "~0.5.8",
+    "file-saver": "*",
+    "Blob": "*"
   },
   "overrides": {
     "jquery": {
@@ -33,6 +35,12 @@
     },
     "font-awesome": {
       "main": "css/font-awesome.css"
+    },
+    "file-saver": {
+      "main": "FileSaver.js"
+    },
+    "Blob": {
+      "main": "Blob.js"
     }
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/view.xml
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/view.xml b/contrib/views/capacity-scheduler/src/main/resources/view.xml
index a986dc4..514548d 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/view.xml
+++ b/contrib/views/capacity-scheduler/src/main/resources/view.xml
@@ -20,6 +20,8 @@
     <version>0.3.0</version>
     <min-ambari-version>1.7.*</min-ambari-version>
 
+    <validator-class>org.apache.ambari.view.capacityscheduler.PropertyValidator</validator-class>
+
     <parameter>
         <name>ambari.server.url</name>
         <description>Enter the Ambari REST API cluster resource.</description>


[2/2] ambari git commit: AMBARI-10871. Ambari Views. CapSch View, config elements for ordering policies (alexantonenko)

Posted by al...@apache.org.
AMBARI-10871. Ambari Views. CapSch View, config elements for ordering policies (alexantonenko)


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

Branch: refs/heads/trunk
Commit: 968ff91d93933c3aa9671bdcd049f6fff5e57eee
Parents: 2c7e034
Author: Alex Antonenko <hi...@gmail.com>
Authored: Fri May 1 00:58:14 2015 +0300
Committer: Alex Antonenko <hi...@gmail.com>
Committed: Fri May 1 09:42:49 2015 +0300

----------------------------------------------------------------------
 .../capacityscheduler/ConfigurationService.java |  24 +++
 .../capacityscheduler/PropertyValidator.java    |  75 +++++++
 .../capacityscheduler/proxy/RequestBuilder.java |   5 +-
 .../proxy/ResponseTranslator.java               |  14 +-
 .../src/main/resources/ui/app/adapters.js       |  63 ++++--
 .../src/main/resources/ui/app/app.js            |  17 +-
 .../src/main/resources/ui/app/components.js     |   7 +-
 .../ui/app/components/capacityInput.js          |  10 +
 .../ui/app/components/clickElsewhere.js         |  12 +-
 .../resources/ui/app/components/diffTooltip.js  |  94 ++++++++
 .../ui/app/components/dropdownButtons.js        |  54 +++++
 .../ui/app/components/dropdownConfirmation.js   |  56 -----
 .../resources/ui/app/components/escapeAcl.js    |  49 -----
 .../resources/ui/app/components/queueBadge.js   |  83 ++++++++
 .../ui/app/components/queueListItem.js          |  92 +++-----
 .../resources/ui/app/components/radioButton.js  |  22 +-
 .../resources/ui/app/components/saveButton.js   |  54 +++++
 .../resources/ui/app/components/tooltipLabel.js |  41 ++++
 .../ui/app/components/totalCapacity.js          |  74 ++++---
 .../main/resources/ui/app/controllers/queue.js  | 126 ++++++-----
 .../main/resources/ui/app/controllers/queues.js | 130 ++++++------
 .../main/resources/ui/app/helpers/escapeAcl.js  |  49 +++++
 .../main/resources/ui/app/helpers/timeAgo.js    |  62 ++++++
 .../src/main/resources/ui/app/initialize.js     |   4 +
 .../src/main/resources/ui/app/models/queue.js   |  87 ++++++--
 .../src/main/resources/ui/app/router.js         |  82 +++++--
 .../src/main/resources/ui/app/serializers.js    |  84 ++++----
 .../src/main/resources/ui/app/store.js          | 212 ++++++++++++++++++-
 .../resources/ui/app/styles/application.less    |  89 +++++++-
 .../src/main/resources/ui/app/templates.js      |   3 +
 .../ui/app/templates/capacityEditForm.hbs       |  24 ++-
 .../components/dropdownConfirmation.hbs         |   6 +-
 .../templates/components/dropdownDownload.hbs   |  29 +++
 .../ui/app/templates/components/queueBadge.hbs  |  22 ++
 .../app/templates/components/queueContainer.hbs |  31 ++-
 .../app/templates/components/queueListItem.hbs  |  44 ++--
 .../main/resources/ui/app/templates/loading.hbs |   5 +-
 .../main/resources/ui/app/templates/queue.hbs   | 130 ++++++++++--
 .../main/resources/ui/app/templates/queues.hbs  |  37 ++--
 .../main/resources/ui/app/templates/refuse.hbs  |  25 +++
 .../ui/app/templates/schedulerPanel.hbs         |  40 ++--
 .../src/main/resources/ui/bower.json            |  10 +-
 .../src/main/resources/view.xml                 |   2 +
 43 files changed, 1605 insertions(+), 574 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/ConfigurationService.java
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/ConfigurationService.java b/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/ConfigurationService.java
index f703f65..8e1b6a6 100644
--- a/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/ConfigurationService.java
+++ b/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/ConfigurationService.java
@@ -157,6 +157,30 @@ public class ConfigurationService {
    * @return scheduler configuration
    */
   @GET
+  @Path("cluster")
+  @Produces(MediaType.APPLICATION_JSON)
+  public Response readClusterInfo() {
+    Response response = null;
+    try {
+      validateViewConfiguration();
+
+      JSONObject configurations = proxy.request(baseUrl).get().asJSON();
+      response = Response.ok(configurations).build();
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
+    }
+
+    return response;
+  }
+
+  /**
+   * Gets capacity scheduler configuration by all tags.
+   *
+   * @return scheduler configuration
+   */
+  @GET
   @Path("all")
   @Produces(MediaType.APPLICATION_JSON)
   public Response readAllConfigurations() {

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/PropertyValidator.java
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/PropertyValidator.java b/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/PropertyValidator.java
new file mode 100644
index 0000000..c7c7da9
--- /dev/null
+++ b/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/PropertyValidator.java
@@ -0,0 +1,75 @@
+/**
+ * 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.
+ */
+
+package org.apache.ambari.view.capacityscheduler;
+
+import org.apache.ambari.view.ViewInstanceDefinition;
+import org.apache.ambari.view.validation.ValidationResult;
+import org.apache.ambari.view.validation.Validator;
+
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+
+public class PropertyValidator implements Validator {
+
+  public static final String AMBARI_SERVER_URL = "ambari.server.url";
+
+  @Override
+  public ValidationResult validateInstance(ViewInstanceDefinition viewInstanceDefinition, ValidationContext validationContext) {
+    return null;
+  }
+
+  @Override
+  public ValidationResult validateProperty(String property, ViewInstanceDefinition viewInstanceDefinition, ValidationContext validationContext) {
+    if (property.equals(AMBARI_SERVER_URL)) {
+      String ambariServerUrl = viewInstanceDefinition.getPropertyMap().get(AMBARI_SERVER_URL);
+      URL url = null;
+      try {
+        url = new URL(ambariServerUrl);
+        url.toURI();
+      } catch (MalformedURLException e) {
+        return new InvalidPropertyValidationResult(false, "Must be valid URL");
+      } catch (URISyntaxException e) {
+        return new InvalidPropertyValidationResult(false, "Must be valid URL");
+      }
+    }
+    return ValidationResult.SUCCESS;
+  }
+
+  public static class InvalidPropertyValidationResult implements ValidationResult {
+    private boolean valid;
+    private String detail;
+
+    public InvalidPropertyValidationResult(boolean valid, String detail) {
+      this.valid = valid;
+      this.detail = detail;
+    }
+
+    @Override
+    public boolean isValid() {
+      return valid;
+    }
+
+    @Override
+    public String getDetail() {
+      return detail;
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/proxy/RequestBuilder.java
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/proxy/RequestBuilder.java b/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/proxy/RequestBuilder.java
index 420cb35..88e19bb 100644
--- a/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/proxy/RequestBuilder.java
+++ b/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/proxy/RequestBuilder.java
@@ -27,6 +27,7 @@ import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.net.UnknownHostException;
 import java.util.HashMap;
 
 /**
@@ -99,8 +100,10 @@ public class RequestBuilder {
     InputStream inputStream = null;
     try {
       inputStream = urlStreamProvider.readFrom(url, method, data, headers);
+    } catch (UnknownHostException e) {
+      throw new ServiceFormattedException(e.getMessage() + " is unknown host. Check Capacity-Scheduler instance properties.", e);
     } catch (IOException e) {
-      throw new ServiceFormattedException(e.getMessage(), e);
+      throw new ServiceFormattedException(e.toString(), e);
     }
     return new ResponseTranslator(inputStream);
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/proxy/ResponseTranslator.java
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/proxy/ResponseTranslator.java b/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/proxy/ResponseTranslator.java
index f2eca3c..8e3b4a6 100644
--- a/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/proxy/ResponseTranslator.java
+++ b/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/proxy/ResponseTranslator.java
@@ -75,7 +75,19 @@ public class ResponseTranslator {
    */
   public JSONObject asJSON() {
     String jsonString = asString();
-    return (JSONObject) JSONValue.parse(jsonString);
+    JSONObject jsonObject = (JSONObject) JSONValue.parse(jsonString);
+    if (jsonObject.get("status") != null && (Long)jsonObject.get("status") >= 400L) {
+      // Throw exception if HTTP status is not OK
+      String message;
+      if (jsonObject.containsKey("message")) {
+        message = (String) jsonObject.get("message");
+      } else {
+        message = "without message";
+      }
+      throw new ServiceFormattedException("Proxy: Server returned error " + jsonObject.get("status") + " " +
+          message + ". Check Capacity-Scheduler instance properties.");
+    }
+    return jsonObject;
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/adapters.js
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/adapters.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/adapters.js
index 07d6fbd..40e6bfd 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/adapters.js
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/adapters.js
@@ -47,8 +47,12 @@ App.QueueAdapter = DS.Adapter.extend({
   queues: [],
 
   createRecord: function(store, type, record) {
-    var data = record.toJSON({ includeId: true });
+    var data = record.serialize({clone:true});
     return new Ember.RSVP.Promise(function(resolve, reject) {
+      if (type.typeKey === 'label') {
+        Ember.run(record, resolve, {'label':data});
+        return;
+      }
       return store.filter('queue',function (q) {
         return q.id === record.id;
       }).then(function (queues) {
@@ -63,16 +67,17 @@ App.QueueAdapter = DS.Adapter.extend({
           store.recordWasInvalid(record, error.errors);
           return;
         }
+        data.labelsEnabled = record.get('labelsEnabled');
         Ember.run(record, resolve, {'queue':data,'label':[]});
 
       });
-    });
+    },'App: QueueAdapter#createRecord ' + type + ' ' + record.id);
   },
 
   deleteRecord:function (store, type, record) {
     return new Ember.RSVP.Promise(function(resolve, reject) {
       Ember.run(null, resolve, {'queue':record.serialize({ includeId: true , clone: true })});
-    });
+    },'App: QueueAdapter#deleteRecord ' + type + ' ' + record.id);
   },
 
   saveMark:'',
@@ -114,7 +119,7 @@ App.QueueAdapter = DS.Adapter.extend({
           adapter.postSave(postSaveUri);
         }
       });
-    });
+    },'App: QueueAdapter#updateRecord save config woth ' + new_tag + ' tag');
   },
 
   postSave:function(uri){
@@ -129,13 +134,16 @@ App.QueueAdapter = DS.Adapter.extend({
     id = id.toLowerCase();
     var record = store.getById(type,id);
     var key = type.typeKey;
+    var json = {};
     if (record) {
       return new Ember.RSVP.Promise(function(resolve, reject) {
-        resolve({key:record.toJSON({includeId:true})});
-      });
+        json[key] = record.toJSON({includeId:true});
+        resolve(json);
+      },'App: QueueAdapter#find ' + type + ' ' + id);
     } else {
       return store.findAll('queue').then(function (queues) {
-        resolve({key:store.getById(type,id).toJSON({includeId:true})});
+        json[key] = store.getById(type,id).toJSON({includeId:true});
+        resolve(json);
       });
     }
   },
@@ -162,12 +170,13 @@ App.QueueAdapter = DS.Adapter.extend({
         jqXHR.then = null;
         Ember.run(null, reject, jqXHR);
       });
-    });
+    },'App: QueueAdapter#findAll ' + type);
   },
 
   findAllTagged: function(store, type) {
     var adapter = this,
-        uri = [_getCapacitySchedulerViewUri(this),'byTag',store.get('tag')].join('/');
+        tag = store.get('tag'),
+        uri = [_getCapacitySchedulerViewUri(this),'byTag',tag].join('/');
 
     return new Ember.RSVP.Promise(function(resolve, reject) {
       adapter.ajax(uri,'GET').then(function(data) {
@@ -176,7 +185,7 @@ App.QueueAdapter = DS.Adapter.extend({
         jqXHR.then = null;
         Ember.run(null, reject, jqXHR);
       });
-    });
+    },'App: QueueAdapter#findAllTagged ' + tag);
   },
 
   getNodeLabels:function () {
@@ -185,11 +194,13 @@ App.QueueAdapter = DS.Adapter.extend({
     return new Ember.RSVP.Promise(function(resolve, reject) {
       this.ajax(uri,'GET').then(function(data) {
         var parsedData = JSON.parse(data), labels;
-          if (parsedData && Em.isArray(parsedData.nodeLabels)) {
-            labels = parsedData.nodeLabels;
-          } else {
-            labels = (parsedData && parsedData.nodeLabels)?[parsedData.nodeLabels]:[];
-          }
+
+        if (parsedData && Em.isArray(parsedData.nodeLabels)) {
+          labels = parsedData.nodeLabels;
+        } else {
+          labels = (parsedData && parsedData.nodeLabels)?[parsedData.nodeLabels]:[];
+        }
+
         Ember.run(null, resolve, labels.map(function (label) {
           return {name:label};
         }));
@@ -197,7 +208,7 @@ App.QueueAdapter = DS.Adapter.extend({
         jqXHR.then = null;
         Ember.run(null, reject, jqXHR);
       });
-    }.bind(this));
+    }.bind(this),'App: QueueAdapter#getNodeLabels');
   },
 
   getPrivilege:function () {
@@ -211,7 +222,21 @@ App.QueueAdapter = DS.Adapter.extend({
         jqXHR.then = null;
         Ember.run(null, reject, jqXHR);
       });
-    }.bind(this));
+    }.bind(this),'App: QueueAdapter#getPrivilege');
+  },
+
+  checkCluster:function () {
+    var uri = [_getCapacitySchedulerViewUri(this),'cluster'].join('/');
+    if (App.testMode)
+      uri = uri + ".json";
+    return new Ember.RSVP.Promise(function(resolve, reject) {
+      this.ajax(uri,'GET').then(function(data) {
+        Ember.run(null, resolve, data);
+      }, function(jqXHR) {
+        jqXHR.then = null;
+        Ember.run(null, reject, jqXHR);
+      });
+    }.bind(this),'App: QueueAdapter#checkCluster');
   },
 
   ajax: function(url, type, hash) {
@@ -229,7 +254,7 @@ App.QueueAdapter = DS.Adapter.extend({
       };
 
       Ember.$.ajax(hash);
-    }, "DS: RestAdapter#ajax " + type + " to " + url);
+    }, "App: QueueAdapter#ajax " + type + " to " + url);
   },
 
   ajaxOptions: function(url, type, hash) {
@@ -278,7 +303,7 @@ App.TagAdapter = App.QueueAdapter.extend({
         jqXHR.then = null;
         Ember.run(null, reject, jqXHR);
       });
-    });
+    }, "App: TagAdapter#findAll " + type);
   }
 });
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/app.js
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/app.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/app.js
index 33759f7..4349538 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/app.js
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/app.js
@@ -16,4 +16,19 @@
  * limitations under the License.
  */
 
-module.exports = Em.Application.create();
\ No newline at end of file
+var FileSaver = Ember.Object.extend({
+  save: function(fileContents, mimeType, filename) {
+    window.saveAs(new Blob([fileContents], {type: mimeType}), filename);
+  }
+});
+
+Ember.Application.initializer({
+  name: 'file-saver',
+
+  initialize: function(container, application) {
+    container.register('file-saver:main', FileSaver);
+    container.injection('controller', 'fileSaver', 'file-saver:main');
+  }
+});
+
+module.exports = Em.Application.create();

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/components.js
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components.js
index d1279d7..ac679b7 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components.js
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components.js
@@ -22,8 +22,11 @@ require('components/capacityInput');
 require('components/totalCapacity');
 require('components/queueListItem');
 require('components/pathInput');
+require('components/saveButton');
 require('components/radioButton');
 require('components/userGroupInput');
-require('components/escapeAcl');
 require('components/confirmDelete');
-require('components/dropdownConfirmation');
+require('components/dropdownButtons');
+require('components/queueBadge');
+require('components/diffTooltip');
+require('components/tooltipLabel');

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/capacityInput.js
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/capacityInput.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/capacityInput.js
index 8ac766d..3f3999e 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/capacityInput.js
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/capacityInput.js
@@ -37,6 +37,16 @@ App.FocusInputComponent = Ember.TextField.extend({
   }
 });
 
+App.ExpandableInputComponent = Em.TextField.extend({
+  classNameBindings:['expanded'],
+  focusIn:function  (argument) {
+    this.$().parent().addClass('expanded').parent().addClass('expanded-wrap');
+  },
+  focusOut:function  (argument) {
+    this.$().parent().removeClass('expanded').parent().removeClass('expanded-wrap');
+  }
+});
+
 App.IntInputComponent = Ember.TextField.extend({
   classNames:['form-control'],
   maxVal:null,

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/clickElsewhere.js
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/clickElsewhere.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/clickElsewhere.js
index 879319a..7412187 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/clickElsewhere.js
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/clickElsewhere.js
@@ -18,17 +18,11 @@
 
 var App = require('app');
 
-var bound;
-
-bound = function(fnName) {
-  return Ember.computed(fnName,function() {
-    return this.get(fnName).bind(this);
-  });
-};
-
 App.ClickElsewhereMixin = Ember.Mixin.create({
   onClickElsewhere: Ember.K,
-  clickHandler: bound("elsewhereHandler"),
+  clickHandler: Ember.computed('elsewhereHandler',function() {
+    return this.get('elsewhereHandler').bind(this);
+  }),
   elsewhereHandler: function(e) {
     var $target, element, thisIsElement;
     element = this.get("element");

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/diffTooltip.js
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/diffTooltip.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/diffTooltip.js
new file mode 100644
index 0000000..1f26c2b
--- /dev/null
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/diffTooltip.js
@@ -0,0 +1,94 @@
+/**
+ * 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.DiffTooltipComponent = Em.Component.extend({
+  classNames:'fa fa-fw fa-lg'.w(),
+  tagName:'i',
+  queue:null,
+  isActive:true,
+  toggleTooltip: function () {
+    if (this.get('isActive')) {
+      this.$().tooltip({
+        title:this.buildDiff.bind(this),
+        html:true,
+        placement:'bottom'
+      });
+    } else {
+      this.$().tooltip('destroy');
+    }
+  }.observes('isActive').on('didInsertElement'),
+  buildDiff: function () {
+    var queue = this.get('queue'),
+        caption = '',
+        fmtString = '<span>%@: %@ -> %@</span>\n',
+        emptyValue = '<small><em>not set</em></small>',
+        changes = queue.changedAttributes(),
+        idsToNames = function (l) {
+          return l.split('.').get('lastObject');
+        },
+        formatChangedAttributes = function (prefix,item) {
+          // don't show this to user.
+          if (item == '_accessAllLabels') return;
+
+          var oldV = this[item].objectAt(0),
+              newV = this[item].objectAt(1);
+
+          caption += fmtString.fmt(
+              [prefix,item].compact().join('.'),
+              (oldV != null && '\'%@\''.fmt(oldV)) || emptyValue,
+              (newV != null && '\'%@\''.fmt(newV)) || emptyValue
+            );
+        },
+        initialLabels,
+        currentLabels,
+        isAllChanged,
+        oldV,
+        newV;
+
+    if (queue.get('isError')) {
+      return 'Data was not saved';
+    }
+
+    Em.keys(changes).forEach(Em.run.bind(changes,formatChangedAttributes,null));
+
+    if (queue.constructor.typeKey === 'queue') {
+      //cpmpare labels
+      isAllChanged = changes.hasOwnProperty('_accessAllLabels');
+      initialLabels = queue.get('initialLabels').sort();
+      currentLabels = queue.get('labels').mapBy('id').sort();
+
+      if (queue.get('isLabelsDirty') || isAllChanged) {
+
+        oldV = ((isAllChanged && changes._accessAllLabels.objectAt(0)) || (queue.get('accessAllLabels') && !isAllChanged))?'*':initialLabels.map(idsToNames).join(',') || emptyValue;
+        newV = ((isAllChanged && changes._accessAllLabels.objectAt(1)) || (queue.get('accessAllLabels') && !isAllChanged))?'*':currentLabels.map(idsToNames).join(',') || emptyValue;
+
+        caption += fmtString.fmt('accessible-node-labels', oldV, newV);
+      }
+
+      queue.get('labels').forEach(function (label) {
+        var labelsChanges = label.changedAttributes(),
+            prefix = ['accessible-node-labels',label.get('name')].join('.');
+        Em.keys(labelsChanges).forEach(Em.run.bind(labelsChanges,formatChangedAttributes,prefix));
+      });
+    }
+
+    return caption;
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/dropdownButtons.js
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/dropdownButtons.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/dropdownButtons.js
new file mode 100644
index 0000000..cc15563
--- /dev/null
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/dropdownButtons.js
@@ -0,0 +1,54 @@
+/**
+ * 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.DropdownButtonsComponent = Em.Component.extend(App.ClickElsewhereMixin,{
+  tagName:'li',
+  restartConfirming:false,
+  onClickElsewhere:function () {
+    this.set('restartConfirming',false);
+    this.$().parents('.dropdown-menu').parent().removeClass('open');
+  },
+  dropdownHideControl:function () {
+    this.$().parents('.dropdown-menu').parent().on(
+      "hide.bs.dropdown", function() {
+        return !this.get('restartConfirming');
+      }.bind(this));
+  }.on('didInsertElement'),
+  button:Em.Component.extend({
+    tagName:'a',
+    click:function (event) {
+      event.stopPropagation();
+      this.triggerAction({
+        action: 'showRestartConfirmation',
+        target: this.get('parentView'),
+        actionContext: this.get('context')
+      });
+    }
+  }),
+  actions:{
+    showRestartConfirmation: function() {
+      this.toggleProperty('restartConfirming');
+    },
+    confirm: function (arg) {
+      this.set('restartConfirming',false);
+      this.sendAction('action',arg);
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/dropdownConfirmation.js
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/dropdownConfirmation.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/dropdownConfirmation.js
deleted file mode 100644
index d86e3a0..0000000
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/dropdownConfirmation.js
+++ /dev/null
@@ -1,56 +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.DropdownConfirmationComponent = Em.Component.extend(App.ClickElsewhereMixin,{
-  layoutName:'components/dropdownConfirmation',
-  tagName:'li',
-  restartConfirming:false,
-  actions:{
-    showRestartConfirmation: function() {
-      this.toggleProperty('restartConfirming');
-    },
-    confirm: function() {
-      this.set('restartConfirming',false);
-      this.sendAction('action','restart');
-    }
-  },
-  onClickElsewhere:function () {
-    this.set('restartConfirming',false);
-    this.$().parents('.dropdown-menu').parent().removeClass('open');
-  },
-  dropdownHideControl:function () {
-    this.$().parents('.dropdown-menu').parent().on(
-      "hide.bs.dropdown", function() {
-        return !this.get('restartConfirming');
-      }.bind(this));
-  }.on('didInsertElement'),
-  button:Em.Component.extend({
-    tagName:'a',
-    click:function () {
-      this.triggerAction({
-        action: 'showRestartConfirmation',
-        target: this.get('parentView'),
-        actionContext: this.get('context')
-      });
-    }
-  }),
-  needRestart:false
-});

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/escapeAcl.js
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/escapeAcl.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/escapeAcl.js
deleted file mode 100644
index 125b037..0000000
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/escapeAcl.js
+++ /dev/null
@@ -1,49 +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.
- */
-
-
-Ember.Handlebars.helper('escapeACL', function(value) {
-  var output = '';
-
-  value = value || '';
-
-  if (value.trim() == '') {
-    output = '<span class="label label-danger"> <i class="fa fa-ban fa-fw"></i>  Nobody </span> ';
-  } else if (value.trim() == '*') {
-    output = '<label class="label label-success"> <i class="fa fa-asterisk fa-fw"></i> Anyone</label>';
-  } else {
-    var ug = value.split(' ');
-    var users = ug[0].split(',')||[];
-    var groups = (ug.length == 2)?ug[1].split(',')||[]:[];
-
-    output += ' <span class="users"> ';
-
-    users.forEach(function (user) {
-      output += (user)?'<span class="label label-primary"><i class="fa fa-user fa-fw"></i> '+ user +'</span> ':'';
-    });
-
-    output += ' </span> <span class="groups"> ';
-
-    groups.forEach(function (group) {
-      output += (group)?'<span class="label label-primary"><i class="fa fa-users fa-fw"></i> '+ group +'</span> ':'';
-    });
-
-    output += ' </span> ';
-  }
-  return new Ember.Handlebars.SafeString(output);
-});

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueBadge.js
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueBadge.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueBadge.js
new file mode 100644
index 0000000..dad4180
--- /dev/null
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueBadge.js
@@ -0,0 +1,83 @@
+/**
+ * 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.QueueBadgeComponent = Em.Component.extend({
+  layoutName:'components/queueBadge',
+  tagName:'span',
+  q: null,
+
+  loadedQ: Em.computed.not('q.isSaving'),
+  tooltip: Em.computed.and('loadedQ','q.isAnyDirty'),
+
+  warning: Em.computed.alias('q.overCapacity'),
+
+  color:function () {
+    var q = this.get('q'),
+        color;
+
+    switch (true) {
+      case (q.get('isDeletedQueue')):
+        color = 'red';
+        break;
+      case (q.get('isSaving')):
+        color = 'gray';
+        break;
+      case (q.get('isNewQueue')):
+        color = 'blue';
+        break;
+      case (q.get('isError')):
+        color = 'red';
+        break;
+      case (q.get('isAnyDirty')):
+        color = 'blue';
+        break;
+      default:
+        color = 'green';
+    }
+
+    return color;
+  }.property('q.isNewQueue','q.isSaving','q.isError','q.isAnyDirty','q.isDeletedQueue'),
+  icon:function () {
+    var q = this.get('q'),
+        icon;
+
+    switch (true) {
+      case (q.get('isDeletedQueue')):
+        icon = 'fa-minus';
+        break;
+      case (q.get('isSaving')):
+        icon = 'fa-spinner';
+        break;
+      case (q.get('isNewQueue')):
+        icon = 'fa-refresh';
+        break;
+      case (q.get('isError')):
+        icon = 'fa-warning';
+        break;
+      case (q.get('isAnyDirty')):
+        icon = 'fa-pencil';
+        break;
+      default:
+        icon = 'fa-check';
+    }
+
+    return icon;
+  }.property('q.isNewQueue','q.isSaving','q.isError','q.isAnyDirty','q.isDeletedQueue')
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueListItem.js
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueListItem.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueListItem.js
index fc52eb9..a281e96 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueListItem.js
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueListItem.js
@@ -19,11 +19,13 @@
 var App = require('app');
 
 App.RecurceQueuesComponent = Em.View.extend({
-  templateName: "components/queueListItem",
+  templateName: 'components/queueListItem',
   depth:0,
   parent:'',
+  parentIsDeleted:false,
+  queues: Ember.computed.union('controller.arrangedContent', 'controller.store.deletedQueues'),
   leaf:function () {
-    return this.get('controller.arrangedContent')
+    return this.get('queues')
       .filterBy('depth',this.get('depth'))
       .filterBy('parentPath',this.get('parent'));
   }.property('depth','parent','controller.content.length','controller.content.@each.name'),
@@ -31,77 +33,37 @@ App.RecurceQueuesComponent = Em.View.extend({
     return this.get('leaf.firstObject.depth')+1;
   }.property('depth'),
   didInsertElement:function () {
-    Ember.run.scheduleOnce('afterRender',null, this.setFirstAndLast, this);
+    Ember.run.scheduleOnce('afterRender', null, this.setFirstAndLast, this);
   },
   setFirstAndLast:function (item) {
     var items = item.$().parents('.queue-list').find('.list-group-item');
     items.first().addClass('first');
     items.last().addClass('last');
-  }
-
-});
-
-App.DiffTooltipComponent = Em.Component.extend({
-  classNames:'fa fa-fw fa-lg blue fa-pencil'.w(),
-  tagName:'i',
-  queue:null,
-  initTooltip:function  () {
-    var queue = this.get('queue');
-    this.$().tooltip({
-        title:function () {
-          var caption = '',
-              fmtString = '<span>%@: %@ -> %@</span>\n',
-              emptyValue = '<small><em>not set</em></small>',
-              changes = queue.changedAttributes(),
-              idsToNames = function (l) {
-                return l.split('.').get('lastObject');
-              },
-              formatChangedAttributes = function (prefix,item) {
-                // don't show this to user.
-                if (item == '_accessAllLabels') return;
-
-                var oldV = this[item].objectAt(0),
-                    newV = this[item].objectAt(1);
-
-                caption += fmtString.fmt(
-                    [prefix,item].compact().join('.'),
-                    (oldV != null && '\'%@\''.fmt(oldV)) || emptyValue,
-                    (newV != null && '\'%@\''.fmt(newV)) || emptyValue
-                  );
-              },
-              initialLabels,
-              currentLabels,
-              isAllChanged,
-              oldV,
-              newV;
-
-          Em.keys(changes).forEach(Em.run.bind(changes,formatChangedAttributes,null));
-
-          if (queue.constructor.typeKey === 'queue') {
-            //cpmpare labels
-            isAllChanged = changes.hasOwnProperty('_accessAllLabels');
-            initialLabels = queue.get('initialLabels').sort();
-            currentLabels = queue.get('labels').mapBy('id').sort();
+  },
+  capacityBarView: Em.View.extend({
 
-            if (queue.get('isLabelsDirty') || isAllChanged) {
+      classNameBindings:[':progress-bar','queue.overCapacity:progress-bar-danger:progress-bar-success'],
 
-              oldV = ((isAllChanged && changes._accessAllLabels.objectAt(0)) || (!isAllChanged && queue.get('_accessAllLabels')))?'*':initialLabels.map(idsToNames).join(',') || emptyValue;
-              newV = ((isAllChanged && changes._accessAllLabels.objectAt(1)) || (!isAllChanged && queue.get('_accessAllLabels')))?'*':currentLabels.map(idsToNames).join(',') || emptyValue;
+      attributeBindings:['capacityWidth:style'],
 
-              caption += fmtString.fmt('accessible-node-labels', oldV, newV);
-            }
+      /**
+       * Formatting pattern.
+       * @type {String}
+       */
+      pattern:'width: %@%;',
 
-            queue.get('labels').forEach(function (label) {
-              var labelsChanges = label.changedAttributes(),
-                  prefix = ['accessible-node-labels',label.get('name')].join('.');
-              Em.keys(labelsChanges).forEach(Em.run.bind(labelsChanges,formatChangedAttributes,prefix));
-            });
-          }
+      /**
+       * Alias for parentView.capacityValue.
+       * @type {String}
+       */
+      value:Em.computed.alias('queue.capacity'),
 
-          return caption;
-        },
-        html:true,
-        placement:'bottom'
-      });
-  }.on('didInsertElement')
+      /**
+       * Formats pattern whit value.
+       * @return {String}
+       */
+      capacityWidth: function(c,o) {
+        return  this.get('pattern').fmt((+this.get('value')<=100)?this.get('value'):100);
+      }.property('value')
+    })
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/radioButton.js
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/radioButton.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/radioButton.js
index 8be8919..476171b 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/radioButton.js
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/radioButton.js
@@ -18,15 +18,6 @@
 
 var App = require('app');
 
-App.RadioButtonInputComponent = Ember.View.extend({
-    tagName : "input",
-    type : "radio",
-    attributeBindings : [ "type", "value", "checked:checked" ],
-    click : function() {
-      this.set("selection", this.get('value'));
-    }
-});
-
 App.RadioButtonComponent = Em.Component.extend({
   tagName:'label',
   classNames:['btn btn-default'],
@@ -42,5 +33,16 @@ App.RadioButtonComponent = Em.Component.extend({
   isActive : function() {
     return this.get("value") == this.get("selection");
   }.property("selection"),
-  layout:Em.Handlebars.compile('{{label}} {{radio-button-input selection=selection value=value checked=isActive}}')
+  radioInput: Ember.View.extend({
+    tagName : "input",
+    type : "radio",
+    attributeBindings : [ "type", "value", "checked:checked" ],
+    selection:Em.computed.alias('controller.selection'),
+    value:Em.computed.alias('controller.value'),
+    checked:Em.computed.alias('controller.isActive'),
+    click : function() {
+      this.set("selection", this.get('value'));
+    }
+  }),
+  layout:Em.Handlebars.compile('{{label}} {{view radioInput}}')
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/saveButton.js
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/saveButton.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/saveButton.js
new file mode 100644
index 0000000..ed3b1d2
--- /dev/null
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/saveButton.js
@@ -0,0 +1,54 @@
+/**
+ * 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.SaveButtonComponent = Em.Component.extend({
+  canNotSave:false,
+  needSave:true,
+  refresh:false,
+  restart:false,
+
+  tagName:'button',
+  layout:Em.Handlebars.compile('{{{icon}}} Actions <span class="caret"></span> '),
+  attributeBindings: ['data-toggle'],
+  classNames:['btn','dropdown-toggle'],
+  classNameBindings:['color','disabled'],
+  icon:function () {
+    var tmpl = '<i class="fa fa-fw fa-%@"></i>',icon;
+    if (this.get('refresh')) {
+      icon = 'refresh';
+    } else if (this.get('restart')) {
+      icon = 'cogs';
+    }
+    return (icon)?tmpl.fmt(icon):'';
+  }.property('refresh','restart'),
+  color:function () {
+    var className = 'btn-default';
+    if (this.get('needSave')) {
+      className = 'btn-warning';
+    }
+    if (this.get('canNotSave')) {
+      className = 'btn-danger';
+    }
+    return className;
+  }.property('canNotSave','needSave'),
+  disabled:function () {
+    return this.get('canNotSave');
+  }.property('canNotSave','needSave')
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/tooltipLabel.js
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/tooltipLabel.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/tooltipLabel.js
new file mode 100644
index 0000000..c76383c
--- /dev/null
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/tooltipLabel.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.TooltipLabelComponent = Em.Component.extend({
+  tagName:'label',
+  label:'',
+  message:'',
+  propertyName:'',
+  classNames:['tooltip-label'],
+  layout:Em.Handlebars.compile('<span>{{label}}</span> {{yield}}'),
+  initTooltip:function () {
+    this.$('span').first().popover({
+      trigger:'hover',
+      placement:'bottom',
+      container: 'body',
+      title:this.get('propertyName'),
+      content:this.get('message'),
+      html:true
+    });
+  }.on('didInsertElement'),
+  destroyTooltip:function () {
+    this.$('span').first().popover('destroy');
+  }.on('willClearRender')
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/totalCapacity.js
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/totalCapacity.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/totalCapacity.js
index d4fc80b..4ccf969 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/totalCapacity.js
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/totalCapacity.js
@@ -63,31 +63,23 @@ App.TotalCapacityComponent = Ember.Component.extend({
     rollbackProp:function (prop, item) {
       this.sendAction('rollbackProp', prop, item);
     },
+    toggleDefaultLabel:function (queue, label) {
+      if (queue.get('default_node_label_expression') === label.get('name')) {
+        queue.set('default_node_label_expression',null);
+      } else {
+        queue.set('default_node_label_expression',label.get('name'));
+      }
+    },
     toggleLabel:function (labelName, queue) {
       var q = queue || this.get('currentQueue'),
           labelRecord = q.store.getById('label',[q.get('path'),labelName].join('.').toLowerCase());
 
       if (q.get('labels').contains(labelRecord)) {
-        this.recurseRemoveLabel(q,labelRecord);
+        q.recurseRemoveLabel(labelRecord);
       } else {
         q.get('labels').pushObject(labelRecord);
+        q.notifyPropertyChange('labels');
       }
-      q.notifyPropertyChange('labels');
-    }
-  },
-
-  /**
-   * @param  {App.Queue} target queue
-   * @param  {App.Label} label ralated to queue. All labels with it's name will be removed from child queues.
-   * @method recurseRemoveLabel
-   */
-  recurseRemoveLabel:function(queue,label) {
-    label = queue.get('labels').findBy('name',label.get('name'));
-    if (label) {
-      queue.get('labels').removeObject(label);
-      this.get('allQueues').filterBy('parentPath',queue.get('path')).forEach(function (child) {
-        this.recurseRemoveLabel(child,label);
-      }.bind(this));
     }
   },
 
@@ -225,6 +217,13 @@ App.TotalCapacityComponent = Ember.Component.extend({
       }
     },
 
+    /**
+     * Current capacity value of current label.
+     * Update only when value realy changes.
+     * @type {Number}
+     */
+    currentCapacity:null,
+
     // COMPUTED PROPERTIES
 
     /**
@@ -241,7 +240,7 @@ App.TotalCapacityComponent = Ember.Component.extend({
      * @return {Boolean}
      */
     isActive:function () {
-      return this.get('queue.labels').mapBy('name').contains(this.get('labelName'));
+      return  !this.get('queue.labels.isDestroying') && this.get('queue.labels').mapBy('name').contains(this.get('labelName'));
     }.property('queue.labels.[]'),
 
     /**
@@ -249,7 +248,7 @@ App.TotalCapacityComponent = Ember.Component.extend({
      * @return {App.Label}
      */
     currentLabel:function () {
-      return this.get('queue.labels').findBy('name',this.get('labelName'));
+      return !this.get('queue.labels.isDestroying') && this.get('queue.labels').findBy('name',this.get('labelName'));
     }.property('labelName','queue.labels.length'),
 
     /**
@@ -285,7 +284,11 @@ App.TotalCapacityComponent = Ember.Component.extend({
      * @method capacityWatcher
      */
     capacityWatcher:function () {
-      this.get('labels').setEach('overCapacity',this.get('warning'));
+      Em.run.next(this,function () {
+        if (!!this.get('labels')) {
+          this.get('labels').setEach('overCapacity',this.get('warning'));
+        }
+      });
     }.observes('warning').on('didInsertElement'),
 
     /**
@@ -319,27 +322,30 @@ App.TotalCapacityComponent = Ember.Component.extend({
     isShownTimer:null,
 
     /**
+     * Triggers tooltip when capacity on labels changes.
+     * @return {[type]} [description]
+     */
+    triggerCapacityTooltip:function() {
+      if (!(Em.isNone(this.$()) || !this.get('queue.labelsEnabled') || Em.isNone(this.get('currentLabel')))
+          && this.get('currentLabel.capacity') != this.get('currentCapacity')) {
+        Em.run.scheduleOnce('afterRender', this, 'showCapacityTooltip');
+      }
+    }.observes('currentLabel.capacity'),
+
+    /**
      * Shows tooltip when label's capacity value changes.
      * @method showCapacityTooltip
      */
     showCapacityTooltip: function() {
-      Em.run.next(this,function () {
-        if (Em.isNone(this.$())) return;
-
-        this.$().tooltip({
-          title: function  () {
-            return this.get('capacityValue') + '';
-          }.bind(this),
-          container:"#"+this.elementId,
-          animation:false
-        }).tooltip('show');
-
+      if (this._state === 'inDOM') {
+        this.$().tooltip('show');
+        this.set('currentCapacity',this.get('currentLabel.capacity'));
         Em.run.cancel(this.get('isShownTimer'));
-        this.set('isShownTimer',Em.run.debounce(this,function() {
+        this.set('isShownTimer',Em.run.later(this,function() {
           if (this.$()) this.$().tooltip('hide');
         },500));
-      })
-    }.observes('currentLabel.capacity'),
+      }
+    },
 
     /**
      * Destrioy tooltips when element destroys.

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/queue.js
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/queue.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/queue.js
index 41a4dfd..9828399 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/queue.js
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/queue.js
@@ -37,47 +37,50 @@ App.QueueController = Ember.ObjectController.extend({
       this.get('controllers.queues').send('delQ',record);
     },
     renameQ:function (opt) {
+
+      var queue = this.get('content'),
+          store = this.get('store'),
+          queuesController = this.get('controllers.queues'),
+          parentPath = queue.get('parentPath'),
+          name, renamedQueueBackup;
+
       if (opt == 'ask') {
-        this.set('tmpName',{name:this.get('content.name'),path:this.get('content.path')});
-        this.get('content').addObserver('name',this,this.setQueuePath);
+        this.set('tmpName', queue.getProperties('name','path','id'));
+        queue.addObserver('name',this,this.setQueuePath);
         this.toggleProperty('isRenaming');
         return;
       }
       if (opt == 'cancel') {
-        this.get('content').removeObserver('name',this,this.setQueuePath);
-        this.get('content').setProperties({
-          name:this.get('tmpName.name'),
-          id:this.get('tmpName.path'),
-          path:this.get('tmpName.path')
-        });
+        queue.set('name',this.get('tmpName.name'));
+        queue.removeObserver('name',this,this.setQueuePath);
         this.toggleProperty('isRenaming');
         return;
       }
-      if (opt && !this.get('content').get('errors.path')) {
-
-        this.store.filter('label',function (label){
-          return label.get('forQueue') == this.get('tmpName.path');
-        }.bind(this)).then(function (labels) {
-          labels.forEach(function (label) {
-            label.materializeId([this.get('id'),label.get('name')].join('.'));
-            label.store.updateId(label,label);
-          }.bind(this))
-        }.bind(this));
+
+      if (opt && !queue.get('errors.path')) {
+        name = queue.get('name');
         this.toggleProperty('isRenaming');
-        this.get('content').removeObserver('name',this,this.setQueuePath);
-        this.store.updateId(this.get('content'),this.get('content'));
-        this.transitionToRoute('queue',this.get('content.id'));
+        queue.removeObserver('name',this,this.setQueuePath);
+        queue.set('name',this.get('tmpName.name'));
+
+        if (queue.get('isNewQueue')) {
+          renamedQueueBackup = this.get('store').buildDeletedQueue(queue);
+        }
 
+        store.recurceRemoveQueue(queue).then(function (queue) {
+          return (queue.get('isNewQueue')) ? renamedQueueBackup : store.get('deletedQueues').findBy('path',queue.get('path'));
+        }).then(function (deletedQueue) {
+          var targetDeleted =  store.get('deletedQueues').findBy('path',[parentPath,name].join('.')),
+              queuePrototype = (targetDeleted) ? store.createFromDeleted(targetDeleted) : store.copyFromDeleted(deletedQueue,parentPath,name);
+
+          return store.saveAndUpdateQueue(queuePrototype,deletedQueue);
+        }).then(Em.run.bind(this,'transitionToRoute','queue'));
       }
 
     },
-    // TODO bubble to route
-    rollbackProp:function(prop, queue){
-      queue = queue || this.get('content');
-      attributes = queue.changedAttributes();
-      if (attributes.hasOwnProperty(prop)) {
-        queue.set(prop,attributes[prop][0]);
-      }
+    toggleProperty:function (property,target) {
+      target = target || this;
+      target.toggleProperty(property);
     }
   },
 
@@ -95,10 +98,15 @@ App.QueueController = Ember.ObjectController.extend({
 
   /**
    * Object contains temporary name and path while renaming the queue.
-   * @type {Object} - { name : {String}, path : {String} }
+   * @type {Object} - { name : {String}, path : {String} , id : {String}}
    */
   tmpName:{},
 
+  /**
+   * Possible values for ordering policy
+   * @type {Array}
+   */
+  orderingPolicyValues: [null,'fifo', 'fair'],
 
 
   // COMPUTED PROPERTIES
@@ -187,26 +195,35 @@ App.QueueController = Ember.ObjectController.extend({
 
   /**
    * Error messages for queue path.
-   * @type {[type]}
+   * @type {Array}
    */
   pathErrors:Ember.computed.mapBy('content.errors.path','message'),
 
-
-
-  // OBSERVABLES
+  /**
+   * Current ordering policy value of queue.
+   * @param  {String} key
+   * @param  {String} value
+   * @return {String}
+   */
+  currentOP:function (key,val) {
+    if (arguments.length > 1) {
+      if (!this.get('isFairOP')) {
+        this.send('rollbackProp','enable_size_based_weight',this.get('content'));
+      }
+      this.set('content.ordering_policy',val || null);
+    }
+    return this.get('content.ordering_policy');
+  }.property('content.ordering_policy'),
 
   /**
-   * Marks each queue in leaf with 'overCapacity' if sum if their capacity values is greater then 100.
-   * @method capacityControl
+   * Does ordering policy is equal to 'fair'
+   * @type {Boolean}
    */
-  capacityControl:function () {
-    var leafQueues = this.get('leafQueues'),
-        total = leafQueues.reduce(function (prev, queue) {
-          return +queue.get('capacity') + prev;
-        },0);
+  isFairOP: Em.computed.equal('content.ordering_policy','fair'),
+
 
-    leafQueues.setEach('overCapacity',total>100);
-  }.observes('content.capacity','leafQueues.@each.capacity'),
+
+  // OBSERVABLES
 
   /**
    * Keeps track of leaf queues and sets 'queues' value of parent to list of their names.
@@ -251,25 +268,22 @@ App.QueueController = Ember.ObjectController.extend({
    */
   setQueuePath:function (queue) {
     var name = queue.get('name').replace(/\s|\./g, ''),
-        parentPath = queue.get('parentPath');
+        parentPath = queue.get('parentPath'),
+        foundWithName;
+
+    queue.set('name',name);
 
-    queue.setProperties({
-      name:name,
-      path:parentPath+'.'+name,
-      id:(parentPath+'.'+name).dasherize()
+    foundWithName = this.store.all('queue').find(function (q) {
+      return q.get('path') === [queue.get('parentPath'),queue.get('name')].join('.');
     });
 
-    if (name == '') {
+    if (!Em.isEmpty(foundWithName) && queue.changedAttributes().hasOwnProperty('name')) {
+      return this.get('content').get('errors').add('path', 'Queue already exists');
+    } else if (name == '') {
       queue.get('errors').add('path', 'This field is required');
+    } else {
+      queue.get('errors').remove('path');
     }
-
-    this.store.filter('queue',function (q) {
-      return q.get('id') === queue.get('id');
-    }.bind(this)).then(function (queues){
-      if (queues.get('length') > 1) {
-        return this.get('content').get('errors').add('path', 'Queue already exists');
-      }
-    }.bind(this));
   },
 
   /**

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/queues.js
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/queues.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/queues.js
index aecd265..b26697f 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/queues.js
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/queues.js
@@ -29,50 +29,49 @@ App.QueuesController = Ember.ArrayController.extend({
          this.store.fetchTagged(App.Queue,tag);
        }.bind(this));
     },
-    goToQueue:function (queue) {
-      this.transitionToRoute('queue',queue);
-    },
     askPath:function () {
       this.set('isWaitingPath',true);
     },
     addQ:function (parentPath,name) {
-      if (!parentPath || this.get('hasNewQueue')) {
+      if (!parentPath || this.get('hasNewQueue') || !this.store.hasRecordForId('queue',parentPath.toLowerCase())) {
         return;
       }
       name = name || '';
-      var newQueue = this.store.createRecord('queue',{
-        name:name,
-        parentPath: parentPath,
-        depth: parentPath.split('.').length,
-        isNewQueue:true
-      });
-      this.set('newQueue',newQueue);
+      var newQueue,
+          existed = this.get('store.deletedQueues').findBy('path',parentPath+'.'+name);
+
+      if (existed) {
+        newQueue = this.store.createFromDeleted(existed);
+      } else {
+        newQueue = this.store.createRecord('queue', {
+          name:name,
+          parentPath: parentPath,
+          depth: parentPath.split('.').length,
+          isNewQueue:true
+        });
+        this.set('newQueue',newQueue);
+      }
+
       if (name) {
-        this.send('goToQueue',newQueue);
-        this.send('createQ',newQueue);
+        this.get('store').saveAndUpdateQueue(newQueue,existed)
+          .then(Em.run.bind(this,'transitionToRoute','queue'))
+          .then(Em.run.bind(this,'set','newQueue',null));
       } else {
-        this.send('goToQueue',newQueue);
+        this.transitionToRoute('queue',newQueue);
       }
     },
-    createQ:function (record) {
-      record.save().then(Em.run.bind(this,this.set,'newQueue',null));
+    downloadConfig: function (format) {
+      var config =  this.get('store').buildConfig(format);
+      return this.fileSaver.save(config, "application/json", 'scheduler_config_' + moment() + '.' + format);
+    },
+    createQ:function (record,updates) {
+      this.get('store').saveAndUpdateQueue(record, updates);
     },
     delQ:function (record) {
-      var queues = this.get('content'),
-          parentPath = record.get('parentPath'),
-          name = record.get('name');
-      if (record.get('isNew')) {
-        this.set('newQueue',null);
-      }
-      if (!record.get('isNewQueue')) {
-        this.set('hasDeletedQueues',true);
-      }
       if (record.isCurrent) {
-        this.transitionToRoute('queue',parentPath.toLowerCase())
+        this.transitionToRoute('queue',record.get('parentPath').toLowerCase())
           .then(Em.run.schedule('afterRender', function () {
-            record.destroyRecord().then(function() {
-              queues.findBy('path',parentPath).set('queuesArray',{'exclude':name});
-            });
+            record.get('store').recurceRemoveQueue(record);
           }));
       } else {
         record.destroyRecord();
@@ -88,15 +87,15 @@ App.QueuesController = Ember.ArrayController.extend({
         return prev.pushObjects(q.get('labels.content'));
       },[]);
 
-      var hadDeletedQueues = this.get('hasDeletedQueues'),
-          scheduler = this.get('scheduler').save(),
+      var scheduler = this.get('scheduler').save(),
           model = this.get('model').save(),
-          labels = DS.ManyArray.create({content:collectedLabels}).save(),
-          all = Em.RSVP.Promise.all([labels,model,scheduler]);
+          labels = DS.ManyArray.create({content:collectedLabels}).save();
 
-      all.catch(Em.run.bind(this,this.saveError,hadDeletedQueues));
+      Em.RSVP.Promise.all([labels,model,scheduler]).then(
+        Em.run.bind(this,'saveSuccess'),
+        Em.run.bind(this,'saveError')
+      );
 
-      this.set('hasDeletedQueues',false);
     },
     clearAlert:function () {
       this.set('alertMessage',null);
@@ -160,13 +159,6 @@ App.QueuesController = Ember.ArrayController.extend({
 
   configNote: cmp.alias('store.configNote'),
 
-  /*configNote:function (arg,val) {
-    if (arguments.length > 1) {
-      this.set('store.configNote',val);
-    }
-    return this.get('store.configNote');
-  }.property('store.configNote'),*/
-
   tags:function () {
     return this.store.find('tag');
   }.property('store.current_tag'),
@@ -175,9 +167,11 @@ App.QueuesController = Ember.ArrayController.extend({
     return (+a.id > +b.id)?(+a.id < +b.id)?0:-1:1;
   }),
 
+  saveSuccess:function () {
+    this.set('store.deletedQueues',[]);
+  },
 
-  saveError:function (hadDeletedQueues,error) {
-    this.set('hasDeletedQueues',hadDeletedQueues);
+  saveError:function (error) {
     var response = JSON.parse(error.responseText);
     this.set('alertMessage',response);
   },
@@ -195,21 +189,37 @@ App.QueuesController = Ember.ArrayController.extend({
 
 
   trackNewQueue:function () {
-    var newQueue = this.get('newQueue');
+    var newQueue = this.get('newQueue'), props;
     if (Em.isEmpty(newQueue)) {
       return;
     }
-    var name = newQueue.get('name');
-    var parentPath = newQueue.get('parentPath');
 
-    this.get('newQueue').setProperties({
-      name:name.replace(/\s/g, ''),
-      path:parentPath+'.'+name,
-      id:(parentPath+'.'+name).dasherize()
+    props = newQueue.getProperties('name','parentPath');
+
+    newQueue.setProperties({
+      name: props.name.replace(/\s/g, ''),
+      path: props.parentPath+'.'+props.name,
+      id: (props.parentPath+'.'+props.name).toLowerCase()
     });
 
   }.observes('newQueue.name'),
 
+  /**
+   * Marks each queue in leaf with 'overCapacity' if sum if their capacity values is greater then 100.
+   * @method capacityControl
+   */
+  capacityControl: function() {
+    var pathes = this.get('content').getEach('parentPath').uniq();
+    pathes.forEach(function (path) {
+      var leaf = this.get('content').filterBy('parentPath',path),
+      total = leaf.reduce(function (prev, queue) {
+          return +queue.get('capacity') + prev;
+        },0);
+
+      leaf.setEach('overCapacity',total>100);
+    }.bind(this));
+  }.observes('content.length','content.@each.capacity'),
+
 
 
   // TRACKING OF RESTART REQUIREMENT
@@ -218,27 +228,13 @@ App.QueuesController = Ember.ArrayController.extend({
    * check if RM needs restart
    * @type {bool}
    */
-  needRestart: cmp.any('hasDeletedQueues', 'hasRenamedQueues'),
+  needRestart: Em.computed.alias('hasDeletedQueues'),
 
   /**
    * True if some queue of desired configs was removed.
    * @type {Boolean}
    */
-  hasDeletedQueues:false,
-
-  /**
-   * List of queues with modified name.
-   * @type {Array}
-   */
-  renamedQueues:cmp.filter('content.@each.name',function (queue){
-    return queue.changedAttributes().hasOwnProperty('name') && !queue.get('isNewQueue');
-  }),
-
-  /**
-   * True if renamedQueues is not empty.
-   * @type {Boolean}
-   */
-  hasRenamedQueues: cmp.notEmpty('renamedQueues.[]'),
+  hasDeletedQueues: Em.computed.alias('store.hasDeletedQueues'),
 
 
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/helpers/escapeAcl.js
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/helpers/escapeAcl.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/helpers/escapeAcl.js
new file mode 100644
index 0000000..125b037
--- /dev/null
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/helpers/escapeAcl.js
@@ -0,0 +1,49 @@
+/**
+ * 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.
+ */
+
+
+Ember.Handlebars.helper('escapeACL', function(value) {
+  var output = '';
+
+  value = value || '';
+
+  if (value.trim() == '') {
+    output = '<span class="label label-danger"> <i class="fa fa-ban fa-fw"></i>  Nobody </span> ';
+  } else if (value.trim() == '*') {
+    output = '<label class="label label-success"> <i class="fa fa-asterisk fa-fw"></i> Anyone</label>';
+  } else {
+    var ug = value.split(' ');
+    var users = ug[0].split(',')||[];
+    var groups = (ug.length == 2)?ug[1].split(',')||[]:[];
+
+    output += ' <span class="users"> ';
+
+    users.forEach(function (user) {
+      output += (user)?'<span class="label label-primary"><i class="fa fa-user fa-fw"></i> '+ user +'</span> ':'';
+    });
+
+    output += ' </span> <span class="groups"> ';
+
+    groups.forEach(function (group) {
+      output += (group)?'<span class="label label-primary"><i class="fa fa-users fa-fw"></i> '+ group +'</span> ':'';
+    });
+
+    output += ' </span> ';
+  }
+  return new Ember.Handlebars.SafeString(output);
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/helpers/timeAgo.js
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/helpers/timeAgo.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/helpers/timeAgo.js
new file mode 100644
index 0000000..050f0c0
--- /dev/null
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/helpers/timeAgo.js
@@ -0,0 +1,62 @@
+/**
+ * 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.
+ */
+
+Ember.Handlebars.registerHelper('timeAgo', function(property, options) {
+  var normalizePath = Em.Handlebars.normalizePath(((options.contexts && options.contexts[0]) || this), property, options.data);
+
+  var boundView = Em._HandlebarsBoundView.extend({
+    path: normalizePath.path,
+    pathRoot: normalizePath.root,
+    isEscaped: !options.hash.unescaped,
+    templateData: options.data,
+    shouldDisplayFunc:function () {
+      return true;
+    },
+    normalizedValue:function() {
+      var value = Em._HandlebarsBoundView.prototype.normalizedValue.call(this);
+
+      return function(value, options) {
+        return (value)?moment(value).fromNow(false):'';
+      }.call(this, value, options);
+    }
+  });
+
+  boundView.reopen({
+    didInsertElement: function() {
+      if (this.updateTimer) {
+        Em.run.cancel(this.updateTimer);
+      }
+
+      Em.run.later(this,function() {
+        if (this._state === 'inDOM') {
+          Em.run.scheduleOnce('render', this, 'rerender');
+        }
+      },30000);
+    },
+    willDestroyElement: function() {
+      if (this.updateTimer) {
+        Em.run.cancel(this.updateTimer);
+      }
+    },
+    updateValue:function () {
+      Em.run.scheduleOnce('render', this, 'rerender');
+    }.observes(normalizePath.path)
+  });
+
+  options.data.view.appendChild(boundView);
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/initialize.js
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/initialize.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/initialize.js
index 57f57a0..05bb64c 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/initialize.js
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/initialize.js
@@ -30,6 +30,10 @@ require('serializers');
 //store
 require('store');
 
+//helpers
+require('helpers/timeAgo');
+require('helpers/escapeAcl');
+
 //components
 require('components');
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/models/queue.js
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/models/queue.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/models/queue.js
index 2044ff1..3132c10 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/models/queue.js
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/models/queue.js
@@ -32,14 +32,18 @@ App.Label = DS.Model.extend({
   overCapacity:false,
   isNotExist:function () {
     return this.get('store.nodeLabels.content').findBy('name',this.get('name')).notExist;
-  }.property('store.nodeLabels.content.@each.notExist')
+  }.property('store.nodeLabels.content.@each.notExist'),
+  isDefault: function () {
+    return this.get('queue.default_node_label_expression') === this.get('name');
+  }.property('queue.default_node_label_expression')
 });
 
 App.Scheduler = DS.Model.extend({
   maximum_am_resource_percent: DS.attr('number', { defaultValue: 0 }),
   maximum_applications: DS.attr('number', { defaultValue: 0 }),
   node_locality_delay: DS.attr('number', { defaultValue: 0 }),
-  resource_calculator: DS.attr('string', { defaultValue: '' })
+  resource_calculator: DS.attr('string', { defaultValue: '' }),
+  isAnyDirty:Em.computed.alias('isDirty')
 });
 
 
@@ -53,14 +57,8 @@ App.Tag = DS.Model.extend({
     return this.get('tag') === this.get('store.current_tag');
   }.property('store.current_tag'),
   changed:function () {
-    return (this.get('tag').match(/version[1-9]+/))?moment(+this.get('tag').replace('version','')).fromNow():'';
-  }.property('tag'),
-  updateTime:function () {
-    Em.run.later(this,function () {
-      this.trigger('tick');
-      this.notifyPropertyChange('tag');
-    },5000);
-  }.on('init','tick')
+    return (this.get('tag').match(/version[1-9]+/))?moment(+this.get('tag').replace('version','')):'';
+  }.property('tag')
 });
 
 /**
@@ -69,6 +67,9 @@ App.Tag = DS.Model.extend({
  */
 App.Queue = DS.Model.extend({
   labels: DS.hasMany('label'),
+
+  labelsEnabled: false,
+
   sortBy:['name'],
   sortedLabels:Em.computed.sort('labels','sortBy'),
 
@@ -82,23 +83,61 @@ App.Queue = DS.Model.extend({
       this.set('_accessAllLabels',val);
 
       if (this.get('_accessAllLabels')) {
-          labels.forEach(function(lb) {
-
+          labels.forEach(function (lb) {
             var containsByParent = (Em.isEmpty(this.get('parentPath')))?true:this.store.getById('queue',this.get('parentPath')).get('labels').findBy('name',lb.get('name'));
             if (!this.get('labels').contains(lb) && !!containsByParent) {
               this.get('labels').pushObject(lb);
-              this.notifyPropertyChange('labels');
             }
           }.bind(this));
+          this.notifyPropertyChange('labels');
       }
     }
 
-    if (this.get('labels.length') != labels.get('length')) {
+    return this.get('_accessAllLabels');
+  }.property('_accessAllLabels','labels'),
+
+  labelsAccessWatcher:function () {
+    Em.run.scheduleOnce('sync',this,'unsetAccessAllIfNeed');
+  }.observes('labels','_accessAllLabels'),
+
+  unsetAccessAllIfNeed:function () {
+    if (!this.get('isDeleted') && this.get('labels.length') != this.get('store.nodeLabels.length')) {
       this.set('_accessAllLabels',false);
     }
+  },
 
-    return this.get('_accessAllLabels');
-  }.property('_accessAllLabels','labels.[]'),
+  clearLabels:function () {
+    if (!this.get('labelsEnabled')) {
+      this.recurseClearLabels(false);
+    }
+  }.observes('labelsEnabled'),
+
+  recurseClearLabels:function(leaveEnabled) {
+    this.set('accessAllLabels',false);
+    this.get('labels').clear();
+    this.notifyPropertyChange('labels');
+    this.store.all('queue').filterBy('parentPath',this.get('path')).forEach(function (child) {
+      if (!Em.isNone(leaveEnabled)) {
+        child.set('labelsEnabled',leaveEnabled);
+      }
+      child.recurseClearLabels(leaveEnabled);
+    });
+  },
+
+  /**
+   * @param  {App.Label} label ralated to queue. All labels with it's name will be removed from child queues.
+   * @method recurseRemoveLabel
+   */
+  recurseRemoveLabel:function(label) {
+    label = this.get('labels').findBy('name',label.get('name'));
+    if (label) {
+      this.get('labels').removeObject(label);
+      this.notifyPropertyChange('labels');
+      this.store.all('queue').filterBy('parentPath',this.get('path')).forEach(function (child) {
+        child.recurseRemoveLabel(label);
+      }.bind(this));
+    }
+  },
 
   isAnyDirty: function () {
     return this.get('isDirty') || !Em.isEmpty(this.get('labels').findBy('isDirty',true)) || this.get('isLabelsDirty');
@@ -106,6 +145,7 @@ App.Queue = DS.Model.extend({
 
   initialLabels:[],
   labelsLoad:function() {
+    this.set('labelsEnabled',this._data.labelsEnabled);
     this.set('initialLabels',this.get('labels').mapBy('id'));
   }.on('didLoad','didUpdate','didCreate'),
 
@@ -126,6 +166,9 @@ App.Queue = DS.Model.extend({
   state: DS.attr('string', { defaultValue: 'RUNNING' }),
   acl_administer_queue: DS.attr('string', { defaultValue: '*' }),
   acl_submit_applications: DS.attr('string', { defaultValue: '*' }),
+  ordering_policy: DS.attr('string', { defaultValue: 'fifo' }),
+  enable_size_based_weight: DS.attr('boolean', { defaultValue: false }),
+  default_node_label_expression: DS.attr('string'),
 
   capacity: DS.attr('number', { defaultValue: 0 }),
   maximum_capacity: DS.attr('number', { defaultValue: 0 }),
@@ -144,7 +187,7 @@ App.Queue = DS.Model.extend({
         qrray = (this.get('queues'))?this.get('queues').split(','):[];
         this.set('queues',qrray.removeObject(val.exclude).join(',') || null);
       } else {
-        this.set('queues',val.join(',') || null);
+        this.set('queues',val.sort().join(',') || null);
       }
     }
     return (this.get('queues'))?this.get('queues').split(','):[];
@@ -180,8 +223,16 @@ App.Queue = DS.Model.extend({
     'minimum_user_limit_percent',
     'maximum_applications',
     'maximum_am_resource_percent',
+    'ordering_policy',
     'queues',
+    'enable_size_based_weight',
     'labels.@each.capacity',
     'labels.@each.maximum_capacity'
-  )
+  ),
+
+  clearDefaultNodeLabel: function () {
+    if (Em.isEmpty(this.get('labels').findBy('name',this.get('default_node_label_expression')))) {
+      this.set('default_node_label_expression',null);
+    }
+  }.observes('labels','default_node_label_expression')
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/968ff91d/contrib/views/capacity-scheduler/src/main/resources/ui/app/router.js
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/router.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/router.js
index b4250fb..97473fb 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/router.js
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/router.js
@@ -23,18 +23,7 @@ App.Router.map(function() {
     this.resource('queue', { path: '/:queue_id' });
     this.resource('trace', { path: '/log' });
   });
-});
-
-
-/**
- * The queues route.
- *
- * /queues
- */
-App.TraceRoute = Ember.Route.extend({
-  model: function() {
-    return this.controllerFor('queues').get('alertMessage');
-  }
+  this.route('refuse');
 });
 
 /**
@@ -51,26 +40,47 @@ App.QueuesRoute = Ember.Route.extend({
       }
     }
   },
+  beforeModel:function (transition) {
+    var controller = this.container.lookup('controller:loading') || this.generateController('loading');
+    controller.set('model', {message:'cluster check'});
+    return this.get('store').checkCluster().catch(Em.run.bind(this,'loadingError',transition));
+  },
   model: function() {
-    var store = this.get('store');
+    var store = this.get('store'),
+        controller = this.controllerFor('queues'),
+        loadingController = this.container.lookup('controller:loading');
     return new Ember.RSVP.Promise(function (resolve,reject) {
-      store.get('nodeLabels').then(function () {
+      loadingController.set('model', {message:'access check'});
+      store.checkOperator().then(function (isOperator) {
+        controller.set('isOperator', isOperator);
+
+        loadingController.set('model', {message:'loading node labels'});
+        return store.get('nodeLabels');
+      }).then(function () {
+        loadingController.set('model', {message:'loading queues'});
         return store.find('queue');
       }).then(function (queues) {
         resolve(queues);
       }).catch(function (e) {
         reject(e);
       });
-    });
+    }, 'App: QueuesRoute#model');
   },
   setupController:function (c,model) {
-    this.store.checkOperator().then(function (isOperator) {
-      c.set('isOperator', isOperator);
-    });
     c.set('model',model);
     this.store.find('scheduler','scheduler').then(function (s) {
       c.set('scheduler',s);
     });
+  },
+  loadingError: function (transition, error) {
+    var refuseController = this.container.lookup('controller:refuse') || this.generateController('refuse'),
+        message = error.responseJSON || {'message':'Something went wrong.'};
+
+    transition.abort();
+
+    refuseController.set('model', message);
+
+    this.transitionTo('refuse');
   }
 });
 
@@ -92,7 +102,6 @@ App.QueueRoute = Ember.Route.extend({
       this.transitionTo('queues');
     }
   },
-
   actions: {
     willTransition: function (tr) {
       if (this.get('controller.isRenaming')) {
@@ -100,7 +109,6 @@ App.QueueRoute = Ember.Route.extend({
       }
     }
   }
-
 });
 
 /**
@@ -108,16 +116,46 @@ App.QueueRoute = Ember.Route.extend({
  *
  */
 App.IndexRoute = Ember.Route.extend({
-  beforeModel: function() {
+  redirect: function() {
     this.transitionTo('queues');
   }
 });
 
 /**
+ * Page for trace output.
+ *
+ * /queues/log
+ */
+App.TraceRoute = Ember.Route.extend({
+  model: function() {
+    return this.controllerFor('queues').get('alertMessage');
+  }
+});
+
+/**
+ * Connection rejection page.
+ *
+ * /refuse
+ */
+App.RefuseRoute = Ember.Route.extend({
+  setupController:function (controller,model) {
+    if (Em.isEmpty(controller.get('model'))) {
+      this.transitionTo('queues');
+    }
+  }
+});
+
+/**
  * Loading spinner page.
  *
  */
-App.LoadingRoute = Ember.Route.extend();
+App.LoadingRoute = Ember.Route.extend({
+  setupController:function(controller) {
+    if (Em.isEmpty(controller.get('model'))) {
+      this._super();
+    }
+  }
+});
 
 /**
  * Error page.