AMBARI-15384. CapSched View: Revamping the old UI. (Akhil PB via dipayanb)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/79489a8f Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/79489a8f Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/79489a8f Branch: refs/heads/trunk Commit: 79489a8f35a6b808277a91251beae704aaad982f Parents: ac731f7 Author: Dipayan Bhowmick <[email protected]> Authored: Thu Apr 28 16:42:33 2016 +0530 Committer: Dipayan Bhowmick <[email protected]> Committed: Thu Apr 28 16:42:33 2016 +0530 ---------------------------------------------------------------------- .../src/main/resources/ui/app/components.js | 3 + .../ui/app/components/queueHierarchy.js | 46 +++ .../resources/ui/app/components/queueMapping.js | 141 ++++++++ .../resources/ui/app/components/queueSummary.js | 52 +++ .../src/main/resources/ui/app/controllers.js | 5 + .../resources/ui/app/controllers/advanced.js | 55 +++ .../resources/ui/app/controllers/capsched.js | 39 +++ .../resources/ui/app/controllers/editqueue.js | 333 +++++++++++++++++++ .../resources/ui/app/controllers/queuesconf.js | 141 ++++++++ .../resources/ui/app/controllers/scheduler.js | 83 +++++ .../src/main/resources/ui/app/helpers.js | 22 ++ .../ui/app/helpers/queueMappingParser.js | 34 ++ .../main/resources/ui/app/helpers/uppercase.js | 21 ++ .../src/main/resources/ui/app/initialize.js | 6 +- .../src/main/resources/ui/app/router.js | 87 ++++- .../resources/ui/app/styles/application.less | 285 ++++++++++++++++ .../src/main/resources/ui/app/templates.js | 13 + .../resources/ui/app/templates/capsched.hbs | 44 +++ .../ui/app/templates/capsched/advanced.hbs | 32 ++ .../capsched/partials/accessControlList.hbs | 154 +++++++++ .../capsched/partials/editQueueCapacity.hbs | 51 +++ .../capsched/partials/queueCapacity.hbs | 95 ++++++ .../capsched/partials/queueResources.hbs | 216 ++++++++++++ .../ui/app/templates/capsched/queuesconf.hbs | 104 ++++++ .../templates/capsched/queuesconf/editqueue.hbs | 47 +++ .../ui/app/templates/capsched/scheduler.hbs | 135 ++++++++ .../app/templates/components/queueHierarchy.hbs | 41 +++ .../app/templates/components/queueMapping.hbs | 154 +++++++++ .../app/templates/components/queueSummary.hbs | 66 ++++ .../ui/app/templates/versionsPanel.hbs | 49 +++ .../resources/ui/app/views/editQueueCapacity.js | 29 ++ .../main/resources/ui/app/views/editqueue.js | 50 +++ .../main/resources/ui/app/views/queuesconf.js | 27 ++ 33 files changed, 2657 insertions(+), 3 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/79489a8f/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 ac679b7..7f7bc3b 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 @@ -30,3 +30,6 @@ require('components/dropdownButtons'); require('components/queueBadge'); require('components/diffTooltip'); require('components/tooltipLabel'); +require('components/queueMapping'); +require('components/queueHierarchy'); +require('components/queueSummary'); http://git-wip-us.apache.org/repos/asf/ambari/blob/79489a8f/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueHierarchy.js ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueHierarchy.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueHierarchy.js new file mode 100644 index 0000000..64a28343 --- /dev/null +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueHierarchy.js @@ -0,0 +1,46 @@ +/** + * 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.QueueHierarchyComponent = Ember.Component.extend({ + layoutName: 'components/queueHierarchy', + depth:0, + parent:'', + + leafQs: function () { + return this.get('queues') + .filterBy('depth', this.get('depth')) + .filterBy('parentPath', this.get('parent')); + }.property('depth', 'parent', 'queues.length', '[email protected]'), + + childDepth: function () { + return this.get('leafQs.firstObject.depth') + 1; + }.property('depth'), + + didInsertElement: function () { + Ember.run.scheduleOnce('afterRender', null, this.setFirstAndLast, this); + }, + + setFirstAndLast: function (item) { + var items = item.$().parents('.queue-hierarchy').find('.list-group-item'); + items.first().addClass('first'); + items.last().addClass('last'); + } + + }); http://git-wip-us.apache.org/repos/asf/ambari/blob/79489a8f/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueMapping.js ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueMapping.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueMapping.js new file mode 100644 index 0000000..02062a6 --- /dev/null +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueMapping.js @@ -0,0 +1,141 @@ +/** + * 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.QueueMappingComponent = Em.Component.extend({ + layoutName: 'components/queueMapping', + + queues: null, + mappings: '', + mappingsOverrideEnable: false, + + isShowing: false, + queueMappings: [], + leafQueueNames: [], + selectedMapping: '', + customUserMappings: '', + customGroupMappings: '', + selectedLeafQueueNameForUsers: null, + selectedLeafQueueNameForGroups: null, + + actions: { + showMappingOptions: function(){ + this.resetMappingOptions(); + this.set('isShowing', true); + }, + hideMappingOptions: function(){ + this.set('isShowing', false); + }, + addQueueMapping: function(){ + this.addQueueMapping(); + }, + removeQueueMapping: function(qm){ + this.get('queueMappings').removeObject(qm); + } + }, + + resetMappingOptions: function(){ + this.set('selectedMapping', ''); + this.set('customUserMappings', ''); + this.set('customGroupMappings', ''); + }, + + parseMappings: function(){ + var mappings = this.get('mappings') || ''; + this.set('queueMappings', mappings.split(',').filter(function(mapping){ + return mapping !== ""; + }) || []); + }.observes('mappings').on('init'), + + extractLeafQueueNames: function(){ + var that = this; + var queues = this.get('queues') || []; + var leafQs = queues.filterBy('queues', null); + leafQs.forEach(function(q){ + that.get('leafQueueNames').pushObject(q.get('name')); + }); + }.observes('queues.length').on('init'), + + addQueueMapping: function(){ + var that = this; + if(this.get('selectedMapping') !== ''){ + if(this.get('selectedMapping') !== 'u:%name:%qname' && this.get('selectedMapping') !== 'g:%name:%qname'){ + this.get('queueMappings').pushObject(this.get('selectedMapping')); + }else{ + if(this.get('selectedMapping') === 'u:%name:%qname' && this.get('customUserMappings').trim() !== '' + && this.get('selectedLeafQueueNameForUsers') !== null){ + this.addCustomQueueMappings(this.get('customUserMappings'), this.get('selectedLeafQueueNameForUsers')); + }else if(this.get('selectedMapping') === 'g:%name:%qname' && this.get('customGroupMappings').trim() !== '' + && this.get('selectedLeafQueueNameForGroups') !== null){ + this.addCustomQueueMappings(this.get('customGroupMappings'), this.get('selectedLeafQueueNameForGroups')); + } + } + this.resetMappingOptions(); + } + }, + + queueMappingsDidChange: function(){ + var csMappings = this.get('queueMappings').join(',') || ''; + this.set('mappings', csMappings); + }.observes('queueMappings', 'queueMappings.length', 'queueMappings.@each'), + + addCustomQueueMappings: function(csValues, selectedLeafQName){ + var that = this; + csValues = csValues.trim() || '', + userOrGroupNames = csValues.split(',') || [], + mappingPattern = this.get('selectedMapping'); + userOrGroupNames.forEach(function(ugname){ + that.get('queueMappings').pushObject(mappingPattern.replace('%name', ugname).replace('%qname', selectedLeafQName)); + }); + }, + + isCustomUserMapping: function(){ + return this.get('selectedMapping').trim() === 'u:%name:%qname'; + }.property('selectedMapping'), + + isCustomGroupMapping: function(){ + return this.get('selectedMapping').trim() === 'g:%name:%qname'; + }.property('selectedMapping'), + + radioButton: Em.View.extend({ + tagName: 'input', + type: 'radio', + attributeBindings: ['type', 'name', 'value', 'checked:checked:'], + click: function(){ + this.set("selection", this.$().val()); + }, + checked: function(){ + return this.get("value") === this.get("selection"); + }.property('selection') + }), + + isCollapsed: true, + doExpandCollapse: function(){ + var that = this; + this.$('#collapseQueueMappingsBtn').on('click', function(e){ + Ember.run.next(that, function(){ + this.toggleProperty('isCollapsed'); + }); + }); + }.on('didInsertElement'), + + destroyEventListeners: function() { + this.$('#collapseQueueMappingsBtn').off('click'); + }.on('willDestroyElement') + }); http://git-wip-us.apache.org/repos/asf/ambari/blob/79489a8f/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueSummary.js ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueSummary.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueSummary.js new file mode 100644 index 0000000..3d4f7be --- /dev/null +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueSummary.js @@ -0,0 +1,52 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + var App = require('app'); + + var _runState = 'RUNNING'; + var _stopState = 'STOPPED'; + + App.QueueSummaryComponent = Ember.Component.extend({ + layoutName: 'components/queueSummary', + queue: null, + allQueues: null, + + isRunningState: function() { + return this.get('queue.state') === _runState || this.get('queue.state') === null; + }.property('queue.state'), + + queueState: function() { + if (this.get('isRunningState')) { + return _runState; + } else { + return _stopState; + } + }.property('queue.state'), + + effectiveCapacity: function() { + var currentQ = this.get('queue'), + allQueues = this.get('allQueues'), + effectiveCapacityRatio = 1; + while (currentQ !== null) { + effectiveCapacityRatio *= (currentQ.get('capacity') / 100); + currentQ = allQueues.findBy('id', currentQ.get('parentPath').toLowerCase()) || null; + } + var effectiveCapacityPercent = Math.round(effectiveCapacityRatio * 100); + return effectiveCapacityPercent; + }.property('queue.capacity', '[email protected]', 'allQueues.length') + }); http://git-wip-us.apache.org/repos/asf/ambari/blob/79489a8f/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers.js ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers.js index 16b7d0a..5cd31ed 100644 --- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers.js +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers.js @@ -20,3 +20,8 @@ require('controllers/queue'); require('controllers/queues'); require('controllers/trace'); require('controllers/configs'); +require('controllers/capsched'); +require('controllers/advanced'); +require('controllers/scheduler'); +require('controllers/queuesconf'); +require('controllers/editqueue'); http://git-wip-us.apache.org/repos/asf/ambari/blob/79489a8f/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/advanced.js ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/advanced.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/advanced.js new file mode 100644 index 0000000..7af45bb --- /dev/null +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/advanced.js @@ -0,0 +1,55 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var App = require('app'); + +var cmp = Ember.computed; + +App.CapschedAdvancedController = Ember.Controller.extend({ + needs: ['capsched'], + + actions: { + rollbackQueueMappingProps: function() { + var sched = this.get('scheduler'), + attributes = sched.changedAttributes(), + props = this.queueMappingProps; + props.forEach(function(prop) { + if (attributes.hasOwnProperty(prop)) { + sched.set(prop, attributes[prop][0]); + } + }); + } + }, + + isOperator: cmp.alias('controllers.capsched.isOperator'), + scheduler: cmp.alias('controllers.capsched.content'), + queues: cmp.alias('controllers.capsched.queues'), + isQueueMappingsDirty: false, + queueMappingProps: ['queue_mappings', 'queue_mappings_override_enable'], + + queueMappingsDidChange: function() { + var sched = this.get('scheduler'), + attributes = sched.changedAttributes(), + props = this.queueMappingProps; + var isDirty = props.any(function(prop){ + return attributes.hasOwnProperty(prop); + }); + this.set('isQueueMappingsDirty', isDirty); + }.observes('scheduler.queue_mappings', 'scheduler.queue_mappings_override_enable') + +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/79489a8f/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/capsched.js ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/capsched.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/capsched.js new file mode 100644 index 0000000..1ee7a6b --- /dev/null +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/capsched.js @@ -0,0 +1,39 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var App = require('app'); + +var cmp = Ember.computed; + +App.CapschedController = Ember.Controller.extend({ + actions: { + + }, + + /** + * User admin status. + * @type {Boolean} + */ + isOperator: false, + + /** + * Inverted isOperator value. + * @type {Boolean} + */ + isNotOperator: cmp.not('isOperator') +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/79489a8f/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/editqueue.js ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/editqueue.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/editqueue.js new file mode 100644 index 0000000..69be18c --- /dev/null +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/editqueue.js @@ -0,0 +1,333 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var App = require('app'); + +var _runState = 'RUNNING'; +var _stopState = 'STOPPED'; + +App.CapschedQueuesconfEditqueueController = Ember.Controller.extend({ + needs: ['capsched'], + isOperator: Ember.computed.alias('controllers.capsched.isOperator'), + isNotOperator: Ember.computed.not('isOperator'), + scheduler: Ember.computed.alias('controllers.capsched.content'), + allQueues: Ember.computed.alias('controllers.capsched.queues'), + + isRangerEnabledForYarn: function() { + var isRanger = this.get('controllers.capsched.isRangerEnabledForYarn'); + if (isRanger == null || typeof isRanger == 'undefined') { + return false; + } + isRanger = isRanger.toLowerCase(); + if (isRanger == 'yes' || isRanger == 'true') { + return true; + } + return false; + }.property('controllers.capsched.isRangerEnabledForYarn'), + + actions: { + toggleProperty: function (property, target) { + target = target || this; + target.toggleProperty(property); + }, + mouseUp: function(){ + return false; + } + }, + + /** + * Collection of modified fields in queue. + * @type {Object} - { [fileldName] : {Boolean} } + */ + queueDirtyFields: {}, + + isQueueDirty: Ember.computed.bool('content.isDirty'), + + /** + * Possible values for ordering policy + * @type {Array} + */ + orderingPolicyValues: [ + {label: 'FIFO', value: 'fifo'}, + {label: 'Fair', value: 'fair'} + ], + + /** + * Returns true if queue is root. + * @type {Boolean} + */ + isRoot: Ember.computed.match('content.id', /^(root)$/), + + /** + * Returns true if queue is default. + * @type {Boolean} + */ + isDefaultQ: Ember.computed.match('content.id', /^(root.default)$/), + + /** + * Represents queue run state. Returns true if state is null. + * @return {Boolean} + */ + isRunning: function() { + return this.get('content.state') == _runState || this.get('content.state') == null; + }.property('content.state'), + + /** + * 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') || 'fifo'; + }.property('content.ordering_policy'), + + /** + * Does ordering policy is equal to 'fair' + * @type {Boolean} + */ + isFairOP: Ember.computed.equal('content.ordering_policy', 'fair'), + + /** + * Returns maximum applications for a queue if defined, + * else the inherited value (for all queues) + */ + maximumApplications: function(key, val) { + if (arguments.length > 1) { + if (val !== this.get('scheduler.maximum_applications')) { + this.set('content.maximum_applications', val); + } else { + this.set('content.maximum_applications', null); + } + } + return this.get('content.maximum_applications') || this.get('scheduler.maximum_applications'); + }.property('content.maximum_applications', 'scheduler.maximum_applications'), + + /** + * Returns maximum AM resource percent for a queue if defined, + * else the inherited value (for all queues) + */ + maximumAMResourcePercent: function(key, val) { + if (arguments.length > 1) { + if (val !== this.get('scheduler.maximum_am_resource_percent')) { + this.set('content.maximum_am_resource_percent', val); + } else { + this.set('content.maximum_am_resource_percent', null); + } + } + return this.get('content.maximum_am_resource_percent') || this.get('scheduler.maximum_am_resource_percent'); + }.property('content.maximum_am_resource_percent', 'scheduler.maximum_am_resource_percent'), + + /** + * Sets ACL value to '*' or ' ' and returns '*' and 'custom' respectively. + * @param {String} key - ACL attribute + * @param {String} value - ACL value + * @return {String} + */ + handleAcl: function (key, value) { + if (value) { + this.set(key, (value === '*')? '*' : ' '); + } + return (this.get(key) === '*' || this.get(key) == null) ? '*' : 'custom'; + }, + + /** + * Queue's acl_administer_queue property can be set to '*' (everyone) or ' ' (nobody) thru this property. + * + * @param {String} key + * @param {String} value + * @return {String} - '*' if equal to '*' or 'custom' in other case. + */ + acl_administer_queue: function (key, value) { + return this.handleAcl('content.acl_administer_queue', value); + }.property('content.acl_administer_queue'), + + /** + * Returns true if acl_administer_queue is set to '*' + * @type {Boolean} + */ + aaq_anyone: Ember.computed.equal('acl_administer_queue', '*'), + + /** + * Returns effective permission of the current queue to perform administrative functions on this queue. + */ + aaq_effective_permission: function(key, value){ + return this.getEffectivePermission('acl_administer_queue'); + }.property('content.acl_administer_queue'), + + /** + * Queue's acl_submit_applications property can be set to '*' (everyone) or ' ' (nobody) thru this property. + * + * @param {String} key + * @param {String} value + * @return {String} - '*' if equal to '*' or 'custom' in other case. + */ + acl_submit_applications: function (key, value) { + return this.handleAcl('content.acl_submit_applications', value); + }.property('content.acl_submit_applications'), + + /** + * Returns true if acl_submit_applications is set to '*' + * @type {Boolean} + */ + asa_anyone:Ember.computed.equal('acl_submit_applications', '*'), + + /** + * Returns effective permission of the current queue to submit application. + */ + asa_effective_permission: function(key, value){ + return this.getEffectivePermission('acl_submit_applications'); + }.property('content.acl_submit_applications'), + + /** + * Returns effective permission of the current queue. + */ + getEffectivePermission: function(permissionType){ + var effectivePermission, + users = [], + groups = [], + currentPermissions = this.getPermissions(permissionType); + for(var i = 0; i < currentPermissions.length; i++){ + var permission = currentPermissions[i]; + if (permission === '*') { + return '*'; + } else if (permission.trim() === '') { + effectivePermission = ''; + } else { + var usersAndGroups = permission.split(' '); + this.fillUsersAndGroups(users, usersAndGroups[0]); + if (usersAndGroups.length === 2) { + this.fillUsersAndGroups(groups, usersAndGroups[1]); + } + } + } + if(users.length > 0 || groups.length > 0){ + effectivePermission = users.join(',') + ' ' + groups.join(','); + } + return effectivePermission; + }, + + /** + * Removes duplicate users or groups. + */ + fillUsersAndGroups: function(usersOrGroups, list){ + var splitted = list.split(','); + splitted.forEach(function(item){ + if(usersOrGroups.indexOf(item) === -1){ + usersOrGroups.push(item); + } + }); + }, + + /** + * Returns array of permissions from root to leaf. + */ + getPermissions: function(permissionType){ + var currentQ = this.get('content'), + permissions = []; + while (currentQ !== null) { + if (currentQ.get(permissionType) !== null) { + permissions.push(currentQ.get(permissionType)); + } else { + permissions.push('*'); + } + currentQ = this.store.getById('queue', currentQ.get('parentPath').toLowerCase()); + } + permissions.reverse();//root permission at the 0th position. + return permissions; + }, + + /** + * Array of leaf queues. + * @return {Array} + */ + childrenQueues: function () { + return this.get('allQueues') + .filterBy('depth', this.get('content.depth') + 1) + .filterBy('parentPath', this.get('content.path')); + }.property('allQueues.length', 'content.path', 'content.parentPath'), + + /** + * Parent of current queue. + * @return {App.Queue} + */ + parentQueue: function () { + return this.store.getById('queue', this.get('content.parentPath').toLowerCase()); + }.property('content.parentPath'), + + /* + * Returns true if the current queue is a leaf queue + */ + isLeafQ: function() { + return this.get('content.queues') == null; + }.property('allQueues.length', 'content.queues'), + + childrenQueuesTotalCapacity: function() { + var childrenQs = this.get('childrenQueues'), + totalCapacity = 0; + childrenQs.forEach(function(currentQ){ + totalCapacity += currentQ.get('capacity'); + }); + return totalCapacity; + }.property('childrenQueues.length', '[email protected]'), + + pattern: 'width: %@%', + + warnInvalidCapacity: function() { + var totalCap = this.get('childrenQueuesTotalCapacity'); + if (totalCap > 100 || totalCap < 100) { + return true; + } + return false; + }.property('childrenQueuesTotalCapacity'), + + totalCapacityBarWidth: function() { + var totalCap = this.get('childrenQueuesTotalCapacity'); + if (totalCap > 100) { + totalCap = 100; + } + return this.get('pattern').fmt(totalCap); + }.property('childrenQueuesTotalCapacity'), + + /** + * Adds observers for each queue attribute. + * @method dirtyObserver + */ + dirtyObserver: function () { + this.get('content.constructor.transformedAttributes.keys.list').forEach(function(item) { + this.addObserver('content.' + item, this, 'propertyBecomeDirty'); + }.bind(this)); + }.observes('content'), + + /** + * Adds modified queue fileds to q queueDirtyFields collection. + * @param {String} controller + * @param {String} property + * @method propertyBecomeDirty + */ + propertyBecomeDirty: function (controller, property) { + var queueProp = property.split('.').objectAt(1); + this.set('queueDirtyFields.' + queueProp, this.get('content').changedAttributes().hasOwnProperty(queueProp)); + } +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/79489a8f/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/queuesconf.js ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/queuesconf.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/queuesconf.js new file mode 100644 index 0000000..0f4d038 --- /dev/null +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/queuesconf.js @@ -0,0 +1,141 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var App = require('app'); +var _runState = 'RUNNING'; +var _stopState = 'STOPPED'; + +App.CapschedQueuesconfController = Ember.Controller.extend({ + needs: ['capsched', 'loading'], + queues: Ember.computed.alias('controllers.capsched.queues'), + isOperator: Ember.computed.alias('controllers.capsched.isOperator'), + + actions: { + addNewQueue: function() { + this.set('newQueueName', ''); + this.set('showQueueNameInput', true); + }, + createNewQueue: function() { + var store = this.get('store'), + queueName = this.get('newQueueName'), + parentPath = this.get('selectedQueue.path'), + queuePath = parentPath + "." + queueName, + depth = parentPath.split('.').length, + leafQueueNames = store.getById('queue', parentPath.toLowerCase()).get('queuesArray'), + newInLeaf = Em.isEmpty(leafQueueNames), + existed = store.get('deletedQueues').findBy('path', [parentPath, queueName].join('.')), + totalLeafCapacity, + freeLeafCapacity, + newQueue; + + this.send('cancelCreateQueue'); + + if (existed) { + newQueue = store.createFromDeleted(existed); + } else { + if (!newInLeaf) { + totalLeafCapacity = leafQueueNames.reduce(function (capacity, qName) { + return store.getById('queue', [parentPath, qName].join('.').toLowerCase()).get('capacity') + capacity; + }, 0); + + freeLeafCapacity = (totalLeafCapacity < 100) ? 100 - totalLeafCapacity : 0; + } + var qCapacity = (newInLeaf) ? 100 : freeLeafCapacity; + + newQueue = store.createRecord('queue', { + id: queuePath, + name: queueName, + path: queuePath, + parentPath: parentPath, + depth: depth, + isNewQueue: true, + capacity: qCapacity, + maximum_capacity: qCapacity + }); + + this.set('newQueue', newQueue); + } + + store.saveAndUpdateQueue(newQueue).then(function() { + Em.run.bind(this, 'set', 'newQueue', null); + }).catch(Em.run.bind(this, 'saveQueuesConfigError', 'createQueue')); + }, + saveQueuesConfig: function() { + var store = this.get('store'), + opt = 'saveAndRefresh', + saveQs = this.get('queues').save(); + + Ember.RSVP.Promise.all([saveQs]).then( + Em.run.bind(this, 'saveQueuesConfigSuccess'), + Em.run.bind(this, 'saveQueuesConfigError', opt) + ).then(function() { + return store.relaunchCapSched(opt); + }).catch(Em.run.bind(this, 'saveQueuesConfigError', opt)); + + }, + cancelCreateQueue: function() { + this.set('newQueueName', ''); + this.set('showQueueNameInput', false); + }, + clearAlert:function () { + this.set('alertMessage', null); + } + }, + + selectedQueue: null, + newQueue: null, + newQueueName: '', + showQueueNameInput: false, + + /** + * True if newQueue is not empty. + * @type {Boolean} + */ + hasNewQueue: Ember.computed.bool('newQueue'), + + /** + * Represents queue run state. Returns true if state is null. + * @return {Boolean} + */ + isSelectedQRunning: function() { + return this.get('selectedQueue.state') == _runState || this.get('selectedQueue.state') == null; + }.property('selectedQueue.state'), + + /** + * Returns true if queue is root. + * @type {Boolean} + */ + isRootQSelected: Ember.computed.match('selectedQueue.id', /^(root)$/), + + /** + * Property for error message which may appear when saving queue. + * @type {Object} + */ + alertMessage: null, + + configNote: Ember.computed.alias('store.configNote'), + + saveQueuesConfigSuccess: function() { + this.set('store.deletedQueues', []); + }, + saveQueuesConfigError: function(operation, error) { + var response = (error && error.responseJSON)? error.responseJSON : {}; + response.simpleMessage = operation.capitalize() + ' failed!'; + this.set('alertMessage', response); + } +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/79489a8f/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/scheduler.js ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/scheduler.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/scheduler.js new file mode 100644 index 0000000..c0c56b6 --- /dev/null +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/scheduler.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'); + +var cmp = Ember.computed; + +App.CapschedSchedulerController = Ember.Controller.extend({ + needs: ['capsched'], + schedulerProps: ['maximum_am_resource_percent', 'maximum_applications', 'node_locality_delay', 'resource_calculator'], + + actions: { + rollbackSchedulerProps: function() { + var sched = this.get('scheduler'), + attributes = sched.changedAttributes(), + props = this.schedulerProps; + props.forEach(function(prop) { + if (attributes.hasOwnProperty(prop)) { + sched.set(prop, attributes[prop][0]); + } + }); + } + }, + + isOperator: cmp.alias('controllers.capsched.isOperator'), + + /** + * Scheduler record + * @type {App.Scheduler} + */ + scheduler: cmp.alias('controllers.capsched.content'), + isSchedulerDirty: false, + + schedulerBecomeDirty: function() { + var sched = this.get('scheduler'), + attributes = sched.changedAttributes(), + props = this.schedulerProps; + var isDirty = props.any(function(prop){ + return attributes.hasOwnProperty(prop); + }); + this.set('isSchedulerDirty', isDirty); + }.observes('scheduler.maximum_am_resource_percent', 'scheduler.maximum_applications', 'scheduler.node_locality_delay', 'scheduler.resource_calculator'), + + /** + * Collection of modified fields in Scheduler. + * @type {Object} - { [fileldName] : {Boolean} } + */ + schedulerDirtyFilelds: {}, + + dirtyObserver:function () { + this.get('scheduler.constructor.transformedAttributes.keys.list').forEach(function(item) { + this.addObserver('scheduler.' + item, this, 'propertyBecomeDirty'); + }.bind(this)); + }.observes('scheduler').on('init'), + + propertyBecomeDirty:function (controller, property) { + var schedProp = property.split('.').objectAt(1); + this.set('schedulerDirtyFilelds.' + schedProp, this.get('scheduler').changedAttributes().hasOwnProperty(schedProp)); + }, + + resourceCalculatorValues: [{ + label: 'Default Resource Calculator', + value: 'org.apache.hadoop.yarn.util.resource.DefaultResourceCalculator' + }, { + label: 'Dominant Resource Calculator', + value: 'org.apache.hadoop.yarn.util.resource.DominantResourceCalculator' + }] +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/79489a8f/contrib/views/capacity-scheduler/src/main/resources/ui/app/helpers.js ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/helpers.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/helpers.js new file mode 100644 index 0000000..95f301d --- /dev/null +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/helpers.js @@ -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. + */ + +require('helpers/timeAgo'); +require('helpers/escapeAcl'); +require('helpers/queueMappingParser'); +require('helpers/uppercase'); http://git-wip-us.apache.org/repos/asf/ambari/blob/79489a8f/contrib/views/capacity-scheduler/src/main/resources/ui/app/helpers/queueMappingParser.js ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/helpers/queueMappingParser.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/helpers/queueMappingParser.js new file mode 100644 index 0000000..319b3bc --- /dev/null +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/helpers/queueMappingParser.js @@ -0,0 +1,34 @@ +/** + * 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('queueMappingParser', function(mapping){ + var output = ''; + var parts = mapping.split(':'); + if(parts[0] === 'u'){ + if(parts[1] === '%user' && parts[2] === '%user'){ + output = 'User %user -> queue %user'; + }else if(parts[1] === '%user' && parts[2] === '%primary_group'){ + output = 'User %user -> queue %primary_group'; + }else{ + output = 'User ' + parts[1] + ' -> queue ' + parts[2]; + } + }else if(parts[0] === 'g'){ + output = 'Group ' + parts[1] + ' -> queue ' + parts[2]; + } + return output; + }); http://git-wip-us.apache.org/repos/asf/ambari/blob/79489a8f/contrib/views/capacity-scheduler/src/main/resources/ui/app/helpers/uppercase.js ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/helpers/uppercase.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/helpers/uppercase.js new file mode 100644 index 0000000..222af54 --- /dev/null +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/helpers/uppercase.js @@ -0,0 +1,21 @@ +/** + * 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('uppercase', function(value) { + return value.toUpperCase(); + }); http://git-wip-us.apache.org/repos/asf/ambari/blob/79489a8f/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 05bb64c..3dfe363 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 @@ -31,8 +31,7 @@ require('serializers'); require('store'); //helpers -require('helpers/timeAgo'); -require('helpers/escapeAcl'); +require('helpers'); //components require('components'); @@ -48,6 +47,9 @@ require('models'); //views require('views/queues'); +require('views/editqueue'); +require('views/editQueueCapacity'); +require('views/queuesconf'); // routes require('router'); http://git-wip-us.apache.org/repos/asf/ambari/blob/79489a8f/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 a2d50b9..a32e02c 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,6 +23,14 @@ App.Router.map(function() { this.resource('queue', { path: '/:queue_id' }); this.resource('trace', { path: '/log' }); }); + this.resource('capsched', {path: '/capacity-scheduler'}, function() { + this.route('scheduler', {path: '/scheduler'}); + this.route('advanced', {path: '/advanced'}); + this.route('trace', {path: '/log'}); + this.route('queuesconf', {path: '/queues'}, function() { + this.route('editqueue', {path: '/:queue_id'}); + }); + }); this.route('refuse'); }); @@ -63,7 +71,6 @@ App.QueuesRoute = Ember.Route.extend({ return store.findQuery( 'config', {siteName : RANGER_SITE, configName : RANGER_YARN_ENABLED}).then(function(){ return store.find( 'config', "siteName_" + RANGER_SITE + "_configName_" + RANGER_YARN_ENABLED) .then(function(data){ - console.log("router.queuesRoute : data.configValue isRangerEnabled : " + data.get('configValue')); _this.controllerFor('configs').set('isRangerEnabledForYarn', data.get('configValue')); }); }) @@ -189,3 +196,81 @@ App.ErrorRoute = Ember.Route.extend({ } }); +App.CapschedRoute = Ember.Route.extend({ + actions: { + rollbackProp: function(prop, item) { + var attributes = item.changedAttributes(); + if (attributes.hasOwnProperty(prop)) { + item.set(prop, attributes[prop][0]); + } + } + }, + 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'), + _this = this, + controller = this.controllerFor("capsched"), + loadingController = this.container.lookup('controller:loading'); + + return new Ember.RSVP.Promise(function(resolve, reject) { + 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() { + return store.findQuery('config', { + siteName: RANGER_SITE, + configName: RANGER_YARN_ENABLED + }).then(function() { + return store.find('config', "siteName_" + RANGER_SITE + "_configName_" + RANGER_YARN_ENABLED) + .then(function(data) { + controller.set('isRangerEnabledForYarn', data.get('configValue')); + }); + }); + }).then(function() { + loadingController.set('model', { + message: 'loading queues' + }); + return store.find('queue'); + }).then(function(queues) { + controller.set('queues', queues); + return store.find('scheduler', 'scheduler'); + }).then(function(scheduler){ + resolve(scheduler); + }).catch(function(e) { + reject(e); + }); + }, 'App: CapschedRoute#model'); + } +}); + +App.CapschedIndexRoute = Ember.Route.extend({ + redirect: function() { + this.transitionTo('capsched.scheduler'); + } +}); + +App.CapschedQueuesconfIndexRoute = Ember.Route.extend({ + beforeModel: function(transition) { + var rootQ = this.store.getById('queue', 'root'); + this.transitionTo('capsched.queuesconf.editqueue', rootQ); + } +}); + +App.CapschedQueuesconfEditqueueRoute = Ember.Route.extend({ + setupController: function(controller, model) { + controller.set('model', model); + this.controllerFor('capsched.queuesconf').set('selectedQueue', model); + } +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/79489a8f/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 be79229..a60d39d 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 @@ -880,3 +880,288 @@ .tooltip-label span:first-child { cursor: help; } + +.panel-queue-mapping { + .queue-mapping-options { + padding: 10px; + border: 1px solid #ddd; + border-radius: 4px; + margin-left: -15px; + margin-right: -15px; + .row { + margin: 0px; + } + } + .list-group { + .list-group-item { + margin-bottom: 3px; + background-color: #f5f5f5; + border-radius: 4px; + padding: 5px 15px; + } + } + .remove-mapping-icon { + cursor: pointer; + &:hover { + color: #d9534f; + } + } +} + +.scheduler-panel { + .input-control { + width: 75%; + display: inline-block; + } + .input-resource-percent { + width: 75%; + float: left; + } + .rollback-resource-percent { + margin: 4px; + } + .resource-calc-control { + width: 88%; + display: inline-block; + } +} + +.queue-config-container { + .queue-actions-wrapper { + .btn { + padding: 1px 5px; + } + } + .add-newqueuename-wrapper { + padding-top: 30px; + .new-queue-name { + width: 100%; + display: inline-block; + } + } + hr { + margin-top: 5px; + margin-bottom: 10px; + } + .queue-hierarchy.list-group { + margin-bottom: 5px; + .list-group-item { + border: 1px solid #efefef; + padding: 5px 10px; + &.first { + border-top-left-radius: 4px; + border-top-right-radius: 4px; + } + &.last { + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + } + .badge { + background-color: #fff; + padding: 2px 3px; + } + } + } +} + +.queue-resources-container { + .input-width-control { + width: 75%; + display: inline-block; + } + .input-percent-wrap { + .input-percent { + width: 75%; + float: left; + } + .btn-group.btn-group-xs { + margin: 4px; + } + } + .input-toggle-wrap { + .btn-group.btn-group-sm { + width: 20px; + .btn.btn-default { + width: 20px; + padding: 0; + border: none; + margin: 0; + &:hover, &:active, &:focus { + background-color: #fff; + } + .fa { + font-size: 18px + } + .fa-square-o { + margin-left: -6px; + } + .fa-check-square-o { + margin-left: -4px; + } + } + } + } + .inherited-value { + margin: 4px 0px 4px -40px; + } +} + +.queue-acl-container { + .user-group-input { + .col-sm-4 { + padding: 15px; + } + .col-sm-8 { + width: 35%; + padding: 10px; + } + .fa-user,.fa-users { + top: 8px; + right: 7px; + opacity: 0.4; + } + .help-block { + float: left !important; + } + } +} + +.capsched-container { + .nav-tabs { + border-bottom: none; + } + .panel-heading { + padding: 4px 14px; + .panel-title { + font-size: 14px; + } + } + .input-group-addon { + padding: 7px 6px; + width: 26px; + } +} + +.queue-summary { + .panel-default { + border-color: #f5f5f5; + } +} + +.queue-capacity-container { + .form-inline { + margin-bottom: 20px; + .queue-name { + margin-top: 5px; + } + .capacity-input-percent { + width: 30%; + float: left; + display: inline-block; + input { + width: 60%; + } + } + .capacity-input-slider { + width: 60%; + display: inline-block; + } + } + .progress { + display: inline-block; + vertical-align: middle; + margin-bottom: 0; + .progress-bar { + color: #000; + } + } + .total-capacity-progress { + width: 100%; + } + input[type=range] { + -webkit-appearance: none; + width: 100%; + background-color: #fefefe; + &:focus { + outline: none; + } + &::-webkit-slider-runnable-track { + width: 100%; + height: 2px; + cursor: pointer; + background: #555; + border-radius: 1.3px; + border: 0.2px solid #010101; + &:focus { + background: #555; + } + } + &::-webkit-slider-thumb { + border: 1.8px solid #00001e; + height: 15px; + width: 20px; + border-radius: 15px; + background: #ffffff; + cursor: pointer; + -webkit-appearance: none; + margin-top: -7px; + } + &::-moz-range-track { + width: 100%; + height: 2px; + cursor: pointer; + background: #555; + border-radius: 1.3px; + border: 0.2px solid #010101; + } + &::-moz-range-thumb { + border: 1.8px solid #00001e; + height: 15px; + width: 20px; + border-radius: 15px; + background: #ffffff; + cursor: pointer; + } + &::-ms-track { + width: 100%; + height: 2px; + cursor: pointer; + background: transparent; + border-color: transparent; + color: transparent; + } + &::-ms-thumb { + border: 1.8px solid #00001e; + width: 20px; + border-radius: 15px; + background: #ffffff; + cursor: pointer; + height: 11.4px; + } + &::-ms-fill-lower { + background: rgba(42, 100, 149, 0.78); + border: 0.2px solid #010101; + border-radius: 2.6px; + &:focus { + background: #555; + } + } + &::-ms-fill-upper { + background: #555; + border: 0.2px solid #010101; + border-radius: 2.6px; + &:focus { + background: #555; + } + } + } +} + +.edit-queuename-wrapper { + margin-top: 10px; + margin-bottom: 20px; + .input-queue-name { + width: 69%; + display: inline-block; + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/79489a8f/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 25e2aa9..17b279a 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 @@ -39,3 +39,16 @@ require('templates/components/queueContainer'); require('templates/components/dropdownConfirmation'); require('templates/components/dropdownDownload'); require('templates/components/queueBadge'); +require('templates/components/queueMapping'); +require('templates/capsched'); +require('templates/capsched/scheduler'); +require('templates/capsched/advanced'); +require('templates/capsched/queuesconf'); +require('templates/versionsPanel'); +require('templates/components/queueHierarchy'); +require('templates/capsched/queuesconf/editqueue'); +require('templates/components/queueSummary'); +require('templates/capsched/partials/queueResources'); +require('templates/capsched/partials/accessControlList'); +require('templates/capsched/partials/queueCapacity'); +require('templates/capsched/partials/editQueueCapacity'); http://git-wip-us.apache.org/repos/asf/ambari/blob/79489a8f/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched.hbs b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched.hbs new file mode 100644 index 0000000..1eead4b --- /dev/null +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched.hbs @@ -0,0 +1,44 @@ +{{! +* 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. +}} + +<nav class="navbar navbar-default navbar-static-top"> + <div class="navbar-header"> + <span class="navbar-brand">Capacity Scheduler</span> + </div> +</nav> +<div class="col-lg-12 capsched-container"> + <div class="col-lg-8"> + <ul class="nav nav-tabs"> + {{#link-to "capsched.scheduler" tagName="li"}} + <a href="#">Scheduler</a> + {{/link-to}} + {{#link-to "capsched.queuesconf" tagName="li"}} + <a href="#">Queues</a> + {{/link-to}} + {{#link-to "capsched.advanced" tagName="li"}} + <a href="#">Advanced</a> + {{/link-to}} + </ul> + <div class="tab-content"> + {{outlet}} + </div> + </div> + <div class="col-lg-4"> + {{partial "versionsPanel"}} + </div> +</div> http://git-wip-us.apache.org/repos/asf/ambari/blob/79489a8f/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/advanced.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/advanced.hbs b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/advanced.hbs new file mode 100644 index 0000000..e54e14b --- /dev/null +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/advanced.hbs @@ -0,0 +1,32 @@ +{{! +* 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="capshed-advanced-container"> + {{!-- QUEUE MAPPING --}} + {{#if isOperator}} + <div class="hidden-sm hidden-xs"> + {{queue-mapping mappings=scheduler.queue_mappings mappingsOverrideEnable=scheduler.queue_mappings_override_enable queues=queues}} + </div> + {{/if}} +</div> +<div class="row"> + <div class="btn btn-group-md col-md-offset-5"> + <button type="button" {{bind-attr class=":btn :btn-default :btn-success isQueueMappingsDirty::disabled"}} name="saveAdvanced">Save</button> + <button type="button" {{bind-attr class=":btn :btn-default :btn-danger isQueueMappingsDirty::disabled"}} name="cancelAdvanced" {{action "rollbackQueueMappingProps"}}>Cancel</button> + </div> +</div> http://git-wip-us.apache.org/repos/asf/ambari/blob/79489a8f/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/accessControlList.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/accessControlList.hbs b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/accessControlList.hbs new file mode 100644 index 0000000..1f6fbda --- /dev/null +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/accessControlList.hbs @@ -0,0 +1,154 @@ +{{! +* 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="queue-acl-container"> + <div class="panel panel-default"> + <div class="panel-heading"> + <div class="panel-title"> + Access Control List <a id="collapseQueueAclPanelBtn" href="#collapsibleQueueAclPanel" data-toggle="collapse" {{bind-attr class=":pull-right view.isAclPanelCollapsed:collapsed"}}><i {{bind-attr class=":fa view.isAclPanelCollapsed:fa-plus:fa-minus"}}></i></a> + </div> + </div> + <div id="collapsibleQueueAclPanel" {{bind-attr class=":panel-collapse :collapse view.isAclPanelCollapsed::in"}}> + <div class="panel-body"> + {{#unless isRangerEnabledForYarn}} + {{#if isOperator}} + <div class="row form-group"> + {{tooltip-label + class="col-sm-4 col-md-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-md-6 col-sm-6 control-value"> + <div class="btn-group btn-group-xs" data-toggle="buttons"> + {{radio-button label="Anyone" selectionBinding="acl_administer_queue" value="*"}} + {{radio-button label="Custom" selectionBinding="acl_administer_queue" value="custom"}} + </div> + {{#if queueDirtyFields.acl_administer_queue}} + <div class="btn-group btn-group-xs"> + <a {{action 'rollbackProp' 'acl_administer_queue' content}} href="#" class="btn btn-default btn-warning"><i class="fa fa-undo"></i></a> + </div> + {{/if}} + </div> + </div> + {{#unless aaq_anyone}} + {{user-group-input ug=content.acl_administer_queue disabled=aaq_anyone}} + {{/unless}} + <div class="form-group row"> + {{tooltip-label + tagName='small' + class="col-sm-4 col-md-4 control-label" + label='Effective Administraters' + message='The effective permissions to perform administrative functions on this queue.' + }} + <div class="col-md-6 col-sm-6 control-value"> + <label>{{escapeACL aaq_effective_permission}}</label> + </div> + </div> + <hr/> + <div class="row form-group"> + {{tooltip-label + class="col-sm-4 col-md-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-sm-6 col-md-6 control-value"> + <div class="btn-group btn-group-xs" data-toggle="buttons"> + {{radio-button label="Anyone" selectionBinding="acl_submit_applications" value="*"}} + {{radio-button label="Custom" selectionBinding="acl_submit_applications" value="custom"}} + </div> + {{#if queueDirtyFields.acl_submit_applications}} + <div class="btn-group btn-group-xs" > + <a {{action 'rollbackProp' 'acl_submit_applications' content}} href="#" class="btn btn-default btn-warning"><i class="fa fa-undo"></i></a> + </div> + {{/if}} + </div> + </div> + {{#unless asa_anyone}} + {{user-group-input ug=content.acl_submit_applications disabled=asa_anyone}} + {{/unless}} + <div class="form-group row"> + {{tooltip-label + tagName='small' + class="col-md-4 col-sm-4 control-label" + label='Effective Users' + message='The effective permissions to submit applications to this queue.' + }} + <div class="col-sm-6 col-md-6 control-value"> + <label>{{escapeACL asa_effective_permission}}</label> + </div> + </div> + {{else}} + <div class="form-group row"> + {{tooltip-label + class="col-sm-4 col-md-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-sm-6 col-md-6 control-value"> + <p class="form-control-static"> + {{escapeACL content.acl_administer_queue}} + </p> + </div> + </div> + <div class="form-group row"> + {{tooltip-label + tagName='small' + class="col-sm-4 col-md-4 control-label" + label='Effective Administraters' + message='The effective permissions to perform administrative functions on this queue.' + }} + <div class="col-sm-6 col-md-6 control-value"> + <p class="form-control-static"> + {{escapeACL aaq_effective_permission}} + </p> + </div> + </div> + <hr/> + <div class="form-group row"> + {{tooltip-label + class="col-sm-4 col-md-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-sm-6 col-md-6 control-value"> + <p class="form-control-static"> + {{escapeACL content.acl_submit_applications}} + </p> + </div> + </div> + <div class="form-group row"> + {{tooltip-label + tagName='small' + class="col-sm-4 col-md-4 control-label" + label='Effective Users' + message='The effective permissions to submit applications to this queue.' + }} + <div class="col-sm-6 col-md-6 control-valu"> + <p class="form-control-static"> + {{escapeACL asa_effective_permission}} + </p> + </div> + </div> + {{/if}} + {{else}} + <div>Permissions are managed by Ranger</div> + {{/unless}} + </div> + </div> + </div> +</div> http://git-wip-us.apache.org/repos/asf/ambari/blob/79489a8f/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/editQueueCapacity.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/editQueueCapacity.hbs b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/editQueueCapacity.hbs new file mode 100644 index 0000000..753fa9b --- /dev/null +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/editQueueCapacity.hbs @@ -0,0 +1,51 @@ +{{! +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +}} + +<div class="row form-inline"> + <div class="col-md-2 col-sm-2"> + <span class="queue-name">{{this.name}}</span> + </div> + <div class="col-md-5 col-md-5"> + <div class="form-group input-group capacity-input-percent"> + {{capacity-input class='input-sm' value=this.capacity queue=this maxVal=100}} + <span class="input-group-addon">%</span> + </div> + <div class="form-group capacity-input-slider"> + {{input-range min="0" max="100" step="1" value=this.capacity class="input-sm"}} + </div> + {{#if view.isQueueCapacityDirty}} + <div class="btn-group btn-group-xs"> + <a {{action 'rollbackProp' 'capacity' this}} href="#" class="btn btn-default btn-warning"><i class="fa fa-undo"></i></a> + </div> + {{/if}} + </div> + <div class="col-md-5 col-sm-5"> + <div class="form-group input-group capacity-input-percent"> + {{max-capacity-input class='input-sm' value=this.maximum_capacity queue=this maxVal=100}} + <span class="input-group-addon">%</span> + </div> + <div class="form-group capacity-input-slider"> + {{input-range min="0" max="100" step="1" value=this.maximum_capacity class="input-sm"}} + </div> + {{#if view.isQueueMaximumCapacityDirty}} + <div class="btn-group btn-group-xs"> + <a {{action 'rollbackProp' 'maximum_capacity' this}} href="#" class="btn btn-default btn-warning"><i class="fa fa-undo"></i></a> + </div> + {{/if}} + </div> +</div> http://git-wip-us.apache.org/repos/asf/ambari/blob/79489a8f/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/queueCapacity.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/queueCapacity.hbs b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/queueCapacity.hbs new file mode 100644 index 0000000..8e278f0 --- /dev/null +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/queueCapacity.hbs @@ -0,0 +1,95 @@ +{{! +* 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="queue-capacity-container"> + <div class="panel panel-default"> + <div class="panel-heading"> + <div class="panel-title"> + Capacity <a id="collapseQueueCapacityPanelBtn" href="#collapsibleQueueCapacityPanel" data-toggle="collapse" {{bind-attr class=":pull-right view.isCapacityPanelCollapsed:collapsed"}}><i {{bind-attr class=":fa view.isCapacityPanelCollapsed:fa-plus:fa-minus"}}></i></a> + </div> + </div> + <div id="collapsibleQueueCapacityPanel" {{bind-attr class=":panel-collapse :collapse view.isCapacityPanelCollapsed::in"}}> + <div class="panel-body"> + {{#if isOperator}} + {{#unless isLeafQ}} + <div class="row"> + <div class="col-md-2 col-sm-2"> + <label>Children</label> + </div> + <div class="col-md-5 col-sm-5"> + {{tooltip-label + label='Capacity' + message= 'The minimum guaranteed capacity as a percentage of total capacity that is allocated to the queue.' + }} + </div> + <div class="col-md-5 col-sm-5"> + {{tooltip-label + label='Max Capacity' + message= 'The cap (maximum capacity) as a percentage of total capacity that this queue can utilize.' + }} + </div> + </div> + {{#each childQ in childrenQueues}} + {{render "capsched/partials/editQueueCapacity" childQ}} + {{/each}} + <div class="row"> + <div class="col-md-2 col-sm-2"> + <label>Total Capacity</label> + </div> + <div class="col-sm-5 col-md-5"> + <div class="progress total-capacity-progress"> + <div role="progressbar" {{bind-attr class=":progress-bar warnInvalidCapacity:progress-bar-danger:progress-bar-success" style="totalCapacityBarWidth"}}> + Used:{{childrenQueuesTotalCapacity}}% + </div> + </div> + </div> + </div> + {{else}} + <div class="row"> + <div class="col-md-6 col-md-6"> + <label>Capacity: </label> + <span>{{content.capacity}}%</span> + </div> + <div class="col-md-5 col-md-5"> + <label>Maximum Capacity: </label> + <span>{{content.maximum_capacity}}%</span> + </div> + </div> + <div class="row"> + <div class="col-md-10 col-sm-10"> + <span>To edit capacity and maximum capacity at parent queue level</span> + <span>{{#link-to 'capsched.queuesconf.editqueue' parentQueue}}Click Here{{/link-to}}</span> + </div> + </div> + {{/unless}} + {{else}} + <div class="row"> + <div class="col-md-6 col-md-6"> + <label>Capacity: </label> + <span>{{content.capacity}}%</span> + </div> + <div class="col-md-5 col-sm-5"> + <label>Maximum Capacity: </label> + <span>{{content.maximum_capacity}}%</span> + </div> + </div> + {{/if}} + </div> + </div> + </div> +</div> http://git-wip-us.apache.org/repos/asf/ambari/blob/79489a8f/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/queueResources.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/queueResources.hbs b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/queueResources.hbs new file mode 100644 index 0000000..f25180f --- /dev/null +++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capsched/partials/queueResources.hbs @@ -0,0 +1,216 @@ +{{! +* 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="queue-resources-container"> + <div class="panel panel-default"> + <div class="panel-heading"> + <div class="panel-title"> + Resources <a id="collapseResourcesPanelBtn" href="#collapsibleQueueResourcesPanel" data-toggle="collapse" {{bind-attr class=":pull-right view.isResourcesPanelCollapsed:collapsed"}}><i {{bind-attr class=":fa view.isResourcesPanelCollapsed:fa-plus:fa-minus"}}></i></a> + </div> + </div> + <div id="collapsibleQueueResourcesPanel" {{bind-attr class=":panel-collapse :collapse view.isResourcesPanelCollapsed::in"}}> + <div class="panel-body"> + + <div class="row form-group"> + {{tooltip-label + class="col-sm-4 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-sm-3"> + {{int-input value=content.user_limit_factor maxlength=10 class="input-sm input-int input-width-control" defaultVal=1 placeholder=1}} + {{#if queueDirtyFields.user_limit_factor}} + <div class="btn-group btn-group-xs"> + <a {{action 'rollbackProp' 'user_limit_factor' content}} href="#" class="btn btn-default btn-warning"><i class="fa fa-undo"></i></a> + </div> + {{/if}} + </div> + {{else}} + <div class="col-sm-2"> + {{#if content.user_limit_factor}} + <p class="form-control-static">{{content.user_limit_factor}}</p> + {{else}} + <p class="form-control-static">1</p> + {{/if}} + </div> + {{/if}} + </div> + + <div class="row form-group"> + {{tooltip-label + class="col-sm-4 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-sm-3 control-value input-percent-wrap"> + <div class="input-group input-percent"> + {{int-input value=content.minimum_user_limit_percent class="input-sm" maxVal=100 placeholder=100}} + <span class="input-group-addon">%</span> + </div> + {{#if queueDirtyFields.minimum_user_limit_percent}} + <div class="btn-group btn-group-xs input-percent-rollback"> + <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> + {{else}} + <div class="col-sm-2"> + {{#if content.minimum_user_limit_percent}} + <p class="form-control-static">{{content.minimum_user_limit_percent}}%</p> + {{else}} + <p class="form-control-static">100%</p> + {{/if}} + </div> + {{/if}} + </div> + + <div class="row form-group"> + {{tooltip-label + class="col-sm-4 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-sm-3 control-value"> + {{int-input placeholder="Inherited" maxlength=15 value=maximumApplications class="input-sm input-int input-width-control"}} + {{#if queueDirtyFields.maximum_applications}} + <div class="btn-group btn-group-xs" > + <a {{action 'rollbackProp' 'maximum_applications' content}} href="#" class="btn btn-default btn-warning"><i class="fa fa-undo"></i></a> + </div> + {{/if}} + </div> + <div class="col-sm-2 inherited-value"> + <span>(Inherited)</span> + </div> + {{else}} + <div class="col-sm-3"> + {{#if maximumApplications}} + <p class="form-control-static">{{maximumApplications}} (Inherited)</p> + {{else}} + <p class="form-control-static">Inherited</p> + {{/if}} + </div> + {{/if}} + </div> + + <div class="row form-group"> + {{tooltip-label + class="col-sm-4 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-sm-3 control-value input-percent-wrap"> + <div class="input-group input-percent"> + {{int-input placeholder="Inherited" value=maximumAMResourcePercent class="input-sm" maxVal=100}} + <span class="input-group-addon">%</span> + </div> + {{#if queueDirtyFields.maximum_am_resource_percent}} + <div class="btn-group btn-group-xs"> + <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> + <div class="col-sm-2 inherited-value"> + <span>(Inherited)</span> + </div> + {{else}} + <div class="col-sm-2"> + {{#if maximumAMResourcePercent}} + <p class="form-control-static">{{maximumAMResourcePercent}}% (Inherited)</p> + {{else}} + <p class="form-control-static">Inherited</p> + {{/if}} + </div> + {{/if}} + </div> + + <div class="row form-group"> + {{tooltip-label + class="col-sm-4 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-sm-3 control-value input-percent-wrap"> + <div class="input-percent"> + {{view Ember.Select + class="form-control input-sm" + content=orderingPolicyValues + optionLabelPath="content.label" + optionValuePath="content.value" + value=currentOP}} + </div> + {{#if queueDirtyFields.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-sm-2"> + {{#if content.ordering_policy}} + <p class="form-control-static">{{uppercase content.ordering_policy}}</p> + {{else}} + <p class="form-control-static">FIFO</p> + {{/if}} + </div> + {{/if}} + </div> + + <div class="row"> + {{#if isFairOP}} + <div class="form-group"> + {{tooltip-label + class="col-sm-4 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-sm-3 control-value input-toggle-wrap"> + <div class="btn-group btn-group-sm"> + <a href="#" {{action 'toggleProperty' 'enable_size_based_weight' content}} class="btn btn-default"> + <i {{bind-attr class=":fa content.enable_size_based_weight:fa-check-square-o:fa-square-o"}}></i> + </a> + </div> + {{#if queueDirtyFields.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-sm-2"> + <p class="form-control-static"> + {{#if content.enable_size_based_weight}} + Enabled + {{else}} + Disabled + {{/if}} + </p> + </div> + {{/if}} + </div> + {{/if}} + </div> + + </div> + </div> + </div> +</div>
