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:57 UTC
[2/2] ambari git commit: AMBARI-10871. Ambari Views. CapSch View,
config elements for ordering policies (alexantonenko)
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.