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.